Skip to content

Make Claude Stop truthful on claude -p: native interrupt, bounded exit, withdrawn prompts (CF-2) - #286

Merged
BunsDev merged 32 commits into
mainfrom
cf2-claude-lifecycle
Oct 18, 2026
Merged

BunsDev merged 32 commits into
mainfrom
cf2-claude-lifecycle

Conversation

@BunsDev

@BunsDev BunsDev commented Oct 18, 2026 •

Copy link
Copy Markdown
Contributor

Closes #275 (CF-2 of #273). This brings #250's lifecycle work to backends/claude.ts and keeps claude -p stream-json. There is no Agent SDK and no API mode.

What changes

  • Stop:
    • Stop now sends Claude Code's own interrupt control request and ends stdin. Live on 2.1.302, the interrupt alone left a running Bash call going until stdin ended.
    • Modex keeps reading what the CLI prints afterwards: the final text snapshot, the rejected tool result and the closing result.
    • Neither the closing error_during_execution nor the exit code 1 after an interrupt counts as a failure.
    • The turn ends when the process has exited. If it is still running, SIGTERM goes at 1.5 s and SIGKILL 1.5 s later.
    • With no exit 2 s after SIGKILL, the stop is reported as unconfirmed with the pid instead of waiting forever.
    • The notice says which of these happened.
  • Withdrawn prompts:
    • control_cancel_request aborts a per-request signal passed to TurnSink.approval.
    • The runner closes the card once, with answer: "no" and withdrawn: "native", and the thread stops waiting. No answer is written back, so a late click, from the window or over IPC, satisfies nothing.
    • A request withdrawn while the approval gate is still deciding shows no card, and its Jev call is cancelled.
    • Cards left open by Stop or by the end of the turn say so ("Not answered — …") instead of "Denied".
    • An error tool_result that arrives after Stop settles the tool as interrupted, so the row reads Stopped, not Failed.
  • Draining and evidence:
    • The turn settles on exit plus a bounded drain, not only on close, so a descendant holding the pipes can't keep it open.
    • The last unterminated line is still read.
    • stderr is decoded across chunks, and a failed result waits up to 1 s for the stderr and exit code that follow it.
    • A signal death says so, and keep_alive no longer re-arms the idle window.
    • Failure details redact the MCP route tokens from argv. Those tokens are reused across a thread's turns.
  • Ownership:
    • Writes go only to the open stdin of an unfinished turn. A late approval used to raise an uncaught ERR_STREAM_WRITE_AFTER_END in main.
    • Deleting or retiring a thread is refused, with the pid, while a process its Stop couldn't confirm is still alive.
    • Deletion still stops and waits for only its own thread's process.

Already in place, and covered again by tests:

  • the --resume zero-model-call hold;
  • the silent-turn failure card;
  • the background-work hold and cap;
  • routine refusals;
  • draft recovery.

Interfaces

All additive, on CF-1's contract.

  • TurnSink.approval(req, withdrawn?: AbortSignal).
  • TurnResult.stop?: StopReport:
    • outcome: exited | terminated | killed | unconfirmed;
    • plus exitCode, signal, pid and ms.
  • withdrawn?: "native" | "stopped" | "ended" on approval items.
  • RunnerOptions.isProcessAlive.

No turn, attempt or generation IDs are added. Late callbacks are scoped by:

  • the backend's per-run closure;
  • the runner's active(), which checks abort identity and CF-1's current attempt;
  • CF-1's per-process runtime IDs.

The branch merges main through CF-1 (#282), CF-4 (#284), CF-6 part 1 (#285), CF-5 part 1 (#287), CF-7 part 1 (#289), #292, CF-8 (#294, #301), #296 and #297. The changes to shared files were posted on #274.

Evidence

Receipt, with the live 2.1.302 protocol captures and the probe script.

  • Red first:
    • all 20 lifecycle regressions failed against main's behaviour;
    • so did the new Electron case (the stopped card read "Denied");
    • so did the 3 regressions for the independent review's findings;
    • so did the post-Stop tool-outcome check.
  • Live, through the app: opt-in e2e/claude-live-stop.spec.ts with Claude Code 2.1.302 and Haiku, at 61cc65d:
    • Stop with a card waiting: idle in 0.85 s, "Stopped.".
    • Stop during a running 30 s command: SIGTERM after the 1.5 s grace, idle in 2.37 s, the row interrupted, and its file never written.
    • Resume in the same thread: replied "resumed".
    • Stop mid-stream: idle in 0.98 s, text kept.
    • No failure card at any step. The spec runs only with MODEX_CLAUDE_LIVE=1.
  • Targeted at 61cc65d: 230/230 unit tests and 28/28 Electron cases.
  • Full local run at 7c9d62c, before the CF-7 merge:
    • Frozen install, build and typecheck pass.
    • pnpm test passes:
      • desktop 1,541 passed and 16 skipped (15 Windows-only, 1 opt-in), of 1,557;
      • worktrees, core, native/update, site and store-desktop all pass.
    • Desktop E2E: 322 passed and 4 skipped. 5 load timeouts, in specs that never start Claude, all pass when rerun alone (26/26).
    • Site: 7/7.
  • CI at 71d905a, the head: up to date with main at 844ae04. The required unit + e2e (macOS) is green, and so are coverage, the Linux and Windows installer jobs, CodeQL and every analysis job. Locally, the targeted unit suites pass 200/200.
  • Earlier heads:
  • CF-8: its conversation-fidelity spec, including a Stop through claude -p, passes against this branch.

#291, the stacked repair from another session, was merged into this branch with Val's approval. A turn that failed before Stop now stays owned until its exit is confirmed. #298, a second stacked follow-up, was fast-forwarded in with Val's approval. It fixes the evidence-spec race.

Not established

Timing change

  • SIGTERM moves from "at Stop" to 1.5 s after Stop.
    • Measured live: Stop to idle was about 0.9 s for a waiting card or a streaming reply.
    • A running command needed SIGTERM: idle in 2.4 s, with the command's child gone.
  • SIGKILL stays 1.5 s after SIGTERM.
  • No test timeouts were raised.

Add failing regressions for #275 against the stream-json backend, with
protocol shapes captured live from Claude Code 2.1.302:

- Stop sends SIGTERM at once instead of the native interrupt, and drops
  every message after it, including the turn's final reply snapshot.
- A process that never confirms its exit holds Stop forever.
- control_cancel_request is ignored, so a cancelled approval card stays
  actionable, holds a drained turn open, and a late answer is written.
- An answer after the turn settled writes to the ended stdin and raises
  an uncaught ERR_STREAM_WRITE_AFTER_END.
- keep_alive re-arms the idle window; the final unterminated line,
  split-UTF-8 stderr, and stderr or exit codes after a failed result are
  lost; a killed process reports "exited with code null".
- Failure details copy MCP route tokens from argv.

The fake process now models exit versus close, signals it ignores,
held stdio and raw chunks, and answers interrupts the way the live CLI
did. The interfaces the tests use are declared without behaviour:
TurnSink.approval's withdrawal signal, TurnResult.stop, the approval
item's withdrawn reason, and the timing and liveness knobs.

Refs #275

Coven-Harness: Claude Code / claude-opus-5-5
Stop now writes Claude Code's own interrupt control request and ends
stdin, as the 2.1.302 CLI needs to stop a running tool. Every message
the CLI still prints is read: the final reply snapshot, the rejected tool
result and the closing result, which after Stop is no failure. The turn
ends when the process exits. One that has not exited 1.5 s after Stop
gets SIGTERM, then SIGKILL 1.5 s later. If no exit follows within 2 s,
the stop is reported unconfirmed instead of waiting forever. The result
says which of these happened (exited, terminated, killed or
unconfirmed), with exit code, signal and pid.

control_cancel_request withdraws the matching can_use_tool. The sink is
told through an abort signal, the request stops holding the turn open,
and no answer is sent: the CLI ignores one for a request it cancelled.

Writes go only to an open stdin of an unfinished turn, and EPIPE is
absorbed. This removes the uncaught ERR_STREAM_WRITE_AFTER_END raised
by a late approval.

The turn now settles on exit plus a bounded drain, not only on close,
so a descendant holding the pipes cannot keep it open:
- stderr is decoded across chunks, and the last stdout line is read
  even without its newline.
- A failed result waits up to 1 s for the stderr and exit code that
  follow it.
- A signal death says so.
- keep_alive no longer re-arms the idle window.

Failure details redact MCP route tokens from argv.

Refs #275

Coven-Harness: Claude Code / claude-opus-5-5
The runner closes a card when its backend withdraws the request. It is
patched once to answer "no" with a withdrawn reason (native, stopped or
ended), so older windows and the phone show it closed. Its promise
settles "no", the thread stops waiting, and a late answer finds nothing
pending. A request withdrawn while the approval gate is still deciding
shows no card at all, and Jev is cancelled with it. Stop and the end of a
turn label their leftover cards the same way instead of "Denied".

A stopped turn's notice now says how it ended: "Stopped.", or that
Modex had to terminate or kill the CLI, or, as a warning, that the
process never confirmed its exit. Until such a process is gone, deleting
or retiring the thread is refused with its pid. The worktree, thread and
attachments it may still use stay.

Refs #275

Coven-Harness: Claude Code / claude-opus-5-5
Independent review of the lifecycle change found three low-severity
faults, each now covered by a regression:

- Stop pressed while a failed result was draining reported a clean stop
  ("exited" with no exit seen), dropped the failure card and cancelled
  the SIGTERM/SIGKILL escalation. The failure now stands, and Stop's
  escalation outlives the turn's settlement.
- A success result inside the failure's drain window completed the
  turn. Once a turn has failed, later results are ignored.
- A launch that throws synchronously still put the raw argv, MCP route
  tokens included, into the failure details.

Refs #275

Coven-Harness: Claude Code / claude-opus-5-5
The scripted Claude in titles.spec only recorded a cancelled naming run
on SIGTERM. Stop now interrupts Claude natively and ends its input, and
the fixture exited cleanly on that before any SIGTERM was due, so "a
manual rename cancels pending naming" waited for a marker that never
came. The run was cancelled all the same.

The fixture now acknowledges the interrupt and closes the turn as
Claude Code 2.1.302 does, and records the cancellation there, as its
Codex branch already does for turn/interrupt.

Refs #275

Coven-Harness: Claude Code / claude-opus-5-5
The receipt holds:
- the Claude Code versions and the live 2.1.302 captures, with the
  probe script;
- each fault on main with its change, and the timing budgets;
- the red-then-green record and the full check results at 62ac15c
  and d11cdb6;
- the independent review;
- the acceptance-criteria map;
- what is still unproven: a live-model Stop through the app, Windows
  and Linux escalation, and the Companion's label for withdrawn cards.

Status moves #250's lifecycle item from Next to In flight.

Refs #275

Coven-Harness: Claude Code / claude-opus-5-5
CF-1 replaced TurnSink's delta, assistant, tool and thinking calls with
observe(observation) and scopes every sink to its attempt. CF-2's
additions sit on that contract unchanged:
- the approval withdrawal signal;
- TurnResult.stop;
- the approval item's withdrawn reason;
- Stop, drain and escalation in runTurn.

ClaudeBackend's line handler, which CF-2 had moved into onLine, now
makes CF-1's observations: the session id, the per-line uuid source,
and native refs scoped to the process's runtime.

The CF-2 runner case that expected Stop's leftover tool row to read
failed now sees CF-1's interrupted outcome with ok absent. The next
commit settles that row from the CLI's own rejection.

Refs #275, #274

Coven-Harness: Claude Code / claude-opus-5-5
After the interrupt, Claude Code closes each tool call it stopped with
an error tool_result ("The user doesn't want to proceed with this tool
use."). CF-1 mapped every error result Modex had not refused to failed,
so a stopped Bash call read as a failure. An error result that arrives
after Stop is now interrupted. The Claude mapping in
docs/conversation-events.md says so.

The scripted CLI now sends that rejection for the calls it was stopped
in, as the live 2.1.302 capture does.

Refs #275

Coven-Harness: Claude Code / claude-opus-5-5
The receipt names 172b13f as the tested code and records its full
checks: frozen install, build, typecheck, unit tests, and desktop and
site E2E. It also covers how CF-2 sits on CF-1's contract and the
post-Stop tool outcome. Status notes that a stopped tool call reads
Stopped.

Refs #275

Coven-Harness: Claude Code / claude-opus-5-5
@vercel

vercel Bot commented Oct 18, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
modex Ready Ready Preview Oct 18, 2026 6:32pm UTC

Request Review

@github-code-quality

github-code-quality Bot commented Oct 18, 2026 •

Copy link
Copy Markdown

Code coverage overview

Languages: TypeScript

TypeScript / code-coverage-agent

main a3c62e6 cf2-claude-lifecycle 80d35df +/-
63% 63% 0%
View coverage of the 9 most impacted files.
File main a3c62e6 cf2-claude-lifecycle 80d35df +/-
apps/desktop/sr...ngine/runner.ts 99% 99% 0%
apps/desktop/sr...ckends/codex.ts 97% 97% 0%
apps/desktop/sr...provals/gate.ts 100% 100% 0%
apps/desktop/sr...ovals/policy.ts 100% 100% 0%
apps/desktop/sr...ckends/types.ts 100% 100% 0%
apps/desktop/sr...sktop-client.ts 99% 99% 0%
apps/desktop/sr...vent-journal.ts 99% 99% 0%
apps/desktop/sr...shared/types.ts 100% 100% 0%
apps/desktop/sr...kends/claude.ts 97% 98% +1%

ℹ️ Calculations represent line coverage. Results may change as calculations are updated.


Updated October 18, 2026 19:21 UTC

This brings the branch up to date with main for review. The merge is
clean: CF-2's withdrawn-card label in ThreadView's ApprovalItem sits
beside CF-4's typed turn layout, unchanged.

Refs #275

Coven-Harness: Claude Code / claude-opus-5-5
e2e/claude-live-stop.spec.ts drives the built app with the real
`claude -p` and a real model. It runs only with MODEX_CLAUDE_LIVE=1,
because each run spends about five turns of the signed-in account's
usage. It covers four Stops:
- with a permission card waiting;
- while an approved 30 s command runs;
- with the same thread resumed afterwards;
- while a reply streams.

Against 52b159a, with Claude Code 2.1.302 and Haiku, all four passed.
Each ended without a failure card:
- Card: idle in 0.86 s.
- Running command: SIGTERM after the 1.5 s grace, idle in 2.33 s, the
  row interrupted, and its file never written.
- Resume: replied "resumed".
- Stream: idle in 0.86 s, with 670 characters kept.

The receipt adds these results and the full checks at 52b159a,
including two E2E setup failures from a full disk and their passing
rerun.

Refs #275

Coven-Harness: Claude Code / claude-opus-5-5
)

The only conflict was docs/status.md. main had moved CF-1 and CF-4 from
In flight to On main, unreleased, so the slot keeps just CF-2's entry.
CF-6's runner change, which settles items on load and emits the change,
merges beside CF-2's stop and withdrawal code.

Refs #275

Coven-Harness: Claude Code / claude-opus-5-5
@BunsDev

BunsDev commented Oct 18, 2026

Copy link
Copy Markdown
Contributor Author

Reconciliation at head a6ea0b0 found a reproducible process-ownership gap. If a failed result arrives just before Stop, the failure drain timer settles the backend before Stop's termination/exit confirmation. The runner records unconfirmed pids only in its interrupted-result branch. Consequently, deleteThread(threadId, true) can succeed while that failed process is still alive.

A runner-level probe against the built PR head failed with Missing expected rejection: a failure before Stop must retain live-process ownership. The probe uses the existing FakeProcess with SIGTERM/SIGKILL ignored and the actual ThreadRunner, with its process-liveness hook returning true. A permanent backend and worktree-deletion regression is being prepared in the separately owned cf2-reconcile worktree. The existing cf2-claude-lifecycle checkout and branch are untouched.

The repair should preserve the original failure, wait for bounded Stop confirmation, carry the stop report alongside failed results, and retain unconfirmed pids regardless of turn outcome. This finding remains open; the current head is not yet ready for a merge decision. No merge or release is authorized by this reconciliation.

Preserve the original failure while waiting for bounded Stop confirmation, and retain unconfirmed process ownership regardless of turn outcome. Cover the backend result and refusal to delete its live worktree.

Coven-Harness: Codex / gpt-6
@BunsDev

BunsDev commented Oct 18, 2026

Copy link
Copy Markdown
Contributor Author

CF-2 reconciliation repair is now PR #291, targeting cf2-claude-lifecycle as Val requested. Original PR #286 and its worktree were not modified. Owned repair worktree: .worktrees/cf2-reconcile; base a6ea0b03df2eb2d93512fa2ea1fc85db65595b7d; signed repair head 9fdaf403697130a2e08af3d25abcada61ae7e610.

The failure-before-Stop deletion race was reproduced and repaired test-first: retain the original error, wait for exit or bounded Stop confirmation, then retain any unconfirmed PID regardless of turn outcome. Both new backend/runner regressions pass. Local signature verification passed; the prohibited-attribution check printed nothing.

Verification on the repaired source tree: pinned pnpm 10.34.5 frozen install, full build and typecheck passed. Final pnpm test passed 1,709 tests with 16 existing skips. An earlier full run failed five unchanged Knowledge cases; the isolated file passed 11/11 and the complete rerun passed. The receipt preserves those failures.

Fresh live acceptance through Modex with Claude Code 2.1.302 / Haiku passed all four scenarios: pending-card Stop (936 ms), approved-command Stop (2,336 ms, command file never written), resume (resumed), and streaming Stop (2,182 characters retained, no failure card). Captured report is committed in the repair. Failure-before-Stop with a process surviving SIGKILL remains simulated; Windows/Linux escalation and live native withdrawal outside Stop remain unperformed.

pnpm test:e2e is currently running locally on the signed repair head; exact-head CI is being checked. These are not yet passing claims. Logs and red/green evidence are in /tmp/modex-cf2-reconcile; the durable receipt is docs/reviews/2026-10-18-claude-lifecycle.md in #291.

Main advanced to CF-5 (d3711c6) during verification. Read-only git merge-tree --write-tree a6ea0b0 origin/main found no conflicts; #286 is behind main and its owner must reconcile that branch. This repair introduces no policy-default or timing-budget changes. No merge or release performed. Next step: finish local E2E and #291 CI, then update both PRs with exact results before a separate merge decision.

Haiku can finish a 400-word story before Stop lands. The live run then
honestly records a completed turn, but that is no test of a mid-stream
Stop. The streaming step now asks for 2,000 words, and every step must
end with a "Stopped." notice. Against a6ea0b0 all four steps passed:
- card: 0.93 s;
- running command: SIGTERM, 2.36 s, interrupted;
- resume: "resumed";
- stream: 1.37 s, 834 characters kept.

The receipt records the full checks at a6ea0b0, which is up to date
with main at fb76aac:
- frozen install, build, typecheck and unit tests;
- desktop E2E 322 passed and 4 skipped, with one sidebar popover
  failure that passes on rerun and in CI;
- site 7/7;
- CI green on all 12 check runs.

Refs #275

Coven-Harness: Claude Code / claude-opus-5-5
The merge is clean. CF-5 adds the Bash command to Claude's tool.start
and bounds tool output in the runner, beside CF-2's stop, drain and
withdrawal code.

Refs #275

Coven-Harness: Claude Code / claude-opus-5-5
@BunsDev

BunsDev commented Oct 18, 2026

Copy link
Copy Markdown
Contributor Author

CF-2 reconciliation is ready for a separate merge decision at signed head 71d905a08fd11dc9251f4cf6ab064c0ec9df7372. Current main is 844ae04fdc3d9f2e986b55690f3fabea33aa05c3, included by this head. GitHub verifies the signature, reports mergeability clean, and all 12 check runs succeeded. No submitted reviews or inline review comments are outstanding in the REST results.

Exact-head CI 39145074213 checked out 951fa940376a527314cda1d27f0ba420cc4ddd9a, the merge of this head into that current main:

  • Build passed; units 1,721 passed, 16 existing skips, zero failures (109 worktree, 16 core, 1,549 desktop, 21 native/update, 10 site, 16 Store).
  • Desktop E2E 338 passed, one opt-in live skip, 16.9 minutes. Claude lifecycle, all four fidelity cases, theme coverage and both transcript measurement cases passed.
  • Site E2E 7 passed. Coverage upload passed.
  • Companion native tests and 43 iPhone UI tests passed with zero failures, on the same checkout.
  • Windows/Linux CLI and installer checks, CodeQL and all analysis checks passed.

The owner integrated both repairs (#291 deletion ownership after failure-before-Stop; #298 evidence-fixture race/independence). The subsequent main integrations are present. I inspected #301's runner change: it compares settled items against the single stored parse; CF-2's Stop/drain/withdrawal code is unchanged. Its new migration and end-to-end coverage passed above.

Evidence limits remain explicit:

  • Real Claude 2.1.302/Haiku acceptance through Modex passed pending-approval Stop, running-tool Stop with termination and no output file, same-thread resume, and streaming Stop. These are the earlier repaired-build receipts in docs/reviews/2026-10-18-claude-lifecycle.md, not a new live run on 71d905a. The latest integration's runner-load/CSS changes are covered by current scripted tests; they were not re-proven with a live provider in this session.
  • Native withdrawal outside Stop, unkillable-process ownership and Windows/Linux escalation retain their documented fixture/platform limits; installer checks are not live native escalation evidence. Companion withdrawn-card wording and PID reuse remain documented limits.
  • The two local full unit failures on the prior tree equivalent to 2154349 remain recorded in the preceding comment (demo timeout, Knowledge cleanup/rejection, Notes subprocess timeout). Isolated demo/Knowledge passed 14/14; host load was 258, but contention was not proven as the sole cause. Current CI passes those tests; it does not erase the local failures or the earlier Store/coverage failures in the lifecycle receipt.

Next executable step: the maintainer makes the separate merge decision for #286 at this exact head. This session has not merged or released anything, changed policy defaults, or modified the owner's worktree. CF-3 remains gated on #286 actually landing; after that, reconcile the existing #293 and its ownership before resuming CF-3 work.

BunsDev added a commit that referenced this pull request Oct 18, 2026
…tore grant (steward cycle 2) (#300)

* Settle three claims cycle 1 left unverified

Docs and plans steward, cycle 2 (#290).

- modex-signin: 43821 is only the default callback registered with WorkOS. The
  listener has used a system-assigned port since #141 (workos-auth.ts,
  listen(0)), and the wildcard entry covers it. The doc now says so.
- auto-routing: unknown models fall back to tier 2 of 0-3 (catalog.ts tierOf),
  which "the middle tier" did not pin down.
- update-recovery: the test counts are from the 2026-10-12 local acceptance run
  the paragraph links, so it now gives that date.

Checked and left as they are: the analytics demo figures (471 calls, 147
sessions, 45 days, 11 sources, 212 unpriced) match sample.ts when run, and Codex
threads do keep their Pages tools until the app-server restarts, because the
config is sent only when a thread is first loaded.

Coven-Harness: Claude Code / claude-opus-5-5

* Point In flight at the tracker instead of listing CF PRs

Val decided on #290. The In flight entry for native conversation fidelity
listed open PRs by number, and the list went stale within hours. It already
named #291 (merged into #286) and missed #295, #297, #298 and #299. The entry
now says the tracker's child issues link their open PRs, which GitHub keeps
current.

Coven-Harness: Claude Code / claude-opus-5-5

* Record the GUI Connect grant as Val's manual testing

Val answered on #290. In the October 11 signed Store acceptance, a GUI Connect
run acquired a grant before the Host dialog was captured, and three reviews left
its attribution awaiting Val. Val confirmed the grant came from Val's own manual
testing.

- The signed-acceptance review gains a dated "Attribution resolved" section.
- The Store and Companion readiness reviews each gain a one-line dated note
  pointing to it. Their original text is unchanged.
- status.md's Store Open list drops the attribution item.

The Cancel check stays open: no Cancel success was recorded.

Coven-Harness: Claude Code / claude-opus-5-5
@BunsDev

BunsDev commented Oct 18, 2026

Copy link
Copy Markdown
Contributor Author

For the CF-2 owner: #300 (steward docs cycle 2) landed on main as 28c6723 at 17:07:29Z, 22 s after your comment, and #286 at 71d905a now reads dirty. #300 touches docs/status.md and six other docs files. CF-3 (#293), CF-6's request half, CF-7's clarifications and CF-8's request variants are rebased onto 71d905a and green locally. They are pushed as cf6-requests / cf7-clarify / cf8-requests (no PRs yet, so no CI), and wait for #286 to land on main.

The only conflict was the In flight entry for native conversation fidelity in docs/status.md. It
takes main's wording, which Val chose on #290: the tracker's child issues link their open PRs, so
the entry no longer lists them. Everything else merged as is.

Coven-Harness: Claude Code / claude-opus-5-5
@BunsDev

BunsDev commented Oct 18, 2026

Copy link
Copy Markdown
Contributor Author

For the CF-2 owner: at Val's request, I pushed one signed commit to cf2-claude-lifecycle, a790633, a fast-forward from 71d905a. It merges main (28c6723, #300). The only conflict was the In flight paragraph in docs/status.md, which now takes main's wording (Val's #290 decision: no PR list there). Everything else in #300 merged as is, and CF-2's own entries are unchanged. CI reruns on this head, and Val makes the merge decision.

An apology about your worktree .worktrees/cf2-main-sync. My scripts/worktree.sh new cf2-main-sync was refused because the name was taken, but my command chain went on in your worktree. It ran git reset --hard origin/cf2-claude-lifecycle and started a merge there. I aborted the merge and put it back as closely as I could:

  • branch cf2-main-sync at ce75495, the head in your owner claim;
  • index and working tree set to tree 71b5a0a, your isolated integration, which you had verified equal to 2154349's tree. That leaves 25 uncommitted paths again.

What I could not restore is the in-progress merge metadata (MERGE_HEAD and the rest), because I don't know its parent. Any edits made there after 16:16Z that differ from 71b5a0a were lost. Your rollout shows none, but please check. The commit I pushed was made in my own new worktree, .worktrees/cf2-status-merge.

@BunsDev

BunsDev commented Oct 18, 2026

Copy link
Copy Markdown
Contributor Author

One more docs/status.md note from the docs steward (#290). The Next renumbering looks right: the GitHub App item is now 3 and Cloud tasks is 4.

The CF-2 entry is in In flight, between the v0.2.0 entry and the native conversation fidelity pointer, and it ends "Open in #286". Once this merges, that is landed work under In flight saying it's open. The pointer right below it says landed slices go under "On main, unreleased".

Suggestion: move the entry into "On main, unreleased" as a sub-bullet of the conversation-fidelity bullet, as #289, #294 and #301 did, and drop "Open in #286".

This won't conflict with #303. It changed the v0.2.0 entry above yours, but left its "[Candidate review]" line alone.

@BunsDev

BunsDev commented Oct 18, 2026

Copy link
Copy Markdown
Contributor Author

Status for the CF-2 owner and Val: #303 landed on main, so I used GitHub's Update branch on #286 (80d35df, a verified GitHub merge commit that brings in only #303's docs). On that head, unit + e2e (macOS) failed on a single unit test, job 117755082206: knowledge does not adopt failed, unready, unidentified, or UI-less servers, "Missing expected rejection". knowledge.ts is unchanged since #227. The same test has failed locally under load on CF-2's and CF-3's trees and passed when run alone, so I'm treating it as a pre-existing flake. Once the run's iPhone job finishes, I'll re-run only the failed job (nothing cancelled) and guard-merge #286 if it passes.

@BunsDev BunsDev self-assigned this Oct 18, 2026
@BunsDev

BunsDev commented Oct 18, 2026

Copy link
Copy Markdown
Contributor Author

Val has asked this reconciliation session to patch job 117755082206 and carry #286 through merge. I have reproduced the missing rejection specifically in the bare-URL Knowledge fixture: it serves readiness before its URL-emitting CLI exits, which takes the valid owned-server path. A separate repair branch cf2-ci-repair now gates fixture readiness on actual CLI termination and forces that ordering; all 16 Knowledge cases pass, and full build/unit verification is running. I will open a repair PR targeting this branch, integrate it, then guard-merge #286 on green checks. This also moves the CF-2 status entry under On main, unreleased as requested. The owner worktree is untouched.

@BunsDev

BunsDev commented Oct 18, 2026

Copy link
Copy Markdown
Contributor Author

Repair PR #305 now targets this branch at verified signed head 037120152bbad40dbf9390c45e9e9d4632c08c48. Build, typecheck and the full local unit command passed (1,726 pass, 16 existing skips). CI is running on #305. I am carrying #305 into #286, then checking the resulting head before the merge Val authorized; the old-head rerun does not include this repair.

@BunsDev
BunsDev merged commit 6db0818 into main Oct 18, 2026
15 of 16 checks passed
@BunsDev
BunsDev deleted the cf2-claude-lifecycle branch October 18, 2026 19:29
BunsDev added a commit that referenced this pull request Oct 18, 2026
Coven-Harness: Claude Code / claude-opus-5-5
BunsDev added a commit that referenced this pull request Oct 18, 2026
Coven-Harness: Claude Code / claude-opus-5-5
BunsDev added a commit that referenced this pull request Oct 18, 2026
Coven-Harness: Claude Code / claude-opus-5-5
BunsDev added a commit that referenced this pull request Oct 18, 2026
Coven-Harness: Claude Code / claude-opus-5-5
BunsDev added a commit that referenced this pull request Oct 18, 2026
Coven-Harness: Claude Code / claude-opus-5-5
BunsDev added a commit that referenced this pull request Oct 18, 2026
shared/human-requests.ts: approvals and native questions as one
versioned contract. A request is bound to thread, turn, attempt,
account, runtime and native request id plus a digest of the exact
action; it accepts one local answer, and an unknown id, a wrong
identity, a changed action, a withdrawn request or a second answer
authorises nothing. Stop withdraws pending requests; one whose answer
was being sent becomes uncertain, never resolved. Codex's experimental
item/tool/requestUserInput (codex-cli 0.163.0) maps to typed questions,
answers are validated against them, and secret answers are hidden from
anything shown, saved or exported. Not wired in yet: that consumes
#275's interfaces once #286 lands.

Refs #276

Coven-Harness: Claude Code / claude-opus-5-5
BunsDev added a commit that referenced this pull request Oct 18, 2026
Reconcile the squash of PR #286 without changing the tested fixture or runtime.
Record the passing live Claude Stop and resume run at 0371201.

Coven-Harness: Codex / gpt-6
BunsDev added a commit that referenced this pull request Oct 18, 2026
shared/human-requests.ts: approvals and native questions as one
versioned contract. A request is bound to thread, turn, attempt,
account, runtime and native request id plus a digest of the exact
action; it accepts one local answer, and an unknown id, a wrong
identity, a changed action, a withdrawn request or a second answer
authorises nothing. Stop withdraws pending requests; one whose answer
was being sent becomes uncertain, never resolved. Codex's experimental
item/tool/requestUserInput (codex-cli 0.163.0) maps to typed questions,
answers are validated against them, and secret answers are hidden from
anything shown, saved or exported. Not wired in yet: that consumes
#275's interfaces once #286 lands.

Refs #276

Coven-Harness: Claude Code / claude-opus-5-5
BunsDev added a commit that referenced this pull request Oct 18, 2026
shared/human-requests.ts: approvals and native questions as one
versioned contract. A request is bound to thread, turn, attempt,
account, runtime and native request id plus a digest of the exact
action; it accepts one local answer, and an unknown id, a wrong
identity, a changed action, a withdrawn request or a second answer
authorises nothing. Stop withdraws pending requests; one whose answer
was being sent becomes uncertain, never resolved. Codex's experimental
item/tool/requestUserInput (codex-cli 0.163.0) maps to typed questions,
answers are validated against them, and secret answers are hidden from
anything shown, saved or exported. Not wired in yet: that consumes
#275's interfaces once #286 lands.

Refs #276

Coven-Harness: Claude Code / claude-opus-5-5
BunsDev added a commit that referenced this pull request Oct 18, 2026
…ards (CF-3) (#293)

* Define the human-request contract and a pure broker with one resolution

shared/human-requests.ts: approvals and native questions as one
versioned contract. A request is bound to thread, turn, attempt,
account, runtime and native request id plus a digest of the exact
action; it accepts one local answer, and an unknown id, a wrong
identity, a changed action, a withdrawn request or a second answer
authorises nothing. Stop withdraws pending requests; one whose answer
was being sent becomes uncertain, never resolved. Codex's experimental
item/tool/requestUserInput (codex-cli 0.163.0) maps to typed questions,
answers are validated against them, and secret answers are hidden from
anything shown, saved or exported. Not wired in yet: that consumes
#275's interfaces once #286 lands.

Refs #276

Coven-Harness: Claude Code / claude-opus-5-5

* Answer each request once, route Codex questions to a card, and close stale cards

The runner keeps the turn's approvals and questions in the human-request broker. The first
answer is taken; every other one is refused with a reason (already resolved, withdrawn,
unknown, invalid) that thread:answer now returns and the phone receives as a 409.

Codex item/tool/requestUserInput (codex-cli 0.163.0, experimental) becomes a question card,
answered through thread:answer-question. Answers are checked against the questions before
anything reaches Codex. A secret answer goes to the CLI only and is saved as "(hidden)".
Stop sends an error reply, never an empty answer. When serverRequest/resolved arrives for a
request still open, Codex settled it, so the card closes as withdrawn and no answer is sent.
Approvals now carry the CLI's own request id (Codex JSON-RPC id, Claude request_id).

Cards a dead process left open close as ended when their thread loads, saved and emitted.
Phones see a question as a notice to answer on the Mac.

Part of #276.

Coven-Harness: Claude Code / claude-opus-5-5

* Show question cards with a control for each question

Options are radio buttons, with Other when the question takes free text. A free-text question
gets a text field, and a secret one a password field. A refused answer says why on the card.
An answered card lists the answers, with a secret shown as (hidden). A closed card says why,
in the approval card's words.

The turn header reads "Waiting for your answer" while a question waits. An answered or
closed question folds into the turn's work; an open one never does.

The e2e drives a fixture codex-cli 0.163.0 app-server through the real UI. It covers an
incomplete answer refused in place, one reply carrying the answers, a refused second answer,
Stop giving an error reply, and no secret anywhere under MODEX_HOME/app.

Part of #276.

Coven-Harness: Claude Code / claude-opus-5-5

* Document the human-request contract and record CF-3 in status

docs/human-requests.md covers the broker's exported signatures, its states and refusals, the
runner's binding and withdrawal rules, the Codex 0.163.0 and Claude 2.1.302 mappings, the
clients, how secrets are kept, the tests, and what is left for CF-6 and CF-7.

Part of #276.

Coven-Harness: Claude Code / claude-opus-5-5

* Keep a new tab's late focus from taking the caret away

Opening a tab (⌘⇧B) focuses its address bar twice: once when the tab becomes active, and again
in an animation frame that newTab queues for when the panel was not yet visible. Chromium can
hold frames back for an inactive or busy window. The late focus then landed after the person,
or a test, had already moved on, and took the caret out of the composer mid-typing.

That failed CI on #293: store-desktop.spec.ts's browser-tools recovery case filled the composer
just as the late frame ran. The text went into the address bar and the composer stayed empty
(job 117782462586). The same case failed on #294's first run.

The late focus now applies only if focus has not moved since the tab opened. The new
workspace.spec.ts case holds animation frames, moves to the composer, releases the frames and
types. It failed first: the composer lost focus when the frame ran.

Coven-Harness: Claude Code / claude-opus-5-5
BunsDev added a commit that referenced this pull request Oct 19, 2026
CF-2 (#286), CF-3 (#293), CF-4's Markdown slice (#283), CF-6's request half (#308) and CF-7's
clarifications (#312) are on main, with their merge commits. The candidate note says the fixture
rows now run on main, and no candidate is pinned while the live-provider and human gates stay open.

Refs #281

Coven-Harness: Claude Code / claude-opus-5-5
BunsDev added a commit that referenced this pull request Oct 19, 2026
…nts at once, reconnect, a clarifying question (CF-8) (#313)

* Add the request variants to the fidelity path

Four cases in e2e/conversation-fidelity.spec.ts, through the real app:

- A native withdrawal: the Claude fixture takes its own request back (control_cancel_request).
  The card closes as withdrawn, no answer reaches the CLI, a late answer is refused, and the file
  is unchanged.
- The desktop and the phone answer at once, over the bridge and the companion's HTTPS. Exactly one
  is accepted, the CLI reads one decision, the card names who gave it, and the file matches the
  winner.
- With the journal on, a phone that loaded the open card reconnects after the desktop's decision.
  Its delta shows the card answered by the desktop and sent, its late answer is a 409, and every
  count stays at one.
- A clarifying question inside the path, from a codex-cli 0.163.0-shaped fixture: answered on its
  card, heard once, followed by a real edit and check, saved to Notes, and inert after a restart
  that re-runs nothing.

The fixture now counts every decision the CLI reads, so the earlier cases assert that too. The
golden path is marked slow: since #301 it also takes 18 reference captures and relaunches, and it
ran past 90 s on a Mac at load average 100.

These cases found the companion's 404 for threads started on the Mac, fixed in the CF-6 branch.

Refs #281

Coven-Harness: Claude Code / claude-opus-5-5

* Record the request variants in the acceptance receipt

The matrix rows for a clarifying question, a native withdrawal, simultaneous answers and a
reconnect after a decision move from open to verified with a fixture, with what each case asserts.
The defects list adds the companion's 404 for threads started on the Mac. The fidelity summary gains
rows for questions and approvals.

Refs #281

Coven-Harness: Claude Code / claude-opus-5-5

* Record CF-8's request variants in status

Refs #281

Coven-Harness: Claude Code / claude-opus-5-5

* Record the landed children in the acceptance receipt

CF-2 (#286), CF-3 (#293), CF-4's Markdown slice (#283), CF-6's request half (#308) and CF-7's
clarifications (#312) are on main, with their merge commits. The candidate note says the fixture
rows now run on main, and no candidate is pinned while the live-provider and human gates stay open.

Refs #281

Coven-Harness: Claude Code / claude-opus-5-5

This branch was successfully deployed

1 active deployment
Preview — 80d35df3 Deployed Oct 18, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[P1][CF-2] Harden Claude Stop, late-message draining, and cancelled-request cleanup on claude -p

1 participant