feat(codex): publish observed harness state from the control pump - #321
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 2af6993885
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
2af6993 to
495d84f
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 495d84ffa5
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
495d84f to
1b36b3b
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 1b36b3b0cb
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
1b36b3b to
2578472
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 2578472b6d
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
2578472 to
5bf821f
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 5bf821f2b4
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
5bf821f to
6312ff8
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 6312ff82b4
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
6312ff8 to
32c8169
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 32c81699c6
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
32c8169 to
3cae2af
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 3cae2afdeb
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
3cae2af to
62ba0c8
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 62ba0c82b4
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| let stream = UnixStream::connect(&socket_path).unwrap(); | ||
| let stopper = std::thread::spawn(|| { | ||
| std::thread::sleep(Duration::from_millis(300)); | ||
| crate::provider_session::STOP.store(true, std::sync::atomic::Ordering::SeqCst); |
There was a problem hiding this comment.
Isolate the global stop flag in the handshake test
When the test suite runs with Rust's default parallel execution, this test sets the process-global provider_session::STOP for all tests for roughly one polling interval. Concurrent app-server tests also call initialize_control, which reads this flag and can return None; those tests then fail at their .expect("no stop raised in tests") calls. Serialize every test that accesses this flag or inject a test-local stop flag so this test cannot poison unrelated handshakes.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Fixed in c1c56c9 on schickling/hs-03-codex: flag-touching tests are serialized -- a shared test mutex orders every initialize_control reader against the handshake test that raises the process-global STOP, so parallel readers can no longer observe the raised flag and fail their no stop raised in tests expectations.
Posted on behalf of @schickling
| field | value |
|---|---|
agent_identity |
unknown |
agent_persona |
generalist |
agent_supervisor |
unavailable |
agent_tool |
OMP |
agent_tool_version |
18.0.3 |
agent_runtime |
OMP 18.0.3 |
tooling_profile |
dotfiles@f33cd9c-dirty |
There was a problem hiding this comment.
Fixed in c1c56c9 on schickling/hs-03-codex: flag-touching tests are serialized -- a shared test mutex orders every initialize_control reader against the handshake test that raises the process-global STOP, so parallel readers can no longer observe the raised flag and fail their no stop raised in tests expectations.
Posted on behalf of @schickling
| field | value |
|---|---|
agent_identity |
unknown |
agent_persona |
generalist |
agent_supervisor |
unavailable |
agent_tool |
OMP |
agent_tool_version |
18.0.3 |
agent_runtime |
OMP 18.0.3 |
tooling_profile |
dotfiles@f33cd9c-dirty |
The delivery pump's watcher narrows to its two genuine inputs (inbox + status) so runtime records written into the agent dir - presence temp siblings, harness-state transitions - can never wake it. The pump projects CodexObservedState into the generic record on every persisted change: Held never leaks (human-blocking holds read active+blockedOn, positively-active holds read active, unprovable holds withhold), the heartbeat rides the existing presence cadence and stops on evidence loss, and the wrapper publishes a terminal record once the pump is gone. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…uity marking Review-pass fixes: the wrapper installs the shared stop handler and the monitor observes it, ending the session through the ordinary terminal-write path (previously st2's own stop SIGTERMed the wrapper dead before the post-join write); the terminal record's exit carries the actually-observed ExitStatus on every arm, error text stays diagnostic in reason; evidence loss interrupts the writer so a state restated across an unproven interval opens a fresh transition; and ptySession records the wrapper's runtime ID, which only aliases the identity on driver-expanded seats. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…e-readable ask kinds SIGTERM is honored through connect/initialize/binding (graceful, before the TUI exists no record is left behind), the restarted pump opens a fresh transition instead of claiming continuity across its own gap, and the human-blocking holds name their ask kind (review/permission/question). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…nation token A transition whose write fails no longer counts as evidence: it is retained as pending and retried on the next pump pass before any heartbeat may re-stamp the contradicted on-disk state. Both codex writers adopt the runtime incarnation as their session token, so the terminal record owns exactly this session's records. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…etries The signal handler installs at the top of run_controlled_owned — before the hook-trust preflight spawns its detached app-server — so a SIGTERM in that window sets the polled stop flag instead of leaking a server and a stale socket (and the flag is never reset mid-launch by a second install). A pending observation retries on every pump pass; only the heartbeat stays presence-cadence gated. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…round the handshake Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… ends honestly A claim that cannot be written degrades to a token-only writer with a warning instead of erroring the pump (which killed the TUI through the binding wait); a TUI spawn failure writes a real terminal record — ended, exit unknown, reason launch-error — over the claim placeholder that would otherwise misread as a takeover. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…poll the stop flag
Held{Review} projects plain activity — its enter and exit are
model-emitted items inside a running turn, so blockedOn stays none and
review becomes a reserved ask word no producer emits (the delivery hold
is untouched). The initialize handshake wait and the hook-trust
projection read now poll the stop flag between short socket timeouts,
with the startup timeout restored for later control reads.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…tion Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A short read timeout parks the handshake on its resumable Interrupted state each poll interval, so a stop raised while the app-server never answers the Upgrade request ends startup gracefully instead of blocking the launch for the whole startup timeout. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… stop flag An early failure return would otherwise reset first and let the stopper re-poison the global flag for the rest of the single-threaded test process. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The handshake test flips the process-global STOP flag for a poll interval; initialize_control tests running in parallel read that same global and fail their no-stop expectations at random. A shared test lock now orders every flag reader against its writer. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> agent-identity: unknown agent-persona: generalist agent-supervisor: unavailable agent-tool: OMP agent-tool-version: 18.0.3 agent-runtime: OMP 18.0.3 tooling-profile: dotfiles@f33cd9c-dirty
62ba0c8 to
c1c56c9
Compare
The Codex producer (#268 §A prerequisite + §B), on top of the envelope.
watch::watch_delivery_inputsforwards mutations only for theresources/inboxsubtree and thestatusfile; the delivery pump switches to it. This also removes the pre-existing self-wake where the pump's own 5-minute presence refresh woke it through the unfiltered agent-dir watch. Regression test proves harness-state/tmp/stream writes stay silent while inbox and status writes wake.CodexObservedState::harness_observation()—Heldnever enters the published vocabulary;ActiveWithoutTurn/ConflictingTurnproject toactive(the two rows Observed harness state: the activity-lease slice of #162, specified and made readable #268 §B corrects);Review/WaitingOnApproval/WaitingOnUserInput→active+blockedOn: human;AwaitingStatus/NotLoaded/SystemErrorwithhold (no write, heartbeat suppressed — the record ages out). The projection test is behavioral: an all-indeterminate mapping fails it.Notably this needs no new Codex read path: the pump projects its own in-memory state, sidestepping the fact that
control-state.jsonhas no production reader.Cycle-2: the stop flag is honored through every startup phase (graceful, recordless before the TUI exists); the restarted pump opens a fresh transition on takeover; the human-blocking holds name their ask kind (review/permission/question).
Cycle-3: a transition whose record write fails no longer counts as evidence — it is retained as pending and retried before any heartbeat; both codex writers adopt the runtime incarnation as their session token.
Cycle-4: the stop handler installs before ANY spawn (hook-trust preflight included, and exactly once — reinstalling would reset a raised flag); pending observations retry on every pump pass with only the heartbeat cadence-gated.
Cycle-7:
Held{Review}projects plain activity — review's enter/exit are model-emitted items inside a running turn, soblockedOnstaysnoneandreviewis a reserved ask word (the delivery hold is untouched); the preflight's initialize handshake and projection reads poll the stop flag between short socket timeouts.Cycle-8: the stop flag reaches a silent WebSocket handshake — the handshake runs under the short poll timeout and resumes through tungstenite's interrupted state, checking stop on every resume, so a never-answering app-server no longer holds the launch for the whole startup timeout.
Part of the #268 stack (#319→#326).
🤖 Generated with Claude Code
Refinement pass: the wrapper now installs the shared stop handler and the monitor observes it, ending the session through the ordinary terminal-write path — previously st2's own stop SIGTERMed the wrapper dead before the post-join write, leaving a stopped seat reading its last live state until the staleness horizon; the terminal record's
exitcarries the actually-observed ExitStatus on every arm (error text stays diagnostic inreason); evidence loss interrupts the writer so a state restated across an unproven interval opens a fresh transition; andptySessionrecords the wrapper's runtime ID.