fleetd #788: fleet_inbox, so a pane can collect its own mail #789
Closed
agent
wants to merge 0 commits from
worker/788-eb7dd0-1 into main
pull from: worker/788-eb7dd0-1
merge into: fleet:main
fleet:main
fleet:worker/799-a76336-9
fleet:worker/796-a7911a-5
fleet:worker/778-e301b0-1
fleet:worker/791-fleet-plugin-0-3-0-9e25b0-2
fleet:worker/790-observer-to-lead-send-523d45-1
fleet:worker/782-styling-probe-4ceb10-9
fleet:worker/778-12988a-4
fleet:worker/782-18f8bc-5
fleet:worker/780-faf58b-3
fleet:worker/759-authz-comment-and-role-list-e3f0e5-5
fleet:worker/756-758-observer-pane-discovery-7e6ffd-1
fleet:worker/759-role-model-comments-5d8409-3
fleet:worker/743-pane-discovery-ad5b75-5
fleet:worker/743-observer-send-4706db-6
fleet:worker/749-edge-baseline-28d1a0-3
fleet:worker/748-dead-comment-refs-f42ac5-4
fleet:worker/737-9c61d3-4
fleet:worker/737-a263f3-3
fleet:worker/737-20d1d9-1
fleet:worker/737-b038f7-2
fleet:worker/726-unit2-75cb13-4
fleet:worker/737-owner-key-ff061f-10
fleet:worker/736-presence-forget-f35144-9
fleet:worker/705-observer-14c258-6
fleet:worker/722-024c34-5
fleet:worker/726-ea34a0-2
fleet:worker/726-10cbf0-1
fleet:worker/729-5961c6-3
fleet:worker/727-ee14ed-3
fleet:worker/719-bdd95e-4
fleet:worker/702-4f5c7f-2
fleet:worker/715-5c43fc-1
fleet:worker/721-70f9ea-5
fleet:worker/718-99362b-2
fleet:worker/task-15-af0d10-12
fleet:worker/task-16-50a702-13
fleet:worker/task-12-4d0479-9
fleet:worker/task-13-823ce2-10
fleet:worker/705-ticket-owner-af9928-8
fleet:worker/703-list-collaborators-9c06c2-7
fleet:worker/669-example-truth-0b303d-6
fleet:worker/669-collab-deliverability-9ba859-3
fleet:worker/669-collab-reload-report-2a21bd-4
fleet:worker/669-7e80a6-1
fleet:worker/669-unit-d-efbbd7-1
fleet:worker/669-1b786a-1
fleet:worker/669-1d1d9f-1
fleet:worker/692-4afb9d-2
fleet:worker/689-02fced-13
fleet:worker/693-cf23fa-14
fleet:worker/677-fix-lead-collision-f69073-12
fleet:worker/638-fix-overmask-dbb1bf-11
fleet:worker/675-5b7478-4
fleet:worker/669-unit-a-70cc8f-3
fleet:worker/677-8cdaaf-5
fleet:worker/638-a7b391-1
fleet:worker/683-4536d6-2
fleet:worker/651-a75bbe-8
fleet:worker/680-20607d-7
fleet:worker/664-c12e95-3
fleet:worker/668-08534d-4
fleet:worker/672-0f2469-2
fleet:worker/670-7d1022-1
fleet:worker/661-ac7c28-2
fleet:worker/664-37fb9b-3
fleet:worker/663-remove-3arg-read-3f6783-1
fleet:worker/659-remove-dead-backcompat-ba5e6f-1
fleet:worker/637-revision-60a488-23
fleet:worker/656-redact-regression-tests-892903-19
fleet:worker/637-context-gauge-threshold-466eb5-16
fleet:worker/639-redact-line-numbers-de4ac4-17
fleet:worker/641-set-reformat-guard-6f96a4-18
fleet:worker/642-herdr-guard-scope-5de0e4-15
fleet:worker/650-javadoc-scope-95f3b3-14
fleet:worker/612-01e9f7-13
fleet:worker/612-a-r4-quarantine-outage-7ab0e8-5
fleet:worker/612-a-r9-r11-capacity-coverage-peers-cfcc79-7
fleet:worker/612-a-r10-loophealth-ccc872-8
fleet:worker/612-a-r12-turnregistrar-9e3bb7-9
fleet:worker/612-a-r5-leadconfigdir-9e70cf-6
fleet:lead/config-edit-redact-anchor-wording
fleet:worker/config-edit-seam-ca8dc1-1
fleet:worker/612-r67-630-lifecycle-290b8d-3
fleet:worker/629-625-ports-seams-da7d5d-4
fleet:worker/612-r12-exhaustion-f37cd7-1
fleet:worker/612-r38-amqp-24b083-2
fleet:worker/fleetd-612-unita-87807e-1
fleet:worker/612-b3-mcpwirings-da2b58-3
fleet:worker/612-b2-cb185-176d3a-2
fleet:worker/612-b1-completion-457459-1
fleet:worker/612-agaps-73a926-2
fleet:worker/608-sleeps-3a64ff-3
fleet:worker/621-b4520b-1
fleet:worker/618-b83894-2
fleet:worker/fleetd-615-e05481-5
fleet:worker/lead-autocompact-5f1ab2-3
fleet:worker/fleetd-613-f85deb-3
fleet:worker/fleetd-608-flaky-nudge-test-d0c2d1-3
fleet:worker/lead-context-gauge-ad404f-1
fleet:worker/gauge-wiring-9158c1-4
fleet:worker/redeploy-slowstart-ead0e5-5
fleet:worker/charter-bytes-13668c-6
fleet:worker/rollover-outcome-291483-2
fleet:worker/589-f64303-2
fleet:worker/593-1a8025-5
fleet:worker/589-fcd2aa-1
fleet:worker/568-9fdaa2-3
fleet:worker/571-attempted-outcome-5739f7-2
fleet:worker/581-completionresolver-cas-sites-0542b7-6
fleet:worker/562-loop-health-wiring-test-99611c-5
fleet:worker/562-surface-loop-health-7df5cc-4
fleet:worker/575-waiter-cleanup-sites-62ad80-1
fleet:worker/572-answer-lock-release-46a9ae-5
fleet:worker/567-probe-channel-leak-a38fc5-6
fleet:worker/551-record-before-send-7cbf56-1
fleet:worker/561-listener-fanout-survives-a-throw-61d538-2
fleet:worker/555-redeploy-main-flow-seam-65c2f5-2
fleet:worker/556-injector-owns-registration-e027a5-1
fleet:worker/552-post-restart-mktemp-abort-bc2672-4
fleet:worker/553-onstatus-completion-leak-0da881-2
fleet:worker/550-shasum-linux-196132-1
fleet:worker/538-loop-dies-on-error-4a5eeb-6
fleet:worker/426-health-coverage-ef1fd4-4
fleet:worker/504-failed-reported-clean-3cfd66-3
fleet:worker/537-capturedlog-close-e4c437-2
fleet:worker/459-broken-link-targets-cadc17-5
fleet:worker/535-appender-leak-fe74c1-1
fleet:worker/512-part2-shutdown-detection-434701-9
fleet:worker/529-logger-level-sweep-2a5533-8
fleet:worker/528-drain-gate-call-site-5de83d-7
fleet:charter/forge-mcp-vs-token
fleet:worker/521-swap-guard-unpinned-28e931-5
fleet:worker/519-probe-test-harness-d25ab8-4
fleet:worker/525-logger-level-leak-1b4eb0-6
fleet:worker/518-fleetmcp-resolver-wiring-8ef96c-1
fleet:worker/512-drain-complete-line-7edd71-3
fleet:worker/517-abort-branch-and-jar-id-41b641-2
fleet:worker/500-9e52c9-3
fleet:worker/509-4912f4-2
fleet:worker/511-9a4b23-1
fleet:worker/493-479f45-2
fleet:worker/505-03f8b2-1
fleet:worker/492-followup-detect-unclear
fleet:worker/501-a31fa0-7
fleet:worker/498-451d1c-5
fleet:worker/494-1015ce-2
fleet:worker/492-209647-1
fleet:worker/489-001902-2
fleet:worker/480-relative-handover-path-906323-1
fleet:worker/480-b-handover-skill-45bf1f-5
fleet:worker/474-followup-source-pin-f54a55-17
fleet:worker/474-charter-check-on-reload-f54a55-17
fleet:worker/466-quarantine-repeatcount-report
fleet:worker/393-opencode-skill-seeding-71854b-13
fleet:worker/469-canonical-tool-names-2a472a-16
fleet:worker/466-quarantine-escalation-5ae9c1-15
fleet:worker/446-hot-exhausted-pattern-0af580-6
fleet:worker/464-charter-tool-name-guard-a85635-12
fleet:worker/463-listfleet-default-fails-open-f1c76c-11
fleet:worker/458-invariant-5-by-purpose-862f9a-10
fleet:worker/439-coordinator-row-gate-bc032a-8
fleet:worker/449-herdr-protocol-576015-4
fleet:worker/450-abstract-spawn-599e1c-5
fleet:worker/437-ack-refuses-177d91-1
fleet:worker/444-placement-window-feb56a-2
fleet:worker/440-helddurable-derived-d462d7-13
fleet:worker/425-rework-placement-resolve-c58ba1-9
fleet:worker/421-lead-peek-held-msgs-cdbad2-10
fleet:worker/435-fixed-policy-cap-fe11de-12
fleet:worker/422-gate-state-observability-9e79d6-11
fleet:worker/431-memberregistry-live-readers-cdbad2-10
fleet:worker/424-architect-slot-hot-038b41-7
fleet:worker/422-model-gate-spawn-c29f48-6
fleet:worker/425-default-profile-live-f55534-8
fleet:worker/415-coverage-wording-2cbf9c-5
fleet:worker/416-3ad1da-1
fleet:worker/418-588283-3
fleet:worker/deterministic-stamp-race-409-3cb7b6-10
fleet:worker/armed-reads-live-config-404-ed931f-9
fleet:worker/reply-peer-refusal-391-5a34bd-7
fleet:worker/models-allowlist-aa9e9b-3
fleet:worker/ttl-stamp-race-399-f1122f-8
fleet:worker/scrub-receipt-400-316b3e-5
fleet:worker/exhaustion-detection-395-105105-6
fleet:worker/scrub-abort-394-316b3e-5
fleet:fix/scrub-uid-abort
fleet:worker/task-scrub-517574-2
fleet:worker/t386-clock-bd5b78-4
fleet:worker/t384-scrub-813790-5
fleet:worker/t381-cc-748314-2
fleet:worker/t373-336973-2
fleet:worker/t365-3920c5-3
fleet:worker/t358-6e989b-1
fleet:worker/t355-8b321c-1
fleet:worker/fleetd-369-hermetic-git-tests-e8b19a-3
fleet:worker/fleetd-368-stale-lead-binding-f5682e-2
fleet:worker/fleetd-360-deploy-units-0d3793-1
fleet:worker/359-dead-lead-tabs-f1253b-4
fleet:worker/362-worktree-skills-c03e51-3
fleet:worker/361-coord-visibility-655144-1
fleet:362-plugin-visibility-and-drift
fleet:worker/errscan-bed2ca-2
fleet:worker/amqp-log-identity-bed2ca-2
fleet:worker/withdefaults-guard-561704
fleet:worker/sleepguard-82076d-1
fleet:worker/fd334-9ee1b6-5
fleet:worker/fd348-f1ab27-4
fleet:worker/fd335-a71c35-1
fleet:worker/fd342-174a17-2
fleet:worker/fd345-490d0f-3
fleet:worker/fleetd-337-5ec7d4-21
fleet:worker/fleetd-341-af5a6b-24
fleet:worker/fleetd-339-5ca0a2-23
fleet:worker/fleetd-338-83a4a1-22
fleet:worker/fleetd-333-281f46-18
fleet:worker/fleetd-329-11bdbb-16
fleet:worker/fleetd-330-2770fb-17
fleet:worker/fix-326-50506e-15
fleet:worker/fix-324-3e9bbf-14
fleet:worker/fix-323-b8287d-13
fleet:worker/fix-316b-bd0860-11
fleet:worker/fix-318-76ca36-9
fleet:worker/fix-317-486aec-8
fleet:worker/fix-315-ce47c5-6
fleet:worker/fix-307-275890-6
fleet:worker/fix-308-b4f664-7
fleet:worker/fix-309-ec3939-8
fleet:worker/fix-310-7a3974-9
fleet:worker/fix-302-52ad0e-9
fleet:worker/fix-298-ce1acb-8
fleet:worker/fix-297-66bd11-7
fleet:worker/fix-296-104622-6
fleet:worker/fix-293-bare-closetab-eb22b5-3
fleet:worker/fix-280-gone-ask-lapse-bca98e-2
fleet:worker/fix-290-reapidle-guard-coverage-9b0dd1-1
fleet:worker/fix-285-trust-seed-8f3565-10
fleet:worker/fix-284-backend-error-seat-85912c-11
fleet:worker/fix-282-chained-ask-e6d0bb-8
fleet:worker/fix-283-teardown-leaks-f40dfa-9
fleet:worker/fix-281-pin-handler-actions-4921ac-7
fleet:worker/audit-rendezvous-lifecycle-d072ae-2
fleet:worker/audit-health-placement-1a2476-6
fleet:worker/audit-teardown-exits-e207a5-3
fleet:worker/audit-launcher-asymmetry-27e370-4
fleet:worker/audit-rest-authz-6ca53c-5
fleet:worker/investigate-275-abandon-asking-fdef52-8
fleet:worker/fix-274-worktree-leak-b0095d-7
fleet:worker/fix-273-exhausted-pattern-9665b5-6
fleet:worker/fleetd-267-model-check-bd8068-1
fleet:worker/fleetd-131-archunit-18b834-7
fleet:worker/fleetd-266-sshagent-rename-a014ff-6
fleet:worker/fleetd-184-uid-claim-8e1f31-4
fleet:worker/fleetd-184-warn-b381ee-10
fleet:worker/fleetd-184-docs-be1d12-9
fleet:worker/fleetd-257-9bf010-7
fleet:worker/fleetd-103-23a113-6
fleet:worker/fleetd-247-342356-5
fleet:worker/fleetd-116-04dea8-4
fleet:worker/fleetd-252-a830e0-3
fleet:worker/fleetd-111-7e8673-9
fleet:worker/fleetd-155c-f8ef4b-8
fleet:worker/fleetd-176-b928ca-3
fleet:worker/fleetd-249-7a7878-2
fleet:worker/cb248-composition-root-b-9acdf7-15
fleet:worker/cb148-envrc-default-fa6c82-12
fleet:worker/cb201-unit5-wiring-6c12e6-8
fleet:worker/cb241-fallback-echo-1175e9-11
fleet:worker/cb149-trust-dialog-2392a5-9
fleet:worker/cb134-148-overlay-visible-c9b986-10
fleet:worker/cb234-session-id-keyed-04e1fc-1
fleet:worker/cb201-unit3-nudge-abdf5c-6
fleet:worker/cb201-unit2-policy-c1102c-5
fleet:worker/cb201-unit4-outcome-a13bfa-7
fleet:worker/cb201-unit1-classifier-91b9b1-4
fleet:worker/cb201-227-refine-831980-3
fleet:worker/cb175-model-readback-0f085f-1
fleet:worker/cb222-charter-tmpdir-17f013-1
fleet:worker/cb226-architect-slot-race-cd3aa8-3
fleet:worker/cb224-worktree-root-group-024523-2
fleet:worker/cb-123-role-demotion-c600f7-2
fleet:worker/cb-219-opencode-roots-1f677e-1
fleet:worker/cb214-claude-session-id-b9eab4-4
fleet:worker/cb213-zdotdir-wrong-process-dd6de4-3
fleet:worker/cb211-exhaustion-classification-9546e0-2
fleet:worker/cb137-ambiguous-task-4df3d8-4
fleet:worker/cb209-agentsessionid-4dfdb6-2
fleet:worker/cb185-hostenvnames-2692b5-3
fleet:worker/cb206-opencode-sqlite-128718-2
fleet:worker/cb185-worktree-group-fc0c99-1
fleet:worker/cb-137-ask-ticket-e7760c-2
fleet:worker/cb-172-broker-uri-d36ae4-4
fleet:worker/cb-175-model-readback-76ead6-3
fleet:worker/cb-161-pane-ancestry-293510-1
fleet:worker/cb-164-rebase-885863-8
fleet:worker/cb-164-empty-scrape-false-success-1a80af-3
fleet:fix/cb-197-ticket-ttl-from-completion
fleet:worker/cb-189-remote-url-coverage-4692f3-1
fleet:worker/cb-185-blockers-027756-4
fleet:worker/cb-192-gap-log-11b631-2
fleet:worker/cb-633-fix-5f4396-3
fleet:worker/cb185-router-d6436d-3
fleet:worker/cb185-router-routing-gaps-9e9d33-3
fleet:worker/cb185-paneids-992586-2
fleet:worker/cb-633-allow-list-union-ed374b-1
fleet:worker/cb-157-credential-in-remote-url-496e44-2
fleet:worker/cb-641-health-herdr-evidence-8f1f54-6
fleet:worker/cb-640-health-msg-evidence-99c9cd-1
fleet:worker/cb-642-fleets-status-skill-bbbc40-5
fleet:cb-634-ide-mcp
fleet:worker/lead-comms-wiring-c014b9-7
fleet:worker/lead-mailbox-c19577-6
fleet:worker/autocompact-window-82bc2f-5
fleet:worker/cb-634-probe-18056f-4
fleet:worker/cb635-broker-urienv
fleet:worker/cb-632-config-retry-8e0efa-7
fleet:lead/cb-622e-claude-md
fleet:lead/cb-622-followup
fleet:worker/cb-622a-165dff-1
fleet:lead/cb-622d-opencode-mount
fleet:worker/cb-622b-717c67-2
fleet:worker/cb-622c-ab7759-3
fleet:worker/cb-617b2-20ca4b-3
fleet:worker/cb-617a-5c2f4a-1
fleet:worker/cb596-4e49ef-3
fleet:worker/cb586-10500c-1
fleet:worker/cb-606-b9343a-25
fleet:worker/cb604-1445f8-24
fleet:worker/cb582-477374-21
fleet:worker/cb584-8c2281-22
fleet:worker/cb600-e6b9a9-20
fleet:worker/cb602-ce257f-19
fleet:worker/cb601-b42837-18
fleet:worker/cb598-6c7ba7-17
fleet:worker/cb599-740fe4-16
fleet:worker/cb597-282224-15
fleet:worker/cb590fix-185e9a-10
fleet:worker/cb528-recovery-race
fleet:worker/cb594-96bead-8
fleet:worker/cb590-916766-2
fleet:worker/cb527-997d99-3
fleet:worker/cb592-env-leak-3cbf9c-1
fleet:worker/cb588-async-ticket-nudge-3218f7-5
fleet:worker/cb578b-9dcb13-6
fleet:worker/cb581-d24826-5
fleet:worker/m2-u5-ef8c42-15
fleet:worker/cb578a-516499-2
fleet:worker/cb576-01a04b-17
fleet:worker/cb579-lead-tab-acba06-20
fleet:worker/cb580-terminal-health-ed6058-21
fleet:worker/cb577-f36fdc-18
fleet:worker/cb573b-3db06f-16
fleet:worker/cb568c-f36fdc-18
fleet:worker/cb568-drop-cause-c3ac1c
fleet:worker/cb575-cancelled-notification-c3ac1c
fleet:worker/m4-sol-a2cbec-3
fleet:worker/cb574-async-ask-c3ac1c
fleet:worker/cb573-health-model-8ca857-14
fleet:worker/cb572-unknown-target-7f2e35-13
fleet:worker/u4-700706-9
fleet:worker/u3-b9fcb6-6
fleet:worker/u2-ef5b68-4
fleet:worker/u1-469dce-1-clean
fleet:worker/u1-469dce-1
fleet:worker/cb-564-health-events-70cf7e-2
fleet:worker/cb-565-recycle-drops-role-98e58f-3
fleet:worker/cb-563-missing-reply-df2866-1
fleet:worker/cb-562-readiness-gate-silent-6c23c9-3
fleet:worker/cb-560-architect-presence-da8155-1
fleet:worker/cb-561-architect-silent-off-a71cab-2
fleet:worker/cb-548-bind-architect-slot-fe1b8c-1
fleet:worker/parity-overlay-settings-5fb711-1
fleet:secrets-central-store
fleet:cb-559-hot-key-correction
fleet:cb-557-fleet-role-pools
fleet:worker/cb-553-maxload-explicit-spawn-305ee3-6
fleet:worker/cb-551-idle-lead-heartbeat-f1633c-1
fleet:worker/cb-544-drain-preserves-worktree-925fad-3
fleet:worker/cb-552-docs-sync-1cb9cf-4
fleet:worker/cb-548-rendezvous-guard-rebased
fleet:worker/cb-548-rendezvous-guard-116b53-10
fleet:worker/cb-548-authz-v2-586df6-8
fleet:worker/cb-548-authz-264363-5
fleet:salvage/cb-528b-codex-home
fleet:salvage/cb-528a-codex-launcher
fleet:CB-518-primary-flow
fleet:feature/peer-launcher-spi
fleet:cb-103-injector
No Reviewers
Labels
Clear labels
blocked
needs-live-proof
ready-to-delegate
silent-default
Cannot start until something else lands. The body says what.
Merged and green, but never shown working on the running daemon. Not the same as done.
Scope, files and acceptance criteria are written. A worker can be briefed from the body alone.
A feature that compiles, passes tests, and ships turned off. Nine recurrences and counting.
No Label
Milestone
No items
No Milestone
Projects
Clear projects
No project
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: fleet/fleetd#789
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Delete Branch "worker/788-eb7dd0-1"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes part of fleet/fleetd#788.
What this adds
A Claude Code session running the fleet mod now pulls its messages from fleetd and hands
each one to Claude with
$.prompt.submit, instead of having them typed into its terminal byherdr. fleetd names a caller by its pane, not by its Claude account, so this is the hop that
works between two accounts on one host.
1.
fleet_inbox(new MCP tool)Takes no arguments. The pane is the caller's connection-resolved terminal, so no caller can read
another pane's mail.
Authzgets anINBOXaction, grouped withREPLYandASKin theonly-as-itself case (
caller.ownsSession(targetSession)), so every role may call it for itselfand none for another pane. Returns
{sessionId, count, messages}.2. Injector route
A pane becomes mod-served by calling
fleet_inbox.PaneInboxholds the record and theoffered mail; the Injector owns it, because it is the single writer of delivery state and the
offer has to be made and taken back under the same target monitor that guards the queue.
The Injector asks "is this pane mod-served?" at the moment it is about to deliver, and offers
the message for collection instead of typing it. The message stays at the head of the
Injector's queue until the pane takes it. That one decision gives three things:
lead's pane would submit whatever its operator was in the middle of writing.
cancel,dropand both grace expiries withdraw the offer as well, so a message the caller wastold never arrived can never arrive later.
drainandwithdrawAllrun under the same monitor,so an entry is removed exactly once — never both collected and withdrawn.
3. Ticket states
Unchanged, by construction. The message leaves the Injector queue at the same point as a typed
one, so
queuedWaitMillis— and thereforefleet_poll's PENDING detail — reads"queued, not yet delivered" while it is merely offered and flips to "worker " once
collected. The reply still arrives through
fleet_reply.MessageServiceInboxDeliveryTestruns both routes side by side in one test and asserts thephase, the reply outcome and the reply source match.
4. Mod
A 3s timer in
plugin/hooks/register.jscollects through the existingfleetToolhelper andsubmits each message. A fleetd that is down makes the timer return, not throw: a throw would
stop every later poll.
The two decisions the ticket asked for
N = 15 seconds (
PaneInbox.MOD_SERVED_WINDOW_MILLIS). The mod polls every 3s, so 15stolerates four missed ticks — a slow turn, or a brief daemon hiccup — while still being short
enough that a pane whose mod really stopped falls back within one 250ms Injector poll of the
window closing. The two constants are deliberately a factor of five apart rather than adjacent.
Stop-polling rule: fall back to the terminal paste route. Two reasons. First, it costs no new
machinery: because the message never leaves the Injector's queue until the pane takes it, the
fallback is the absence of a branch rather than a recovery path, so there is no window in which
a message is neither offered nor queued. Second, the pane is a real Claude Code pane in a herdr
pane, so the typed route is known to work for it — failing the ticket would throw away a delivery
route that functions and would need an operator to re-send. The alternative (fail with an explicit
reason) is only the better choice if the typed route were unavailable, and it is not.
What I did NOT change
the Injector never consulted
PromptBox— only the lead push loops do — so that half of theticket's note is true for free. Relaxing the status gate is a separate change and I left it
alone on purpose:
awaitingPickup/awaitingCompletionare built on sampled status edges, soarming them while the pane is mid-turn would let the next idle sample fire a completion for the
previous turn (the CB-116 hazard the existing comments name). Criterion 3 asks for the same
ticket states as a typed message, and those states are exactly what the sampled edges produce.
Injector.enqueuenever sees one — so the mod names the channel ("Message from the fleet, viafleetd") rather than inventing a peer name. Where fleetd already attributes (an observer's send
carries its own
[fleet_send from observer term_…]prefix) that rides inside the text. Adding areal sender field would mean widening
MessageService.sendandInjector.enqueue, which isoutside this scope.
LeadCoordLoopis a different path from the Injector and isuntouched.
CLAUDE.md,wiki/,.mcp.json,fleetd.yamlare all unedited, per the brief.docs/MCP-Contract.mdis flows-only and names no tool catalogue, soMcpContractDocTeststaysgreen with the extra tool; the intent→tool table and the Features entry are the lead's.
Verification
Counted independently from
target/surefire-reports/TEST-*.xmlafter the clean: 182 XML files,same 2200 / 0 / 0 / 0. New classes:
PaneInboxTest7,InjectorModServedDeliveryTest6,MessageServiceInboxDeliveryTest2;AuthzTest31 (one new test, one exemption list widened —anObserverIsDeniedEverythingBeyondReadMetricsReplyAskInboxAndSend, since an observer panerunning the mod must be able to collect its own mail).
Every negative assertion carries a positive control in the same test. Two mutations confirm the
tests bind rather than merely pass:
isModServedtofalse→ 9 of 15 fail;InjectorModServedDeliveryTest.aPaneThatStopsCollectingHasItsMailTypedInsteadfails, with"the message falls back to the terminal route ==> expected: <[do the task]> but was: <[]>".
Both mutations were reverted and the final
mvn clean installabove was run after the revert.For review
FleetdAssembly.assembleAndStartis unchanged — no new wiring line. ThePaneInboxisconstructed inside the Injector's two full constructors from the clock it already holds, which
also means no existing
Injector,MessageServiceorFleetMcpconstructor gained a parameterand no existing test needed editing for construction.
PaneInboxis not a silent default that disables the feature: no pane has polled, sono pane is mod-served, which is the correct answer rather than an off switch.
CLAUDE.md↔ wiki block sync check:wiki/is uninitialized in a workerworktree (
git submodule statusprints a leading-). Not run, not passed.A Claude Code session running the fleet mod now pulls its messages from fleetd and submits them with $.prompt.submit, instead of having them typed into its terminal by herdr. fleetd names a caller by its pane, so this is the hop that works between two Claude accounts on one host. New MCP tool fleet_inbox. It takes no arguments: the pane is the caller's connection-resolved terminal, so no caller can read another pane's mail. Authz gets an INBOX action, grouped with REPLY and ASK as only-as-itself. A pane becomes mod-served by calling fleet_inbox, for a 15s window that each call renews. The Injector asks that question at the moment it is about to deliver, and offers the message for collection rather than typing it. The message stays at the head of the Injector's queue until the pane takes it, so: - delivery is recorded when the pane really has the text, not when it was offered, and fleet_poll reports the same states as for a typed message; - a pane that stops polling leaves the window and its mail is typed instead, with nothing stranded and nothing delivered twice. The offer is withdrawn under the same monitor that drains it, so an entry is removed exactly once; - a collected message gets no Enter nudge. Nothing was typed, and an Enter in a lead's pane would submit whatever its operator was writing. cancel, drop and both grace expiries withdraw the offer too, so a message the caller was told never arrived can never arrive later. Mod side: a 3s timer collects through the existing fleetTool helper and submits each message. A fleetd that is down returns from the timer rather than throwing out of it, which would stop every later poll. Build: mvn clean install in fleetd/ — Tests run: 2200, Failures: 0, Errors: 0, Skipped: 0; BUILD SUCCESS. claude plugin validate plugin and claude plugin test plugin both pass (13 pass, 0 fail). Two mutations confirm the new tests bind: forcing isModServed false fails 9 of 15, and removing the stop-polling fallback fails exactly aPaneThatStopsCollectingHasItsMailTypedInstead.Pull request closed