0207. Work records and native agent delivery
Status: accepted (James, 2026-10-01). Amends
ADR 0180,
ADR 0187, and
ADR 0203. Amended by
ADR 0213: hire_agent is the only way Clankie
starts a worker, and the embedded Swarm has been removed.
Context
Clankie can dispatch his own workers without requiring a second task board or independent peer coordination. The repo's tracker or files already describe the work. Herdr provides the native interactive agents the owner can watch and use. Typing automated messages into those terminals competes with the owner's draft and can land in a question or approval prompt. A paste-aware terminal command does not remove that race.
Decision
Keep three responsibilities separate:
- Work, ownership, acceptance, decisions, and results stay in the repo's existing tracker or files, as defined by ADR 0191. Clankie does not mirror those records into a second task lifecycle for local hires.
- Herdr contains the real interactive harnesses. The owner can observe them and type into them directly.
- Clankie delivers briefs and messages through a supported harness channel or session API. The connection must identify the same session displayed in Herdr.
Normal agent messages never fall back to terminal typing. Missing consent, unsupported control, or a disconnected channel produces an explicit delivery outcome. Existing mailbox and session receipts distinguish confirmed delivery from an uncertain attempt. An uncertain attempt must be reconciled before a retry; it is not permission to type the message or launch another worker. Approvals and questions remain owner decisions. Explicit owner terminal control remains available.
Informational questions from a hired worker may reach its exact hiring conversation for a lead reply through the same native control channel. This does not delegate permission prompts, folder trust, or privileged approvals. Codex 0.160 sync replies require the winning tool-output receipt; async replies use the native TUI's attributed user message and require its exact client ID, content, and returned turn. The async protocol has no atomic first-answer arbitration against a simultaneous owner reply. An uncertain answer never authorizes a replacement send. Native idle releases dispatch only after the exact active turn is observed terminal, preserving newer turns and pending async questions from normally completed turns. See the VUH-1688 protocol evidence.
Independent linked agents reach Clankie through message_clankie and the
per-fleet link. Remote hires use their native harness channels or session APIs.
Coordinator state remains on disk for inspection; it is no longer a connection.
Consequences
Unsupported harnesses can remain useful as owner-operated terminals without claiming to support automated messaging. A headless RPC interface alone does not prove control of an existing interactive session. New harness support must verify that boundary before advertising it.
Delivery acknowledgment is not task completion. A channel may deliver at its next turn boundary; a session API may steer an active turn. Results must describe the actual mechanism. An unavailable connection is not a promise of automatic retry, and a saved session ID alone does not prove restart reattachment. Reuse existing mailboxes and session state; add recovery machinery only for demonstrated gaps.
MCP reconnect and native call receipts (VUH-1638)
Overlapping operator MCP calls share an HTTP client. Resetting that client after
one logical error can close another call that already reached native dispatch,
losing its result while its effect continues. A reconnect must therefore drain
the old client generation without closing its pending calls. Only an explicit
unknown_session rejection before tool admission permits one retry in a new
session. Network failure or a missing result never permits mutation replay.
The bridge assigns deliveryId for message_seat and hireId for hire_agent
before protected dispatch and carries them in MCP _meta["clankie/seat-call"].
The service persists the exact scoped receipt before the native effect, binding
the request by fingerprint without storing its request text. Lost
results return typed uncertainty with the original ID, rather than hiding it in
a transport exception or launching a replacement. Reconnect does not change that
pending action's identity or replay it.
These IDs identify the operator dispatch; native delivery receipts retain their own identities. A settled call receipt preserves the original tool result, including any native uncertainty, rather than asserting task completion.
reconcile_seat_call({deliveryId}) or reconcile_seat_call({hireId}) reads the
original receipt in its owning operator conversation. It sends no message,
starts no hire and grants no new authority. This operator receipt scope is
separate from worker inbound and fleet peer-message ledgers. The journal keeps
the latest 1,000 settled result bodies; older IDs remain non-replayable and report
an expired result, while uncertain originals remain retained. ID, scope and
fingerprint tombstones survive result-body expiry and service restart. Reconciliation
reports what the original receipt proves, preserving the distinction between
native delivery, uncertainty and completed work.
Amendment (VUH-2358, 2026-10-17). A lost acknowledgment had refused every later lead message to its recipient, with only an owner settle to recover. Two settle paths now exist; neither sends anything or resends the original:
- The lead settles by ID with
reconcile_seat_call({deliveryId, settle: "not_delivered"})in the owning conversation. An exact acknowledgment, or the original in the recipient's session, is still recorded as delivered. - Unattended, a native fence stops blocking a different message after five minutes. This applies only when the recipient is idle and its exact original session was read and does not show the original.
Both keep the original as abandoned-unknown evidence. If it does arrive late,
the recipient may see both messages; that is accepted over losing lead control
of the seat.
An uncertain delivery fences only itself (VUH-1779)
"Never duplicate an uncertain dispatch" protects that original, not the recipient. A seat mailbox refuses a delivery whose original ID or exact content matches an unresolved receipt, and returns that original's ID. Other wakes, watches, reports and messages deliver normally. Blocking them all turned one unacknowledged event around a seat reset (2026-10-06) into a silent outage of every later wake and worker report. An unreadable journal still refuses everything, because it cannot tell an original from a new message.
The seat receives one notice per unresolved receipt, and clankie doctor and
clankie seat-delivery list show each with its age. A notice the seat
acknowledged is remembered across service restarts, so a receipt is announced
once, not after every deploy (VUH-2196); a notice that never reached the seat
may be tried once by the next process. A lead that answers a lead message with
message_seat({inReplyTo: <event_id>}) acknowledges that exact original: only
its own conversation received that ID. When the event never
arrived, or its fate cannot be known, the owner settles it with clankie seat-delivery settle ID abandoned-unknown. Like hire-receipt settle … abandoned-unknown, this retains the original and its identity, claims no
receipt, and never resends it; the original ID stays refused afterwards.
Private Windows Codex control (VUH-1563)
Windows control must preserve a pane's private environment. Joining the account's
shared daemon can disconnect the worker MCP bridge from its pane. Existing
elevated-shell --no-daemon sessions use an embedded server with no external
control endpoint. Steering requires a proven native endpoint. Queueing may use
the existing SSH CLI only when a fresh kernel probe proves that the pane's home
matches the SSH account's canonical default ~/.codex, and the SSH CLI inherits
that same default home. Private or unproved homes refuse this fallback.
The adapter supports an existing dedicated app-server and the original
interactive TUI in the same pane's foreground process tree. The server must
inherit the launch's environment, CODEX_HOME, cwd and configuration, bind only
to loopback, and expose its OS-assigned endpoint to the TUI's --remote argument.
Codex rejects --no-daemon together with --remote; neither this adapter nor its
protocol tests implement or prove the native launch supervisor that would select
that launch shape. New Windows launch supervision and live Windows acceptance
remain open verification gaps.
The native app-server cannot select a CLI named profile (-p/--profile),
whose separate configuration file may carry different MCP and model settings.
Those launches retain the native embedded --no-daemon path. Pane-targeted
steering refuses until an endpoint and the same private home can be proven;
queueing retains the default-home CLI path described above. The pane identity
and home proof are repeated after preparation and caller authority checks, just
before CLI dispatch. A private native receipt never falls through to CLI queueing.
A future launcher must forward only configuration flags accepted by both native commands and
preserve the TUI's prompt, model, approval, sandbox and resume arguments.
Before opening the fleet's SSH transport, Clankie independently proves the current Herdr pane/session, installed native TUI and backend executables, full process creation order, common foreground ancestry, private-home consistency and the backend's actual loopback listener. A banner, title, saved session or endpoint file confers no authority. A dedicated native SSH loopback forward carries protocol bytes; a metadata-only host probe identifies its nearest live SSHD ancestor and sole established TCP tuple. After connecting, the kernel TCP table must prove the exact established connection terminates in that same backend lifetime. The complete pane proof and current caller authority are checked again before the native write. MCP children may join the same pane through the proven backend ancestry; existing project grants and per-call admission still apply.
Reuse the existing read-only thread/read and latest-turn inspection, followed
by turn/steer with the exact expectedTurnId. Never resume an unrelated thread
or answer a native approval. A confirmed receipt reports steered; native queue
acceptance reports queued. Private-backend queues reuse native thread/queue/add
with its required experimental protocol negotiation on the same proven connection,
so a private home never falls through to the account-default queue. Busy turns
hold the submission until they settle; idle turns start it through that backend.
Explicit Queue bypasses steering, and an impossible
explicit Steer is a reasoned pre-send rejection under the existing delivery-mode
contract. Once a mutation may have been written, a lost response remains
unconfirmed and cannot authorize a queue, fallback model turn or second send.
Exact Claude worker acknowledgment (VUH-1870, 2026-10-09)
Lead implementation decision under James’s assignment: worker channels require
an explicit authenticated ACK of the original event ID and native recipient
binding; a later poll never acknowledges a different event. The bridge checks
the response’s exact event ID. Native control retains that channel ID and
conversation in its fence and trusts the retained ACK without waiting for a
remote transcript. Transport delivery is reported as delivered, not model
consumption.
The operator list includes worker mailboxes and native fences after restart, even when the mailbox ACK already resolved. Reconciliation accepts the native ID or the original scoped MCP call ID, never dispatches, and checks original content, recipient binding and native occupant. Legacy fences without a retained channel ID remain visible by their internal receipt ID for owner settlement. A matching body hash cannot establish which dispatch was acknowledged, so it never substitutes for the missing ID. Authenticated owner abandonment preserves unknown evidence and frees an ordinary recipient for new intent; it never retries the original.
Hand-started Claude startup fix (2026-10-09)
Under James's VUH-1867 assignment, the implementing lead chose the explicit
startup fix permitted by the acceptance criteria. Claude's documented channel
contract requires session opt-in with --channels; installing a plugin or
allowing it by organization policy does not select it for a session. A plugin
SessionStart hook cannot add flags to the already launched CLI.
The worker's synchronous SessionStart hook therefore returns a user-visible
systemMessage with one exact-session restart command when the matching linked
Herdr pane has no worker channel opt-in. It recognizes the native CLI through
an intermediary shell on macOS/Linux and Windows. Normal and explicit
interactive development-plugin opt-ins suppress the warning; unrelated
plugins do not. Missing or ambiguous links, missing panes, Codex, print mode,
other hooks and unsafe session IDs do not produce that restart command. If
launch observation fails, the message says it cannot confirm the channel.
The notice survives a service reporting failure. It does not alter shell
configuration, choose accounts, restart a pane or grant permission authority.
The official channel controls and hook output contract support this decision. VUH-1867 verification records the native Mac startup display and focused subprocess boundaries.
Seat input choices follow current native support (2026-10-09)
James assigned VUH-1882's service half to expose actual input choices and leave
composer and Send-to-Stop work to the app worker. The implementing lead chose
one optional roster inputCapabilities object with deliveryModes, interrupt
and nextTurnOnly, derived from the attached live control and exact-session
receiver observations. The same support check rejects explicit delivery modes
before dispatch or receipt substitution. A next-turn-only Claude can Queue but
cannot Steer; a stored receipt cannot turn a later Steer into a silent hold.
No mode means plain automatic Send, not an invented harness capability.
Codex's queue remains a separate native route for owned and unowned sessions;
steer requires its available controller. OpenCode offers its declared Queue;
Pi, Grok and mailbox-only seats offer no explicit delivery mode. Stop is
advertised only for a live control implementing the authenticated exact-task
stop_task route, not a generic interrupt or terminal key. Current capabilities
do not grant access and cannot override occupant, owner or machine checks.
Amendment (VUH-2680, 2026-10-23): Claude's exact-task Stop is one guarded
Esc. Interactive Claude has no native interrupt API, so a Claude-driven
conversation, Clankie's own lead included, could not be stopped from the app.
The lead approved one exception to "no terminal key": a controlled Claude
seat's stop_task presses a single Esc in its own pane, only while Herdr shows
that exact pane, terminal and session working and its input line reads
provably empty. Owner authority and the binding are checked again just before
the key; the key is never repeated, and stopped requires Claude seen idle
afterwards, otherwise uncertain. Such a seat now advertises interrupt.
Details and the remaining limits are in
ADR 0187's amendment.
The protocol contract documents the fields, older-service fallback, live changes and app usage.
Amendment (VUH-2410, 2026-10-17): read reports age out. A conversation's
metadata retained every worker report it ever accepted, and every metadata save
rewrote all of them. The default conversation reached 4,840 reports (15 MB per
save), which made it the service's main external-memory garbage and drove full
collections of the whole heap (VUH-2379). A report is now kept until its lead
reads it, and then for 24 hours after readAt (or acceptedAt on records
without one). Unread, pending, delivered and uncertain reports are never
dropped. After that window, targeted reads omit the ID and exact-ID status
reads as unknown or uncertain (from the admission fence), never not_sent. Worker retries happen
within the admission deadline, far inside the window, so acceptance dedupe is
unchanged in practice. Metadata is now written as compact JSON.
Amendment (VUH-2779, 2026-10-24): delivery to linked-machine seats. Two rules assumed every check was local and every receipt prompt.
- Claude mid-turn arrivals are queued, not uncertain. Claude writes a channel
event that arrives during a turn to its transcript only when it absorbs it at
the next tool boundary. On the PC, all four uncertain lead → Claude receipts
were acknowledged by the bridge in under a second and recorded 45–77 s later,
past the 15 s receipt window. A follow-up with an exact bridge ACK, to a seat
still working or blocked, whose session has already taken a channel event (in
its transcript, or confirmed by this service), now reports
delivered/queuedwith a detail, which leads read asqueued_until_turn_end. A seat that is idle, or a session with no proven channel receiver (VUH-2499's startup loss), still needs the exact transcript entry. A failed transcript read is a poll gap, and one that fails before dispatch is a definite not-sent. - The final authority bound covers an in-memory recheck. VUH-1563 bounded the caller's authority check to 250 ms immediately before a native Codex write, so late approval cannot authorize it. A peer check reads the PC several times (sender, recipient and process proof, each an SSH round trip), so every peer message to a PC Codex seat timed out, and a lead's settings read occasionally did too under load. The rule now covers proof age, not check duration: every final proof is stamped when it completes, and the write must follow the oldest within 250 ms, a clock never restarted by waiting on a slower proof. The full check still runs before the final native observation. A check may split in two: its remote part (for a peer, the sender's fresh process proof and the owner's peer switch) is read again alongside the final observation, and its synchronous recheck (link liveness, fleet revision, settings generation) runs at the gate. When the two final proofs finish too far apart, both are read again with the faster one started later by the measured gap, up to three times, and otherwise nothing is sent. A check without the split runs in full under the same deadline. A wrapper that adds its own check is a new function, so it drops the split and falls back to the full check. The lead approved this split over a longer remote bound; three review rounds narrowed it to this one freshness rule (VUH-2779).
- Deliveries to one seat take turns. The live acceptance found the lead's
probes and a peer's colliding on the recipient's receipt fence. One refusal
was recorded
uncertainthough nothing was sent, which held every later peer message to that seat for the 15-minute supersede window (VUH-2100). A message to a seat with a delivery in flight now waits for it, bounded at 25 seconds, then takes the ordinary checks. A message that still loses the seat at its dispatch recheck is a definite not-sent. - Peer sends to a linked machine fit their request budget. The live re-run
then showed the worker bridge abandoning peer sends to the PC Codex seat at
20 seconds while the service's remote proofs were still running (13–18 s
after the attempt for those that delivered). The seat reported uncertain;
the service's final check then found the sender's stream closed and refused.
The bridge now allows 50 seconds, under Codex's 60-second MCP tool limit.
After any settled original, a different message to that seat goes out in
the same call, and the result carries the original's outcome as
previous; only the same text never resends.
