Repository navigation
feat(agents): seats tell their Captain how they ended - #150
bryantderosier merged 9 commits into
Conversation
cbc86d4 to
730d270
Compare
faf4b42 to
233e3e4
Compare
233e3e4 to
902b6d1
Compare
902b6d1 to
51753ad
Compare
51753ad to
f71657d
Compare
Jacksondr5
left a comment
There was a problem hiding this comment.
Settlement semantics check out: only completed and failed count as finishes, interrupted and cancelled are excluded and tested, owed replies are read from open Exchanges, and a notice arriving mid-turn folds into the queued one. The hook is a J5-owned subscriber on the stored-event stream, the same shape as the lifecycle and silence services, so no upstream seam.
On t3_thread_wait: the removal is clean. Upstream's toolkit is untouched, the J5 surface drops it in a J5-owned file, absence is asserted three ways (the FORK case 4 proof file, the surface test, the registration test), and no remaining instruction or doc text tells agents to wait. On delegate_task's wait mode: a child that messages its parent ends its own turn, so the wait returns and the parent reads the message next turn; latency, not starvation. Worth one sentence in agent-tools.md so nobody re-derives it.
Four notes on the settler, inline, none blocking on their own; the seat-name keying matters more now that one Captain runs several Crews.
Reviewed by Claude Fable 5.1 in Claude Code, with an independent pass by GPT-6-Astra in Codex.
f71657d to
17dbccc
Compare
|
Four for four in 17dbccc90: notices keyed by participant, a handoff digest in the facts, the opening seat tag escaped, and the notice committing before the seat settles. The delegate_task wait sentence is in agent-tools.md. |
17dbccc to
ee98bea
Compare
|
You were right about settle. Upstream settles by hand, on a merged PR, or after idle days, and we were settling a seat the moment it finished, so a Crew read "2 settled" seconds after launch. In ee98beae4 the platform settles nothing: the notice to the Captain stays exactly as it was, the thread.settle dispatch is gone, and the machinery is renamed to what it does (CrewSeatFinishNotifier, <j5_seat_finished>). The state summary still counts a seat you settle yourself. |
67bf081 to
84317da
Compare
Jacksondr5
left a comment
There was a problem hiding this comment.
Round five. All five claims verified: the finish notice leads with the outcome and carries the run's recorded error; the notifier stays quiet about a launch failure the report covers, before watch, after the report posts, and after restart, proven with the real notifier; baseline and queued digest require system provenance; the silence detector suppresses only Captain-bound seat errors; later failures reuse the deduplicated alert by run receipt.
One shared defect, filed on #148 where the formatter lives: provider error text is interpolated raw into the line-oriented notice (runFailures.ts), so a multi-line provider message can inject field lines the web parser trusts, and ordinary multi-line diagnostics lose everything after the first line. The finish notice here uses the same formatter, so the writer-side encode fixes both.
Reviewed by Claude Fable 5.1 in Claude Code, with an independent pass by GPT-6-Astra in Codex.
84317da to
16b812c
Compare
|
The shared provider-diagnostic finding is fixed in this head and #148. Finish notices encode diagnostics before interpolation, then decode after field extraction. Regression tests preserve multiline diagnostics and prevent injected participant/thread fields from changing the card destination. |
16b812c to
e07ee5e
Compare
Jacksondr5
left a comment
There was a problem hiding this comment.
Round six. The finish notice now encodes provider diagnostics before interpolation and decodes after field extraction, with the multiline, injected-field, and closing-tag tests in place. Verified by two independent passes.
One gap shared with #148 and filed there on the writer: the escape set covers ampersand, angle brackets, CR, and LF but not U+2028 and U+2029, and the multiline regex that extracts fields treats those as line boundaries. Both reviewers reproduced a spoofed participant and thread from a diagnostic containing the line separator, and on this side the dedupe baseline keys by the parsed participant, so a real seat's earlier notice is not found. One character class at the writer, two code points at the decoder.
Reviewed by Claude Fable 5.1 in Claude Code, with an independent pass by GPT-6-Astra in Codex.
e07ee5e to
95dfed1
Compare
|
The shared escape gap is fixed at the writer in #148 (2e2ba0894) and in the decoder both parsers use; the finish notifier test now carries a U+2028 in the diagnostic and asserts the encoded field. This head is 95dfed148. |
When a Crew member's run completes or fails and it owes no open reply, the platform settles its thread and posts one platform-composed notice into the Captain's thread: the seat, how the run ended, and whether its declared handoff was written, missing, or never declared, with the handoff inline when short. A notice posts on first finish and again only when those facts change, and a notice arriving while the Captain's turn is running folds into the seat notice already queued behind it, so the Captain reads a digest in one turn. Interrupted and cancelled runs are not finishes. t3_thread_wait leaves the J5 surface: a turn that blocks on another thread cannot receive the notices that queue behind it, and a Captain that waited on a seat starved itself of its own Crew's news. The Fleet page and the sidebar expander summarize seat state from the same facts. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
A seat's <j5_seat_settled> notice reached the Captain's thread as raw tagged text, one block per seat when several folded into one message. It now renders through the same J5 user-row seam as the gate decisions: one card per message, one row per seat with its measured run end and where its handoff stands, the handoff opening in the artifacts panel and readable inline when the notice carried it. The notice names the seat's Crew so a Captain of several Crews can tell them apart. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…their own tag, and commit before settling One Captain's several Crews reuse seat names, so the "unchanged" baseline is now the seat's participant, not its name. A written handoff always carries its size and digest in the notice facts, so a re-briefed seat that rewrites a body too long to inline still reads as changed and the Captain hears of it. The opening seat tag is escaped in quoted handoff bodies like the closing body tag, so a Critic quoting a notice cannot become a phantom seat on the card or break the dedupe compare. The notice commits before the seat settles: a failed notice leaves the seat unsettled and logged so the next pass retries it. The tools doc says in one sentence why delegate_task's wait mode is safe where t3_thread_wait was not. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ed by the platform Jackson's review of the stack, round two: upstream settles a thread by hand, on a merged PR, or after idle days, and a seat marked settled seconds after launch read as wrong. The settler becomes the seat finish notifier: the notice to the Captain stays as it was, the thread.settle dispatch is gone, and the notice tag, service, session, and web parser are renamed to what the action is. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…s the run's error, said once A seat that died on its first turn was reported as a finish: block and card named "finished", the failure one line among handoff lines about a missing artifact, and no reason. The reason existed only in the silence detector's "errored" notice, which fires only when the Captain had an open ask to that seat, arrives later, and describes a messaging failure (Jackson's dogfood, 2026-09-17). The notice now opens with the run's status and, for a failed run, the recorded error; the card is titled by what ended how and shows the status first. A failed first turn that the launch report is about to carry is that report's to tell and is not also a finish; the notifier's stream feeds the reporter and runs its sweep first. A failed run is reported whatever the seat still owes, and the silence detector no longer posts an errored notice about a seat to the Captain that commands it, so one death is one telling. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
95dfed1 to
4a4074d
Compare
Crew seats report completed and failed runs to their Captain, including the state of a declared handoff.
When a member's run completes or fails and it owes no open reply, the platform posts one platform-composed notice into the Captain's thread (it settles nothing; upstream's settle rules apply to a seat as to any thread): the seat, how the run ended, and whether its declared handoff was written, missing, or never declared, with the handoff inline when short. A notice posts on first finish and again only when those facts change, and a notice arriving while the Captain's turn is running folds into the seat notice already queued behind it, so the Captain reads a digest in one turn rather than one turn per seat. Interrupted and cancelled runs are not finishes. Each notice names the seat's Crew, since one Captain may run several, and renders in the Captain's thread as a card: one row per seat with how the run ended and where its handoff stands, the handoff opening in the artifacts panel and readable inline when short.
Notice delivery is best effort: a failed passive notice is reconsidered on a later seat finish or server restart; an otherwise idle server does not promise an immediate retry. Sign-in and permission failures also create a durable human-inbox request, deduplicated by run and shared with the launch reporter. A written artifact is evidence of delivery, not a judgment that the handoff is correct or accepted.
t3_thread_waitleaves the J5 surface. Platform notices queue behind a running turn, so a turn that blocks on another thread can never receive the notice it is waiting for; a Captain on my dev server did exactly that and starved itself of ten queued notices. FORK.md case 2 now says the verb is deliberately absent.Built with Claude Fable 5.1 in Claude Code.
Evidence
Finish notices as the Captain sees them: one seat alone and three folded into one card, each with its status, handoff type, artifact path, and the handoff behind a disclosure.
🤖 Generated with Claude Code
Review follow-ups implemented and verified with Codex in the Codex app.
Closes #206.