Work through TODO fixes #8

Merged
harry merged 8 commits from todo-fixes into main 2026-05-25 13:13:25 +01:00
Owner

Works through the outstanding TODO.md items. Each fix was implemented by a codex sub-agent driven through patterm's own MCP (orchestrator + sub-agent dogfooding) and reviewed before commit. One commit per issue.

Fixed

  • MCP timeout / head-of-line blockinghandleConn dispatched requests serially, so a slow tool (wait_for_pattern, up to 300s) monopolized the single per-agent connection and queued calls (get_process_status) timed out behind it. Requests are now handled per-goroutine with serialized writes and a WaitGroup for graceful teardown. (7b5a226)
  • Agent close needed two pressesagent-close sent a single SIGTERM with no escalation, so an agent that traps SIGTERM (e.g. opencode) stayed in the tab bar until closed again. Added Session.Terminate (SIGTERM → wait → SIGKILL, entry preserved) run from the close handler in a goroutine. (50fd7be)
  • Codex reported idle mid-turn — codex uses osc_title_stability, but draws progress in the pane body, so its stable title made the classifier call it idle while working. Added a (?i)esc to interrupt thinking-promoter to the codex preset so it stays thinking until the turn's footer disappears. (0725375)
  • Injected submit Enter intermittently dropped — the trailing CR was written only 15ms after the body, inside agents' paste-coalescing window, so it was sometimes swallowed as a newline. The final lone Enter now gets a 100ms settle gap (centralized in writeInput via a pure pieceWriteDelay), covering send_input/send_message/timers/spawn-prompt. (178b443)

Added

  • scratchpad_delete MCP tool — mirrors the existing scratchpad tools end-to-end (catalog, dispatch, host method delegating to scratchpad.Store.Delete, sidebar refresh). (96f7c66)

Changed

  • Grid get_process_output whitespace-normalizednormalizeGridText strips trailing per-line whitespace, collapses blank runs, and normalizes line endings, cutting token waste from padded grid rows. Stream/raw output and wait_for_pattern matching untouched. (53f06b6)
  • Per-tab summaries — the tab bar now renders every agent tab's own summary on row 2, not just the focused tab's. (d2342f9)

Verification

  • go build ./..., go vet ./..., all unit tests, and the full harness E2E suite pass.
  • Each fix is unit-tested (concurrent-dispatch no-HOL-blocking test, SIGKILL-escalation regression, classify thinking-override, pieceWriteDelay, normalizeGridText, scratchpad_delete round-trip, summaryTextFor).

Caveat — needs a rebuild to confirm live

Two changes are deterministic in code and unit-tested, but their real-world effect can only be confirmed after rebuilding + restarting patterm (the session they were developed in ran the old binary):

  • the submit-Enter timing fix (178b443) — a timing heuristic against codex's paste detector;
  • the per-tab summary rendering (d2342f9).
Works through the outstanding `TODO.md` items. Each fix was implemented by a codex sub-agent driven through patterm's own MCP (orchestrator + sub-agent dogfooding) and reviewed before commit. One commit per issue. ## Fixed - **MCP timeout / head-of-line blocking** — `handleConn` dispatched requests serially, so a slow tool (`wait_for_pattern`, up to 300s) monopolized the single per-agent connection and queued calls (`get_process_status`) timed out behind it. Requests are now handled per-goroutine with serialized writes and a `WaitGroup` for graceful teardown. (`7b5a226`) - **Agent close needed two presses** — `agent-close` sent a single SIGTERM with no escalation, so an agent that traps SIGTERM (e.g. opencode) stayed in the tab bar until closed again. Added `Session.Terminate` (SIGTERM → wait → SIGKILL, entry preserved) run from the close handler in a goroutine. (`50fd7be`) - **Codex reported idle mid-turn** — codex uses `osc_title_stability`, but draws progress in the pane body, so its stable title made the classifier call it idle while working. Added a `(?i)esc to interrupt` thinking-promoter to the codex preset so it stays `thinking` until the turn's footer disappears. (`0725375`) - **Injected submit Enter intermittently dropped** — the trailing CR was written only 15ms after the body, inside agents' paste-coalescing window, so it was sometimes swallowed as a newline. The final lone Enter now gets a 100ms settle gap (centralized in `writeInput` via a pure `pieceWriteDelay`), covering send_input/send_message/timers/spawn-prompt. (`178b443`) ## Added - **`scratchpad_delete` MCP tool** — mirrors the existing scratchpad tools end-to-end (catalog, dispatch, host method delegating to `scratchpad.Store.Delete`, sidebar refresh). (`96f7c66`) ## Changed - **Grid `get_process_output` whitespace-normalized** — `normalizeGridText` strips trailing per-line whitespace, collapses blank runs, and normalizes line endings, cutting token waste from padded grid rows. Stream/raw output and `wait_for_pattern` matching untouched. (`53f06b6`) - **Per-tab summaries** — the tab bar now renders every agent tab's own summary on row 2, not just the focused tab's. (`d2342f9`) ## Verification - `go build ./...`, `go vet ./...`, all unit tests, and the full harness E2E suite pass. - Each fix is unit-tested (concurrent-dispatch no-HOL-blocking test, SIGKILL-escalation regression, classify thinking-override, `pieceWriteDelay`, `normalizeGridText`, `scratchpad_delete` round-trip, `summaryTextFor`). ### Caveat — needs a rebuild to confirm live Two changes are deterministic in code and unit-tested, but their real-world effect can only be confirmed after rebuilding + restarting patterm (the session they were developed in ran the old binary): - the submit-Enter timing fix (`178b443`) — a timing heuristic against codex's paste detector; - the per-tab summary rendering (`d2342f9`).
harry added 8 commits 2026-05-25 13:08:50 +01:00
Mirrors the existing scratchpad_* tools end-to-end: catalog schema,
dispatch, ToolHost.ScratchpadDelete, and a host method that delegates to
scratchpad.Store.Delete and fires scratchpadsChanged() on success so the
sidebar refreshes. Missing-pad errors surface rather than being masked.

Resolves the [MCP SCRATCHPAD DELETE] TODO item.
Agent 'Close' (agent-close) sent a single SIGTERM via Session.Kill and
never escalated, so an agent that traps/ignores SIGTERM (e.g. opencode)
stayed in the running tab bar until the user closed it again. Add
Session.Terminate, which reuses terminateAndWait (SIGTERM, wait, then
SIGKILL) but preserves the session entry so the exited pane stays
readable, and route handleChildClose's agent path through it in a
goroutine to keep the UI responsive during the stop timeout.

Resolves the opencode double-close TODO item.
Grid snapshots pad every row to the full terminal width and leave the
bottom of the screen blank, so MCP grid reads carried a lot of dead
whitespace. Add normalizeGridText (CRLF/lone-CR to LF, right-trim each
line, collapse blank runs to a single blank, drop leading/trailing
blanks) and apply it to the grid branch of GetProcessOutput only.
Stream output, raw output, and WaitForPattern matching are untouched.

Resolves the terminal-read newline/token-waste TODO item.
handleConn processed requests serially, so a slow tool (e.g.
wait_for_pattern with a 300s timeout) monopolized the single per-agent
MCP connection and every queued call timed out behind it. Handle each
request in its own goroutine, serialize responses through a per-conn
write mutex (full response written atomically, partial writes handled),
copy the request line before handing it off (bufio reuses its buffer),
and wait on a WaitGroup before closing the conn so in-flight handlers
finish cleanly. Greeting stays sequential; notifications still get no
response.

Resolves the [MCP TIMEOUT] TODO item.
Codex uses the osc_title_stability idle strategy, but it draws its
progress in the pane body ('Working … esc to interrupt'), not the OSC
title. The title goes stable mid-turn, so ~2s later the classifier
declared codex idle while it was still working. Add a thinking-promoter
pattern ((?i)esc to interrupt) to the codex built-in preset; classify()
checks promoter regexes against the rendered screen before the
title-stability verdict, so codex stays in thinking until the turn's
in-progress footer actually disappears.

Resolves the [CODEX IDLE] TODO item.
The trailing CR that submits orchestrator-injected input was written
only 15ms after the body, inside TUI agents' paste-coalescing window,
so codex (and other paste-detecting agents) intermittently swallowed it
as a newline and left the message composed but unsent. Centralize the
per-piece timing in a pure pieceWriteDelay helper: keep 15ms between
body lines but give the final lone Enter a 100ms settle gap so the
agent closes the preceding burst and registers the CR as submit. Covers
send_input, send_message, timers, and the spawn initial prompt (all go
through writeInput).

Resolves the codex composer-submit TODO item.
The tab bar's row-2 summary was painted only for the active tab. Add a
per-child summaryTextFor/summaryRawFor helper (active variants now
delegate to it), carry each tab's childID on its tabRect, and loop over
all visible tabs so each renders its own summary under its column.
Layout is unchanged (still 3 rows); narrow tabs clip as before.

Resolves the per-tab summary TODO item.
harry merged commit da46340a82 into main 2026-05-25 13:13:25 +01:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: harry/patterm#8