Features: exhaustedPattern validated at load (#273); a failed provision cleans up (#274)
Also records that the per-worker worktree does NOT isolate git stash --
refs/stash is one shared stack, which let two parallel workers pop each
other's uncommitted work today.
Features: draining an inbox is lead-only (#272); the count line names whose env it counted (#276)
Also corrects this page's own version of the overclaim #269 removed from the
code: the entry asserted that under memberHerdrSocket the member pane belongs
to a different OS user. fleetd cannot confirm that either way — that is the
whole reason the report says unknown. Fixing the code and leaving the wiki
asserting the old claim is the same one-way repair the entry warns about.
Features: state the honest boundary a member runs inside (#184)
Several settings on this page look like security controls. They are not, and
the reason is the same for all of them: a member runs as the same OS user as
the lead. That fact was written nowhere an operator reads, so people read the
settings and concluded a member was contained.
Adds one entry giving the per-channel truth, the proven path a member takes to
the operator's ssh key, and the point that no meaningful boundary exists inside
one uid. Names the recurring shape: a gate written after an incident closes
only the direction that incident came from.
Features: correct the free/leadSeats entry, and record the trust-seed CAS
Two entries described behaviour that changed today.
#257: the 'free counts the lead's seat' entry described a subtraction that
has been removed. free now means what the spawn gate grants. leadSeats is
still reported, as a fact beside it. Renamed the entry to say so, and kept
the history of why the subtraction was tried and dropped.
#247: the workspace-trust entry said the lost-update race against the
operator's own Claude Code was 'tracked separately'. It is now fixed with a
compare-and-swap plus a bounded retry that writes nothing rather than
overwrite. Recorded that, the two new WARNs, and the window that remains.
REST surface: add the route that drifted, and a Features entry that points here
Chapter 15 was written on 2026-08-31 and was correct for all 14 routes that
existed then. GET /member-credentials shipped three days later (#111) and the
page did not follow it.
- ch.15: route count 14 -> 15, a table row for GET /member-credentials, and a
per-route detail section (field meanings, and that blockedCount is the
policy's own blocked set, not knownCount - allowedCount).
- ch.11: a short entry for the REST face — what it is for, the bind: knob, why
it exists, the drain-on-read gotcha — linking to ch.15. Deliberately no route
table: a second copy is the defect, not the errors it collects.
The "not an agent channel" point is stated accurately: REST does not skip the
authorization gate. 14 of 15 routes resolve the caller through the same
CallerResolver and Authz table MCP uses, and /healthz is open on purpose as a
liveness probe. The real reason is that identity comes from the connection, and
a member's own child process is a connection the daemon must reason about —
which was wrong before (#161).
fleetd #252. A test in the repo now enumerates FleetApp's registrations and
fails when they no longer match, naming this page as the one to update.
Features: credential probe reads the policy, allow-list refuses a non-zsh spawn, free counts the lead's seat
Three operator-visible changes shipped and live on 2026-09-03:
- fleetd #111 — the probe fetches GET /member-credentials instead of
carrying its own 31-name copy. Verified live in a member: 34 checked,
29 blocked, 5 present.
- fleetd #155 — policy: allow-list now refuses a spawn under a non-zsh
login shell instead of degrading to the weaker overlay. Includes the
memberLoginShell/memberHerdrSocket trap that refuses every spawn.
- fleetd #176 — fleet_list's free subtracts the lead's subscription
seat and the row carries leadSeats. Records that free and the spawn
gate now disagree by one on purpose, so nobody raises maxLoad to
compensate.
Three merged capabilities that had no entry:
- fleetd #201/#227 — the errorPattern knob, credential cool-off, and how
cooling off differs from exhaustion quarantine.
- fleetd #149 — seeding the workspace-trust flag before a claude-code
spawn, why the rejected fixes were rejected, and the external-writer
race the atomic write does NOT cover.
- fleetd #134/#148 — the neutralized-config and parity-overlay log lines,
the git-config channel a member can actually read, and the new
[.env] default.
Features: late-resolved member ids, /members body key, and the honest gap report
Also corrects the opencode.db entry: it claimed fleet_list reports agentSessionId,
which was only true after the second fix (#209). Reading the database was necessary
but not sufficient - SessionManager froze the id at spawn, before opencode writes it.
Features: worktreeGroup, and opencode session ids from opencode.db
Two shipped capabilities that had landed nowhere, plus a correction to the
memberHerdrSocket entry: worktree uid is no longer the open blocker there, but
the per-user 0700 system temp directory holding the role charter now is, and
that one no chmod fixes.
Features: record today's three merges and correct two stale claims
- memberHerdrSocket: the stop-after-restart blocker is cleared (probe by
daemon); /healthz now reports the member daemon under its own key with
protocolMismatch. Both paragraphs described these as open/missing.
- memberCredentials: the gap report now has three cases, not one. Added the
table, and the derived-allow-list superset row that used to be reported as
contained.
- SSH_AUTH_SOCK is blocked now, not allowed on purpose. Says plainly that
#184 measured this as buying nothing, so it does not read as solved.
- New entry for the worktree remote-URL credential check (CB-157/CB-189).
Match the code cutover: daemon name, config (fleetd.yaml), scripts, launchd/
systemd units, module dir, and MCP tool prefix bridge_* -> fleet_*. Kept:
the BRIDGED_MEMBER security marker, mcp__bridge__ (historical mount name), and
the .bridged-worktrees on-disk path. The portable CLAUDE.md block stays
byte-identical with the repo's CLAUDE.md.
Features: auto-compact window (CB-636) + cross-host lead coordination (CB-637)
Add two Features entries and propagate the cross-host peer-lead coordId row
into the canonical CLAUDE.md block template (7-Use-Cases). The block stays
byte-identical with the copy in the fleetd repo's CLAUDE.md.
The page told operators the control needs a BRIDGED_MEMBER-guarded block at the
end of the secret store. That is now only true for deny-by-default. The
allow-list policy owns the seam itself, so no operator file is involved.
Records the two things that cost the most to learn: a control inside a sourced
file can always be undone by a file sourced later (a correct block list still
leaked seven credentials), and the scrub in .zlogin alone was dead on Linux
because a herdr pane there is not a login shell.
CB-586: refs/wip snapshots are swept once they are safe to drop
New entry for the retention rule (reachable-from-main AND older than 24h),
the /members wipRefs{count,costBytes} census, and two gotchas: the sweep
shipped dead because a Long.MIN_VALUE sentinel overflowed the interval gate,
and costBytes double-counts shared objects so it is not disk usage.
CB-606: config values are checked against their valid set
One entry for all four fields rather than four entries — they are one defect of
shape, and the gotcha worth recording is the shape, not the instances.
The auth.mode case is the one to keep: a typo behaved as loopback-trust, and the
exposure check only fires on a non-loopback bind, so a loopback bind hid it end
to end. The daemon started clean and authenticated nobody.
CB-582 + CB-604: the ask nudge, and kind: is now validated
The kind: entry documented the unvalidated-typo gotcha as live. CB-604 fixed it,
so the entry now carries the refusal message instead, and points at CB-606 for
the three fields that still have the same shape.
New entry for CB-582: a worker paused on bridge_ask nudges the lead's pane, and
bridge_status and REST both show the open question. The gotcha is the part that
matters — the ~55s window is closed, not removed, so 'do not brief a worker to
ask me' still stands.
CB-595: the last five Features entries, from a read of the code
Adds /metrics and /healthz, bearer auth and the bind fail-fast, the
authz table and audit log, systemd supervision, and multi-profile kind:
routing. Every claim traced to a file:line on main.
Two defects turned up while writing them, both the same shape - config
accepted, does nothing useful, reports no error: kind: is never
validated, and the systemd unit has the login-shell secret defect that
launchd's wrapper already fixes.
Also records why a worker cannot read this page: the submodule pointer
is deliberately never updated, so a worker's checkout is months old.
CB-584: sync the portable block, and catalogue the resume hint
The bridge_spawn and bridge_list rows in the intent table changed on
main, so the wiki template had drifted from CLAUDE.md. Re-synced; the
check now prints 'in sync: True'.
Features entry for the failed-ticket session id: what it is, that it
needs no knob, why it exists (stage C saves the files, this saves the
thread), and that its absence is the normal case rather than a fault.
CB-589: catalogue that weighted placement is not cheapest-first
Records the two traps: a maxLoad'd profile freezes its score and can
lose the next pick after a slot frees, and the weight-100 workaround
expresses a preference order through a ratio knob, so a future profile
added at a higher weight silently outranks the free box.
CB-593: record which inherited credentials a member may keep
CB-592 blocks one credential name. The pane's login shell exports about
thirty. This records the decision for the two that matter most -
AI_GATEWAY_TOKEN and CONTEXT7_TOKEN both stay, with the reason - so a
deliberate choice never again looks like an oversight.
Also corrects the nudge-budget backfill note: CB-598 moved the budget to
per pending item today, so 'per source' was already out of date.
CB-595: backfill the ~14 operator-facing tickets shipped since v1.0.0
Nine new entries, written from a read of the code on main rather than from
commit messages.
The three keys that changed MEANING lead the batch, because that is what an
upgrading operator meets first and none of it is visible in a diff of their own
config: weight: 0 and maxLoad: 0 both used to do the opposite of what they read
like, and fleet.leaders.*.terminal is now refused at startup rather than
ignored.
Then: the unconditional maxLoad cap on explicit spawns, the opt-in idle-lead
heartbeat and why it must stay opt-in, charter receipts, the quarantine-aware
capacity view, session resume and why it demands an explicit profile, opencode's
--auto and forced auto-compaction with the trade stated plainly, the silent
member-failure logging, and the removal of recycle().
Each entry carries the why, which is the half that stops a decision being
re-litigated from scratch a month later.
CB-594/CB-527/CB-528/CB-590: catalogue supervision, the startup secret report, and the AMQP guarantees
Four entries' worth of shipped, operator-facing behaviour that had no home:
- launchd supervision that actually carries the fleet's secrets, plus the
redeploy script's launchctl branch and why a bare kill was wrong (exit 143)
- the startup secret report, and why it warns rather than refuses to boot
- broker.prefetch and publisher confirms, with the Return-before-Confirm
ordering trap and the fact that these tests only run in a separate CI job
Also corrected two entries the merges invalidated: the async-ticket nudge now
shares one schedule per lead with a budget per source, and the systemd half of
CB-504 is still unchecked for the same login-shell defect the launchd half had.
CB-595: catalogue the async-ticket nudge and the member token shadow
Two of the ~14 operator-facing tickets that shipped since v1.0.0 with no
Features entry. Written from behaviour I verified directly this session,
not from commit messages.
CB-588 — a wait:false ticket now nudges the lead's pane when it goes
terminal. The why line is the point: the charter tells leads to prefer
wait:false, and until this landed that was the one mode with no
notification at all.
Its gotcha is one I hit myself today. The nudge goes to the lead's PANE,
so a lead driving the REST surface gets nothing, and the 10-minute
terminal-ticket TTL then prunes the report. A ticket 404'd while its
member still sat in done. The work survived only because the implementer
skill had opened a PR. The ticket is not the durable artefact; the PR is.
CB-592 — the admin GITEA_ACCESS_TOKEN is shadowed in every member's
environment, with the BRIDGED_MEMBER marker to win against the pane's
login shell re-exporting it.
Its gotcha names the limit honestly: this blocks ONE name out of about
thirty the login shell sources, and a second forge token is among the
unblocked ones. Filed as CB-596. Records why it went unnoticed —
present-and-useless looks identical to absent.