π Terminal REPL POC (#774) β VIEW_ONLY evidence - #779
Conversation
Disposable POC under packages/terminal-tmux/poc/repl/: a Flux-style action store, strict read-only Claude/Codex session-file observers, a generic terminal-convergence algorithm, literal tmux paste delivery, and a schema-validated report. Deterministic RP1-RP18 evidence runs against fake panes and synthetic session files in packages/terminal-tmux/tests/repl-poc.test.ts. The live journey is doubly gated per provider and unexercised until authorized. No production src, package export, CLI grammar, terminal/core/ACP API, Workflow, architecture, specification, dependency or lockfile changed.
| if (type === "result") { | ||
| return classifyResult(record); | ||
| } | ||
| // Summaries, system notices and anything else bear on nothing here. |
There was a problem hiding this comment.
Redundant comment β restates what the code does.
| // Summaries, system notices and anything else bear on nothing here. |
| provider: "codex", | ||
| identityFromName() { | ||
| // Codex names its rollout files by timestamp, not by identity, so the | ||
| // identity is only ever read from the `session_meta` record inside. |
There was a problem hiding this comment.
Redundant comment β restates what the code does.
| // identity is only ever read from the `session_meta` record inside. |
| return { outcome: "refused", refusal: open.refusal }; | ||
| } | ||
|
|
||
| // A pane whose generation moved is a replacement, never silently adopted. |
There was a problem hiding this comment.
Redundant comment β restates what the code does.
| // A pane whose generation moved is a replacement, never silently adopted. |
| } | ||
|
|
||
| yield* store.dispatch({ type: "TerminalObserved", key, readiness: "ready" }); | ||
| // The durable intent, before any byte reaches the terminal. |
There was a problem hiding this comment.
Redundant comment β restates what the code does.
| // The durable intent, before any byte reaches the terminal. |
| // The cursor was established against a particular file identity. Re-locating | ||
| // finds the current file, but a change of identity since the cursor was set | ||
| // is a rotation β so the remembered key, not the freshly located one, is | ||
| // what `read` enforces. |
There was a problem hiding this comment.
Redundant comment β restates what the code does.
| // what `read` enforces. |
| yield* store.dispatch({ type: "MessageQueued", key, id: messageId, text, marker }); | ||
|
|
||
| // Attempt until admitted, then observe until the exact user event and its | ||
| // completion appear, or a deadline diagnoses a hang and marks it uncertain. |
There was a problem hiding this comment.
Redundant comment β restates what the code does.
| // completion appear, or a deadline diagnoses a hang and marks it uncertain. |
| const decoder = new TextDecoder(); | ||
| const tail = decoder.decode(bytes.subarray(cursor)); | ||
| const segments = tail.split("\n"); | ||
| // The last segment has no terminating newline: it is the partial tail. |
There was a problem hiding this comment.
Redundant comment β restates what the code does.
| // The last segment has no terminating newline: it is the partial tail. |
| case "UserAccepted": | ||
| // Acceptance resolves an attempt in flight and also an attempt a restart | ||
| // left uncertain: a later exact user event under the intended identity is | ||
| // allowed to settle that uncertainty. |
There was a problem hiding this comment.
Redundant comment β restates what the code does.
| // allowed to settle that uncertainty. |
| trap(); | ||
| } | ||
| // A barrier is an acknowledged round-trip; the yield models that wait | ||
| // without changing any structural fact by itself. |
There was a problem hiding this comment.
Redundant comment β restates what the code does.
| // without changing any structural fact by itself. |
| // deno-lint-ignore require-yield | ||
| *guardedPaste(guard: PaneSnapshot, delivery: PasteRequest): Operation<GuardOutcome> { | ||
| // The recheck and the paste happen with no suspension between them: the | ||
| // current state is read and compared, and a matching guard pastes at once. |
There was a problem hiding this comment.
Redundant comment β restates what the code does.
| // current state is read and compared, and a matching guard pastes at once. |
PR #779: π§ͺ Prove black-box terminal REPL messaging (#774)19 files, +5718 / -0 Scopeπ΄ PR has 5718 lines changed. Split into focused PRs. π‘ 5718 lines changed. PRs under 400 receive more thorough review. Structuralβ No structural bloat detected. Slop
Static AnalysisOxlint: 9 diagnostics across 7 files (6 rules) no-unsafe-type-assertion (3): packages/terminal-tmux/poc/repl/report.ts, packages/terminal-tmux/poc/repl/controller.ts, packages/terminal-tmux/poc/repl/live-supervisor.ts CorrectnessNo extraneous code patterns detected. |
Addresses the Architect's REQUEST CHANGES on f94f501: - Convergence now samples the provider session file on both sides of the acknowledged barrier and the controller re-samples before the guard, so a turn opening mid-barrier refuses instead of pasting (new barrier-race regression). - The live tmux probe reads real pane generation, process, terminal, mode and output/client-activity, and honors the paste/submit command outcomes (declined/uncertain), never reporting pasted after a failed command. - Per-provider live grid documents so authorizing one provider cannot launch the other; supervisor gate isolation proven offline. - Observer locates by exact identity AND project from header records only, groups Claude output/completion by turn identity, and a missing Claude completion yields PROVIDER_EXCLUDED. - Delivery registers cleanup before acquiring the message file and tmux buffer, and removes the buffer; the live child registers teardown before spawn and proves close after SIGKILL. RP18 covers a declined partial acquisition. - The store parses every persisted action's full shape with zod and refuses filename/sequence disagreement, gaps, duplicates and unknown types. - report.schema.json gates PASS per mode on full evidence (head+base SHAs, version, hashed identities, acceptance/completion, spent turn, delivery); added negative schema rows. Deterministic RP1-RP18 plus the new discriminating rows pass under Deno, Node and Bun. No production, specification, or lockfile change.
|
|
||
| const key = identity.id; | ||
| // A provider-specific document launches exactly one pane, so the target is | ||
| // always pane 0 of the grid. |
There was a problem hiding this comment.
Redundant comment β restates what the code does.
| // always pane 0 of the grid. |
Second Architect round on PR #779. Six blockers: 1. Provider state (open turn, cursor, event count, physical size incl. a partial tail) is sampled across the barrier and again after the buffer is prepared, so a turn opening during buffer load or the barrier refuses with zero paste. 2. The live tmux boundary is one server-side if-shell recheck-and-paste over an injectable command seam with real control-mode activity generations; its outcome is acknowledged (pasted/declined/uncertain), never always-pasted. Boundary tests drive the live probe through a fake seam. 3. Observer locates by exact identity and project from bounded header reads, fails closed on missing project metadata, requires Claude's requestId turn identity, and derives PROVIDER_EXCLUDED from an explicit capability, not a deadline. 4. Delivery prepares before the final sample and cleans file and buffer on success, decline, uncertain and cancellation; the live child proves close after SIGKILL; the isolated root is created synchronously and cleanup is read only after teardown; provider state is cleaned via the provider's own op. 5. The store validates legal transitions on replay and dispatch, and observation deduplicates an event a restart already recorded. 6. An overall aggregator gates PASS on RP1-RP18 plus both live journeys, safe counters, no re-execution and verified cleanup; the schema rejects an unsafe, uncleaned or re-executed PASS; live reports carry a real head SHA and count the materialization turn; the documents invoke the full checked-in validator. No production, specification, dependency or lockfile path changed.
| recheck.outcome === "unreadable" | ||
| ? recheck.reason | ||
| : "provider-or-pane-changed-before-guard"; | ||
| // Nothing was recorded and nothing sent: reset the head to queued. |
There was a problem hiding this comment.
Redundant comment β restates what the code does.
| // Nothing was recorded and nothing sent: reset the head to queued. |
| } | ||
| // An event a restart already recorded is not dispatched again: the durable | ||
| // event carries its own file-and-byte-range key, so re-reading the same | ||
| // record after an interruption produces no duplicate action or event. |
There was a problem hiding this comment.
Redundant comment β restates what the code does.
| // record after an interruption produces no duplicate action or event. |
| return; | ||
| } | ||
| // Assistant output and completion belong to the accepted message only when | ||
| // they are part of the same provider turn. |
There was a problem hiding this comment.
Redundant comment β restates what the code does.
| // they are part of the same provider turn. |
| return { outcome: "not-ready", reason: `provider-${second.reason}` }; | ||
| } | ||
| if (second.sample.provider.openTurn) { | ||
| // A turn that opened during the barrier β the barrier race. |
There was a problem hiding this comment.
Redundant comment β restates what the code does.
| // A turn that opened during the barrier β the barrier race. |
| const path = join(request.dir, `${request.id}.msg`); | ||
| const buffer = bufferName(request.id); | ||
| // Cleanup registered before either resource exists, so a halt between | ||
| // acquiring and registering cannot leave the file or the buffer behind. |
There was a problem hiding this comment.
Redundant comment β restates what the code does.
| // acquiring and registering cannot leave the file or the buffer behind. |
| const parsed = parser.classify(record); | ||
| if (parsed.kind !== "identity") { | ||
| // No identity record before the first relevant/other record: this file | ||
| // does not declare an identity header, so it is not a match here. |
There was a problem hiding this comment.
Redundant comment β restates what the code does.
| // does not declare an identity header, so it is not a match here. |
| const decoder = new TextDecoder(); | ||
| const tail = decoder.decode(bytes.subarray(cursor)); | ||
| const segments = tail.split("\n"); | ||
| // The last segment has no terminating newline: it is the partial tail. |
There was a problem hiding this comment.
Redundant comment β restates what the code does.
| // The last segment has no terminating newline: it is the partial tail. |
| case "UserAccepted": | ||
| // Acceptance resolves an attempt in flight and also an attempt a restart | ||
| // left uncertain: a later exact user event under the intended identity is | ||
| // allowed to settle that uncertainty. |
There was a problem hiding this comment.
Redundant comment β restates what the code does.
| // allowed to settle that uncertainty. |
| } | ||
| default: | ||
| // TerminalObserved, ProviderBusy/Idle, AssistantObserved, ObserverAdvanced, | ||
| // PaneUnavailable and ObserverRefused only need the role to exist. |
There was a problem hiding this comment.
Redundant comment β restates what the code does.
| // PaneUnavailable and ObserverRefused only need the role to exist. |
| return { outcome: "declined", reason: "tmux-command-failed" }; | ||
| } | ||
| if (failure === "uncertain") { | ||
| // The buffer pasted but the submit key could not be proved sent. |
There was a problem hiding this comment.
Redundant comment β restates what the code does.
| // The buffer pasted but the submit key could not be proved sent. |
Result-only closeout, no production/architecture/specification change. - RESULT.md records the VIEW_ONLY verdict and the exact race: a provider turn can open between the final combined sample and the single guarded paste, a window that is not observable before the paste and cannot be atomically refused, so reliable dispatch is not established and tmux delivery stays view-only while reliable REPL interaction remains ACP-owned. - The live-delivery journey is permanently disabled: the supervisor launches no agent and spends no turn under any gate, returning the VIEW_ONLY conclusion; live-worker keeps only the terminal-boundary evidence. The grid launch documents and the two live proof documents are removed. - The deterministic suite records the VIEW_ONLY conclusion in a schema-valid overall report and proves the journey is disabled; it passes under Deno, Node and Bun. - README updated with the conclusion.
| if (type === "result") { | ||
| return supportsCompletion ? classifyResult(record) : { kind: "ignore" }; | ||
| } | ||
| // Summaries, system notices and anything else bear on nothing here. |
There was a problem hiding this comment.
Redundant comment β restates what the code does.
| // Summaries, system notices and anything else bear on nothing here. |
| supportsCompletion: true, | ||
| identityFromName() { | ||
| // Codex names its rollout files by timestamp, not by identity, so the | ||
| // identity is only ever read from the `session_meta` record inside. |
There was a problem hiding this comment.
Redundant comment β restates what the code does.
| // identity is only ever read from the `session_meta` record inside. |
| return { outcome: "refused", refusal: readOut.refusal }; | ||
| } | ||
| // The physical size, including a partial tail no cursor covers, so a record | ||
| // being written is visible before it parses as a complete event. |
There was a problem hiding this comment.
Redundant comment β restates what the code does.
| // being written is visible before it parses as a complete event. |
| recheck.outcome === "unreadable" | ||
| ? recheck.reason | ||
| : "provider-or-pane-changed-before-guard"; | ||
| // Nothing was recorded and nothing sent: reset the head to queued. |
There was a problem hiding this comment.
Redundant comment β restates what the code does.
| // Nothing was recorded and nothing sent: reset the head to queued. |
| } | ||
| // An event a restart already recorded is not dispatched again: the durable | ||
| // event carries its own file-and-byte-range key, so re-reading the same | ||
| // record after an interruption produces no duplicate action or event. |
There was a problem hiding this comment.
Redundant comment β restates what the code does.
| // record after an interruption produces no duplicate action or event. |
| case "UserAccepted": | ||
| // Acceptance resolves an attempt in flight and also an attempt a restart | ||
| // left uncertain: a later exact user event under the intended identity is | ||
| // allowed to settle that uncertainty. |
There was a problem hiding this comment.
Redundant comment β restates what the code does.
| // allowed to settle that uncertainty. |
| }); | ||
|
|
||
| // The schema is held to the declared action union rather than the union being | ||
| // read off it: a change to either the schema stops compiling here. |
There was a problem hiding this comment.
Redundant comment β restates what the code does.
| // read off it: a change to either the schema stops compiling here. |
| } | ||
| default: | ||
| // TerminalObserved, ProviderBusy/Idle, AssistantObserved, ObserverAdvanced, | ||
| // PaneUnavailable and ObserverRefused only need the role to exist. |
There was a problem hiding this comment.
Redundant comment β restates what the code does.
| // PaneUnavailable and ObserverRefused only need the role to exist. |
| // deno-lint-ignore require-yield | ||
| *guardedPaste(guard: PaneSnapshot, delivery: PasteRequest): Operation<GuardOutcome> { | ||
| // The recheck and the paste happen with no suspension between them: the | ||
| // current state is read and compared, and a matching guard pastes at once. |
There was a problem hiding this comment.
Redundant comment β restates what the code does.
| // current state is read and compared, and a matching guard pastes at once. |
| return { outcome: "declined", reason: "tmux-command-failed" }; | ||
| } | ||
| if (failure === "uncertain") { | ||
| // The buffer pasted but the submit key could not be proved sent. |
There was a problem hiding this comment.
Redundant comment β restates what the code does.
| // The buffer pasted but the submit key could not be proved sent. |
Result: VIEW_ONLY β evidence, not a shipped REPL
Issue #774 asked whether generic terminal-state convergence can make black-box
tmux input delivery reliable enough while provider session files supply
authoritative acceptance and completion. After the deterministic evidence and
architecture review, the decision is VIEW_ONLY:
stays ACP-owned.
This PR is that evidence. It is a finite, disposable POC under
packages/terminal-tmux/poc/repl/. It exports nothing from the package andchanges no production, architecture, specification, dependency or lockfile.
The exact race
The controller samples the pane and the provider across an acknowledged barrier,
prepares the private file and tmux buffer, then takes one final combined sample
before recording
AttemptStartedand pasting. Every observable change up to thatfinal sample refuses with zero paste.
What cannot be closed is the interval between that final sample and the single
guarded paste. A provider turn can open there. It is not observable before the
paste, and a tmux-only guard cannot atomically refuse it (the guard can recheck
pane generation, process, liveness and mode, but not the provider's session
file). A delivery admitted in that window is only ever settled
uncertainafterthe fact, never proved safe before the bytes are sent. Because a safe input point
cannot be guaranteed, tmux panes remain view-only for coordinated work.
Full write-up:
packages/terminal-tmux/poc/repl/RESULT.md.What the evidence proves (all deterministic, Deno/Node/Bun)
header reads, cursor advances only past a complete record, Claude output grouped
by
requestIdturn, and refusals for ambiguity, truncation, rotation, identitymismatch and unsupported shapes. Never writes a provider file.
(including a partial tail) into both barrier samples and a final post-preparation
sample, so a turn opening during the barrier or during buffer load refuses with
zero paste.
staged-write log that refuses gaps, duplicates, malformed records and illegal
transitions, and observation that never duplicates an event across restart.
PASSwas reachable only with bothlive provider journeys; the suite records the VIEW_ONLY conclusion in a
schema-valid overall report.
injectable tmux command seam with a real control-mode activity source), driven
by fake-seam boundary tests.
Closeout
coding agent and spends no model turn under any gate; it returns the VIEW_ONLY
conclusion. The grid launch documents and the two live proof documents are
removed. No Claude or Codex model turn was ever spent.
How to verify
deno task test packages/terminal-tmux/tests/repl-poc.test.ts deno task check deno task lint git diff --checkScope confirmation