0036 — The dock is the identity: the task records it, the workflow is keyed on it, the session is found by id
- Status: Accepted (the rule). This is the record DESIGN §11, §11b and §12b (
~/.claude/jobs/fleet-reset-2026-09-12/DESIGN.md, Gera 2026-09-12/13) said it needed under ADR-0004, and it was never written. ⚠️ AMENDED 2026-10-01 — ONE DRIVER PER DOCK GROUP. Gera answered the groups question: he runs three Drivers onportfolio-architecture-phases, one per group set. Decision 3 is corrected in place: a Driver's id isdriver-<dock>.<group-key>when its task names a group, and a second Driver is refused only on the same dock and group, while a whole-dockdriver-<dock>still excludes every group on its dock. Read Amendment 2026-10-01 at the foot. (Appended toStatuson purpose: this adds NO line, so every line-number citation into this file stays true.) The build:gera-propflow/local-bin#293, merged 2026-09-29: thedock-ofresolver, and deleting every default board.PropFlow-Technologies/agent-smith: the dock-keyed workflow id. Open, not deployed.- dotclaude: the Stop gate reads the dock itself. Open, not deployed.
- Date: 2026-09-29
- Deciders: Gera, 2026-09-12: "in the big picture we only want one temporal per dock", and "how do we know this dock is claimed already". 2026-09-13: "I would want a name across the temporal, the board doc and the session, so I can always say oh check on XYZ and it's clear." 2026-09-29, after the homepage Driver's brief read another dock's board: "essentially we've never really audited the driver with the boards and the Temporal."
What was measured (2026-09-29)
- No task records its dock. The only correct copy is in
driver-setup.json, as thedock-finisherspecialist'sref, and it is correct for all four live Drivers. The tools that need a board do not read it:dock-claimed-items-merged:64,dock-finisher:63andslack-poster:142default toportfolio-architecture-phases.fleet-grader:47,critic-poll:44andplan-review:51hardcode it.driver-setupprefills the dock from whichever run came last.driver-brief:251passes no slug. The homepage Driver's brief therefore measured its claims against the portfolio board:REFUSED (exit 5): 6 claimed rows not on the published dock.- All of these were written on 2026-09-12, when there was one dock. The default was correct that night.
- Workflow ids are keyed on the task, not the dock (
op-task-sess-…). Temporal therefore admits two Drivers on one dock, which is DESIGN §11's own diagnosis of the 09-12 collision. - All four live Drivers came in through door 2,
/drivertyped into a live session.- They run in
claude-fresh-*tmux sessions whose names match neither the task nor the dock. - Tools that find a Driver by tmux name (
driver-census,driver-brief,driver-here --name) report "(none)" live. - The watcher finds its Driver by roster name. The fix for that is ADR-0035 decision 5.
- They run in
The decision
- The dock is recorded once: in the task's setup record. That is
driver-setup.json's finish-line row (dock-finisher, itsref), whichdriver-setupwrites when the run is set up.- task.json does not carry a copy. The Supervisor round-trips task.json through an allowlist, which erases a field it does not know, and a second copy is a second fact to keep true.
- The Supervisor's
holdUntilDockis exactly such a copy. It is set on one task (page-speed), and it goes when the Stop gate reads the dock itself (decision 6).
- There is one resolver:
dock-of <task>(local-bin #293).- It reads that row. If it is absent, it refuses (exit 2) and names what is missing.
--verifyproves the finisher can count the dock (exit 4 if not).--livelists every live task and its dock.- Every tool that needs a board takes it from
dock-ofor from an explicit--slug. No tool has a default board. A tool that cannot resolve a board refuses; it does not guess. - Other repos call
dock-of; they do not re-read the file.
- A new Driver's workflow id is
driver-<dock>, ordriver-<dock>.<group-key>when its task names a group (as amended 2026-10-01), started withid_conflict_policy=FAIL. The Temporal server then refuses a second Driver on the same dock and group (§11); a whole-dockdriver-<dock>excludes every group on its dock, and any group excludes a whole-dock Driver. "Who is driving this dock" is a describe ofdriver-<dock>plus a list of the runningdriver-<dock>.*(§11b, as amended). Runs already going underop-task-*ordriver-task-*keep their ids, because a workflow id cannot change. Every lookup tries three ids in order:driver-<key>, the task's own dock key fromdock-of:<dock>, or<dock>.<group>when it names a group (as amended)driver-task-<task>op-task-<task>
- A Driver is found by its session id (
task.jsondriverSid), never by tmux name or roster name. Names are labels for people. - The label is
driver-<dock>, fixed before anything keys on it.- Door 1 creates the tmux session and the roster name as
driver-<dock>, beforeccswitch exec, because ccswitch keys a session's credential directory on its tmux name. - Door 2 cannot rename its tmux session safely. It keeps its old name and loses nothing, because of decision 4.
- Door 1 creates the tmux session and the roster name as
- Door 2 ends in the same state as door 1:
- The dock is recorded.
- The watcher is armed under
driver-<dock>. - The finish gate is on by default for every Driver task, and its board comes from
dock-of. Today it checks a board only if an optional<loop>/FINISHfile exists (drive-gate.py705–739). A respawn does not carry that file (09-13), settings never had one, and page-speed's was switched off on 09-15 for 14 days. - Turning the gate off is a dated switch that expires, and
driver-briefshows it while it is on. It is never a renamed file that nobody reads. - The Driver is covered by the Supervisor's waker (ADR-0035 decision 1), so there is nothing to arm.
- The dock page shows who owns it, derived when the page is built from the same describe (§11b, part 2). It is never a field someone types.
Rejected
- A separate Driver name mapped to the dock. That is a second fact to keep true (§12b). This decision exists because the one copy of the dock that was written down was the one nothing read.
- Renaming live tmux sessions to match. ccswitch keys credentials on the tmux name, so a live rename strands the session's next account swap.
- A default board "for convenience". That default is exactly how every dock other than portfolio came to read the wrong board silently. A refusal is loud and costs one flag.
Consequences, and what happens to the four live Drivers
Nothing in the four live task directories is written.
dock-of --verifyresolves all four today, checked 2026-09-29:- page-speed and settings →
portfolio-architecture-phases, 715 rows. For settings this is correct, because settings is a group on that board. - module-switch →
gatekeeper-build, 12 rows. - homepage →
homepage-rebuild-phases, 18 rows. tighten-video-minutehas no setup record, anddock-ofsays so.
- page-speed and settings →
- Their watchers keep their
op-task-sess-*ids. They getdriver-<dock>names only when they are next spawned.
Two cases are Gera's call.
Permission modes. Cross-session messages are held when the sender's and receiver's permission modes differ. module-switch's heads-up to module-send-gate shows "not delivered to Claude (1 held). The sending session's permission mode class doesn't match this session's…". ADR-0002's agent-to-agent channel therefore fails silently across modes. Should every fleet session run in one permission mode, or should crossSessionInbound be set to accept fleet-wide? Until he answers, a message to a session in another mode is not a delivered message, and senders must check for the held notice.
Groups on one dock. settings was a second Driver on a group of the portfolio dock, running alongside page-speed on the same dock, and decision 3 would have refused it. Should one dock ever have two Drivers split by group (keyed driver-<dock>.<group>), or is it strictly one Driver per dock? Answered 2026-10-01: split by group — see Amendment 2026-10-01.
Amendment 2026-10-01 — one Driver per dock group
The answer. On 2026-09-29 this ADR asked Gera whether one dock may have two Drivers split by group. The coordinator first answered "strictly one" on his behalf. That was wrong. Gera runs three Drivers on portfolio-architecture-phases, each owning groups of its rows, and each with a criterion of the form finisher --slug portfolio-architecture-phases --by-group …:
- dock-found-rows: the group "Found along the way".
- dock-phase-rows: Phase 00–10 and the UI and CI groups.
- page-speed (
sess-propflow-ef): "Phase 11 · Page optimizations" and "Places and names".
The rule.
- Watcher id. A Driver whose task names a group runs as
driver-<dock>.<group-key>. A dock slug has no dot, so the key is unambiguous. - Same group: refused. A second Driver on the same dock and group is refused by the server (
id_conflict_policy=FAIL, with a memo naming the task), exactly as decision 3 refused a second whole-dock Driver. - Whole dock vs groups: mutually exclusive. A whole-dock
driver-<dock>excludes every group on its dock, and any group excludes a whole-dock Driver.- When arming a group, the check is a strongly consistent describe of
driver-<dock>. - When arming a whole dock, the check is a list of the running
driver-<dock>.*watchers. Temporal's visibility index lags a start by seconds, so two arms racing inside that lag can both pass, and nothing else closes that window.
- When arming a group, the check is a strongly consistent describe of
- Unchanged. Distinct group keys on one dock run side by side. The lease holder is per task (
driver-<dock>.<group>#<task>), so claims stay fenced per Driver.
Where the group comes from: dock-of stays the one resolver (decision 2). dock-of <task> --group:
- The setup record's key wins. That is a
groupon the finish-line row, set withdriver-setup --task <t> --set group=<key>. - Otherwise, a criterion that names exactly one group (
… --by-group shows 0 open rows in <Group>; …) keys that group's name as a slug, e.g.found-along-the-way. - "No group recorded" (exit 2) means the whole dock.
- Cannot tell (exit 3) covers a criterion naming several groups with no recorded key, or an unreadable record. It is never read as the whole dock, because a whole-dock Driver would refuse every group Driver on it. A re-arm refuses (could-not-ask) until a key is recorded.
Known limit. Exclusion is by key equality. Two keys whose group sets overlap (one lane recording page-speed while another task's criterion names "Places and names" alone) are not caught. Keys are declared per lane in setup, and overlapping them is a setup error that a human must see.
Finding the Drivers.
- A task's own Driver. Every door asks for one task's watcher, so the lookup above is per task. Its first step is the task's own
watcher.json(ADR-0035 d4), which names whichever id runs, group or not. Only after that does it fall back to the task's key fromdock-of. - Every Driver on a dock (§11b) is a describe of
driver-<dock>plus a list of the runningdriver-<dock>.*.
Exit 3 is tested.
- local-bin
tests/dock-of-groups.pypins it: several groups and no key give exit 3 and never "no group recorded". - agent-smith
tests/test_driver_dock_groups.pypins how an arm reads it: exit 3, an olderdock-ofwithout--group, and a key that isn't a slug are each could-not-ask (DockUnresolvable, exit 4 at arming). None of them is ever a whole-dock id.
Build:
- gera-propflow/local-bin#319, merged and live:
dock-of --group,driver-setup --set group=, anddriver-brief. - PropFlow-Technologies/agent-smith#690: the group key in the arming door and the cross-key exclusion. There is no workflow change: a dotted id is already dock-keyed, so no patch gate is needed.