From cabc4825ef624828cf830d99099ea60117b70d0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Padez?= Date: Sat, 22 Aug 2026 17:54:47 +0000 Subject: [PATCH] a commander never arms on its first turn, and the missing paths are how it knows MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The first-turn gap is closed by a rule rather than a mechanism, and the two halves interlock instead of conflicting. A commander cannot be told its record paths on the very first turn: the transcript id is minted as that turn begins, so there is no id to key a path on. That was written up as a limitation. It is not one, because arming on the first turn is not permissible anyway — the first exchange is for agreeing what to watch and what to do about it, and a watcher armed before that agreement is armed on a guess. A wrong condition fails silently in both directions: never fires, or fires constantly, discovered days later. So the absence became the signal, and the block says so: "If you were given no paths above, you ARE on your first turn: that is what their absence means." An agent with no paths is on the turn it must not arm on, and an agent that armed anyway would be unstoppable and undeletable — the same rule reached from the other side. The rule is in both places on purpose. The block, always loaded, carries the instruction and the interlock in four lines. The Commanders skill carries why, at length, for the agent that is about to arm one. Co-Authored-By: Claude Opus 5 --- src/servers/api/command-hq/commander-block.ts | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/servers/api/command-hq/commander-block.ts b/src/servers/api/command-hq/commander-block.ts index 022ffefb..44d5eafb 100644 --- a/src/servers/api/command-hq/commander-block.ts +++ b/src/servers/api/command-hq/commander-block.ts @@ -51,10 +51,12 @@ function accountFromCommandersCwd(cwd: string): string | null { * The block, or null when this session is not a commander. * * **A brand-new commander's FIRST turn gets no block**, because there is no transcript id yet — the harness - * mints it as that turn begins, and nothing can be told a path keyed on an id that does not exist. In - * practice the first turn is the conversation about what to watch, and the loop is started on a later one, - * by which point every turn carries it. Worth knowing rather than discovering: a commander that somehow - * armed itself on its very first message would not have been told where to record. + * mints it as that turn begins, and nothing can be told a path keyed on an id that does not exist. + * + * That is not a gap, because arming on the first turn is not permitted: the first turn is for agreeing what + * to watch, and a watcher armed before that agreement is a guess. So the two facts interlock rather than + * conflict — an agent with no paths is by definition on its first turn, which is the turn it must not arm on + * anyway. The block says so explicitly, so the absence reads as a signal rather than as something missing. * * Keyed on the cwd rather than a database lookup, and deliberately: `isCommandersDir` is the same test the * sessions list, `/chat/live` and the detail route all use, so a session cannot be a commander in one place @@ -80,5 +82,10 @@ export function commanderBlock(params: CommanderBlockParams): string | null { '', 'The platform cannot find your process and cannot recover a spawned agent id after it exits, so this', 'record is the only one. Without it, deleting this commander leaves your loop running.', + '', + '**Never arm a watcher on your first turn.** The first turn is for settling what to watch and what to do', + 'about it — arming before that is arming a guess, and a wrong condition either never fires or fires', + 'constantly and is found out days later. If you were given no paths above, you ARE on your first turn:', + 'that is what their absence means. Ask, and arm once it is agreed.', ].join('\n'); }