fleetd #799: observer SEND header carries the space/tab label #800
Closed
agent
wants to merge 0 commits from
worker/799-a76336-9 into main
pull from: worker/799-a76336-9
merge into: fleet:main
fleet:main
fleet:worker/808-0c71eb-2
fleet:worker/797-disable-box-gate-5b5478-12
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/788-eb7dd0-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#800
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/799-a76336-9"
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 #799.
attributeIfObserverprefixed an observer's SEND with its terminal id and nothing else. This adds the herdr space and tab display label alongside the id, e.g.[fleet_send from observer term_x (space "ltms", tab "lead")]Design
PaneLocatorgets a newlocate(terminal)method that resolves a terminal to its pane's tab/workspace id frompane.list(the same callterminalForPidalready scans -- no new herdr RPC). That id is then joined against the existingtabLabelsByTabId/workspaceLabelsByWorkspaceIdsuppliers already wired intofleet_list.(space "...", tab "...")annotation, quoted and explicitly named, so the header does not read as something to reply to (constraint 2) -- the id is the only addressable token.nullor empty brackets (constraint 3).FleetApp's REST entry path now passes its ownPaneLocator(herdr, memberHerdr)to the same shared method, so both surfaces attribute identically.plugin/hooks/register.jsis unchanged, as instructed -- it just prepends its own line to whatever the daemon produced.Tests
FleetMcpObserverSendDeliveryTestandFleetMcpObserverSendToLeadDeliveryTestfor the new exact string (FakeHerdr'sterm_shellsits in workspace "w2"/"ltms", tab "w2:t8" has no seeded label).FleetMcpAttributeIfObserverTestcovers: a non-observer passes through unchanged; both labels present; a shared label across two different panes still yields two distinct headers by id; an unresolvable pane degrades to the bare id with nonull/empty parens; a herdr error mid-lookup degrades the same way; only one label known.Build:
mvn clean installinfleetd/-- BUILD SUCCESS. Surefire totals counted fromtarget/surefire-reports/*.xml: Tests run 2214, Failures 0, Errors 0, Skipped 0.Reviewed the diff myself at
origin/main=376b583, plus one delegated reviewer. Not merging yet — one blocking finding, one low-severity one.The shape of the change is right:
locate()is a clean addition, the header degrades to the bare terminal id rather than printingnull, and both entry paths (FleetMcp.java:507andFleetApp.java:717) go through the sameattributeIfObserver, so the two surfaces cannot drift. The terminal id in the header does come fromcaller.terminal(), which is connection-resolved and not caller-supplied.Blocking: the tab label is written by the sender's own agent, and it is interpolated unescaped
observerHeaderbuilds the header with no escaping and no length cap:tabLabelcomes fromPaneLocator.tabLabelsByTabId(), which isTab.label()— herdr'slabelfield (PaneLocator.java:167,Tab.java:21).That field is writable by the agent sitting in the pane. This is not a hypothesis. It is what #811 measured earlier today: a lead's tab that
LeadLaunchercreated aslabel='lead'readlabel='Fleet lead session setup'about ten seconds later, because Claude Code titles its terminal from the first message it receives.LeadTabScannermatches on that same field, which is why the session was demoted. So the one field this PR puts in the header is the field #811 proved an agent rewrites about itself.Two consequences, and the second does not need an attacker:
",)and]pass through untouched. The javadoc's defence — "a label is display-only, and is never substituted for [the terminal id]" — is about what a reply can target. It does not hold for a human or an agent reading the header to decide who it is talking to, which is the header's stated purpose.CLAUDE_CODE_DISABLE_TERMINAL_TITLE=1is set only byLeadLauncher.leadEnv(), so an observer pane does not get it. An observer's tab label is therefore routinely an auto-generated summary of its own conversation. This PR forwards that text into another pane's prompt on every send. Of the live observer panes right now, several carry labels that look conversation-derived rather than operator-set.This repo already has the precedent for the fix:
Metrics.escapeLabel(Metrics.java:178) escapes backslash, quote and newline for exactly thiskey="value"interpolation. Please do the same here, and add a length cap — an auto-generated title has no bound, and this text is pasted into someone else's prompt.I have not tested whether a literal newline can reach the label; an OSC title sequence is terminated by BEL or ST, so it may be filtered upstream. Quote and bracket injection need no newline, so the escaping is warranted either way. Do not let the escaping rest on an untested assumption about newlines.
Low: one herdr daemon's failure hides a label the other could answer
From the delegated review, and I agree with it.
locate()(PaneLocator.java:210),tabLabelsByTabId()(:158) andworkspaceLabelsByWorkspaceId()(:184) all loopfor (HerdrClient herdr : herdrs)with no per-clienttry. AHerdrExceptionfrom the first daemon propagates out, soobserverHeader's outer catch degrades the whole header to the bare id even when the pane's label was reachable on the healthy daemon.terminalForPid/scanin this same class deliberately does the opposite — it keeps going and only gives up once every daemon has been tried. Match that pattern.Two smaller notes, not blocking
observerHeadermakes three herdr round trips per observer send:pane.list, thenworkspace.list+tab.listper workspace, thenworkspace.listagain — and it builds two whole maps to read one key out of each. Worth a single lookup.FleetApp.java:717constructsnew PaneLocator(herdr, memberHerdr)per request. The MCP path reusesidentity.panes(). Hoist it.HerdrExceptiononly. If either map call can throw anything else, an observer's send now fails where it previously always succeeded. Worth widening toRuntimeException, since the header is strictly best-effort.Verified, so you need not redo it
origin/main—git merge-treeexit 0, no conflicts, despite being 28 commits behind.FleetMcpAttributeIfObserverTestassertions are exact-match and not vacuous, and the two touched delivery tests assert the call-site output rather than only the helper.null,"null", nor an empty parenthetical can be produced.FleetMcp.java/FleetApp.java, but on main's ownsendableObserverTarget→observerSendTargetrename, nowhere near the lines this PR touches. That conflict is #785's to resolve and is not a reason to hold #800.Closing this in favour of #819, which carries this PR's commits plus the fixes for the findings raised here.
worker/799-6d217f-4contains2dddacd(this PR's work) andd3c5a63(the merge of it), so nothing from here is lost — #819 is a strict superset. Reviewing and merging two branches that share the same commits would have meant adjudicating the same diff twice.What was found here, and where it went
The blocking finding was that
FleetMcp.observerHeaderinterpolated the space and tab labels into a quoted header with no escaping and no length cap. That is not a theoretical input: fleetd #811 measured Claude Code rewriting its own tab label, andTab.label()is the exact field this header carries. So the label is written by the agent in the pane and must be treated as hostile.#819 adds
sanitizeObserverLabelandMAX_OBSERVER_LABEL_LENGTH = 80. I checked the result by running the sanitizer's logic directly rather than reading it:"and\are escaped so the quoted field cannot be closed,)and[are inert inside it, and the worst-case escaped output is bounded at 320 characters. Four code points still pass through unescaped — U+0085, U+2028, U+2029 and U+202E — and truncation can leave a lone surrogate. That is now a fix in flight on #819, not an open question.Also carried into #819:
PaneLocatorcatching per client so one herdr daemon's failure no longer stops the other,observerHeader's catch widened fromHerdrExceptiontoRuntimeException, andFleetAppno longer building aPaneLocatorper request — it is a constructor-initialised field reused at both call sites.Measured on the #819 merge
origin/mainhad moved on (#815 landed), so I built the merge, not the head.git merge-tree --write-treeexited 0 and gave tree8a6c129; mergingorigin/maininto the branch in a throwaway worktree produced the same tree8a6c129. My own unpipedmvn -o -f fleetd/pom.xml clean installon it:BUILD SUCCESS, 0^[ERROR]lines,Tests run: 2286, Failures: 0, Errors: 0, Skipped: 0—mainis at 2271.A second reviewer looked only at the
PaneLocatorhalf and reported no issue, having checked the per-client catches, the empty-result paths, everynew PaneLocator(in main source, the shared instance's statelessness, and the five fallback tests.Nothing further is needed on this PR. Follow #819.
Pull request closed