/* * `coord_away` — DAVID IS AWAY AND THE AIDE LEADS. * * REDEFINED (David, 2026-08-30). The verb used to mean "the coordinator stepped * out and a duty officer keeps its seat warm". That meaning is RETIRED, not * deprecated: same skill label, one meaning. A verb with two meanings is read * as whichever one the reader already had in mind, and the two differ on who * holds authority — the most expensive thing to be ambiguous about. * * WHAT IT NOW MEANS: David is away, and the aide LEADS in his absence. That is * a grant of DECISION authority, not of execution authority, and the boundary * is the feature: * * DECIDES planning · priority · queue curation · roadmap · canon · releases * under standing publish authorisation. * NEVER merges, gates, or takes a code lane. Unchanged by the grant — a * lead is not a second worker, and the aide's nevers are what keep * the gate non-author. * PARKS licence · funding · public-vs-private · credentials · anything * altering scope or authority · destructive machine actions. * * WHY PARK RATHER THAN REFUSE-AND-FORGET: a parked decision is one David * returns to. The whole point of leading in an absence is that the absence ends, * and the fleet should hand back a DIFF rather than a transcript — which is why * `decisionLog` is required to turn this on rather than being a nice-to-have. * * THE PRECONDITION IS MEASURED COVERAGE, NOT A RUN. Going away is a promise * that the fleet is observed while nobody is watching, and the fleet was * measured making that promise with a stall clock that had been stopped for * eight hours — and then, once started, reporting `checked 3 · measurable 0`. * "The clock ran" and "the fleet is observed" are different facts. So the * precondition asks what the clock could actually SEE, and going away blind is * a deliberate, recorded act rather than a silent default. */ import { existsSync, readFileSync, writeFileSync, mkdirSync } from "node:fs"; import path from "node:path"; import { z } from "zod"; import { ROOT } from "../store.js"; import { stallCheckTool } from "./stall.js"; const awayFile = () => path.join(ROOT, "coord-away.json"); /** * WHAT THE LEAD MAY NOT DO, enforced rather than trusted to a card line. * * ALLOWLIST FOR THE OLD MEANING, DENYLIST FOR THIS ONE — and the asymmetry is * deliberate rather than a relaxation. A duty officer's grant was NARROW ("keep * claimed work moving"), so absence had to mean refusal or every unlisted tool * was a silent grant. A lead's grant is WIDE by design: it decides planning, * priority, curation, roadmap, canon and releases, which is most of the bus. An * allowlist over a wide grant would have to enumerate nearly every tool, and the * one someone forgets would refuse legitimate work in the middle of an absence — * failing in the direction that leaves the fleet stuck with nobody to ask. * * What is refused is small, closed, and named for its reason: the aide's * `never merges / never gates` rule. That rule does not widen when David leaves; * it is load-bearing precisely because the gate must stay non-author. */ export const LEAD_REFUSED: Record = { merge: "merging is the gate acting. The aide never gates, and leading in David's absence does not change who may — a lead that merges is an author gating its own plan.", land: "`land` writes docs/DONE.md, which is the coordinator's file. Leading does not transfer file ownership; ask the coordinator to record it.", }; /** * Decisions that WAIT for David. Not refused because they are dangerous to * execute — refused because they are not the aide's to make, and an absence is * not a transfer of that authority. */ export const PARKED_CATEGORIES = [ "licence", "funding", "public-vs-private", "credentials", "anything altering scope or authority", "destructive machine actions", ] as const; /* * ⟨q-5a71fc3b⟩ — THE THRESHOLD, NAMED. `coord_away` armed a fleet that was 3/5 * blind, silently: the refusal only fired at ZERO measurable lanes, so "2 of * 5" — three seats the clock could not see — read as covered enough. A * partially blind fleet IS the incident, not a lesser case of it: the seats * nobody can see are exactly the ones an absence leaves unattended. So the * threshold is every in-flight lane the clock scores, the refusal names the * seats short of it, and `acknowledgeBlindFleet` is the only way past. */ export const COVERAGE_THRESHOLD = "EVERY in-flight lane the clock scores must be measurable"; /* * ⟨q-ce585029⟩ — A QUERY REPORTS THE COVERAGE MEASURED AT ARM TIME, AND SAYS * SO. "Coverage 2/5" read on 09-11 was three days old by the time it was * quoted as current. The query stays free (no re-measure); the summary * carries the reading's age and labels it stale past this many minutes. */ export const COVERAGE_STALE_MINUTES = 60; /** * ⟨q-1c95f7d4⟩ 5.4 — the tick's own sentence for the announcement and the refusal. A fleet * with NO tick source says so (that is this fleet today: six tmux seats, zero herdr), and a * fleet with a source that answered for nobody says BLIND rather than saying nothing. */ export const tickSentence = (t?: AwayTick): string => { if (!t || t.seats === 0) return " No external tick: no seat has an observer to ask, so every lane above was measured from its branch and the board alone."; if (t.readable === 0) return ` External tick BLIND: ${t.seats} seat(s) have an observer and none answered — a source that cannot be read is not a quiet fleet.`; const states = Object.entries(t.states).map(([k, v]) => `${v} ${k}`).join(", "); return ` External tick: ${t.readable}/${t.seats} seat(s) answered${states ? ` (${states})` : ""} — evidence about the seats, beside the branch axes, never instead of them.`; }; export const fmtAge = (ms: number): string => (ms < 60_000 ? `${Math.max(0, Math.round(ms / 1000))}s` : ms < 3_600_000 ? `${Math.floor(ms / 60_000)}m` : ms < 86_400_000 ? `${Math.floor(ms / 3_600_000)}h` : `${Math.floor(ms / 86_400_000)}d`); export function coverageAgeOf(coverage: { at: string }, now = Date.now()): { ageMs: number | null; stale: boolean; label: string } { const at = Date.parse(coverage.at); if (!Number.isFinite(at)) return { ageMs: null, stale: true, label: "measured at an unreadable time — treat as stale" }; const ageMs = Math.max(0, now - at); const stale = ageMs > COVERAGE_STALE_MINUTES * 60_000; return { ageMs, stale, label: `measured ${fmtAge(ageMs)} ago${stale ? ` — STALE (older than ${COVERAGE_STALE_MINUTES}m; re-arm or run stall_check for a current reading)` : ""}` }; } /** Met when nothing scored is unseen — an empty board (0 of 0) is idle, not blind, and needs no special case. */ export const meetsCoverageThreshold = (c: { checked: number; measurable: number }): boolean => c.measurable >= c.checked; /** * ⟨q-1c95f7d4⟩ Task 5.4 — WHAT THE EXTERNAL TICK CONTRIBUTES TO THE AWAY PRECONDITION. * * The precondition asks what the clock could actually SEE. A seat observed from OUTSIDE * this process — herdr answering `idle | working | blocked` for its pane — is seen in a way * a pid and a branch cannot manage: it is the only signal here that separates a thinking * lane from a wedged one. So a readable tick counts as measured, through `stall_check`'s * own coverage arithmetic, and nothing is special-cased here. * * ⛔ WHAT IS NOT DONE, DELIBERATELY: a tick NEVER raises coverage for a seat the branch * axes could not measure into "observed enough to go away on". It credits the seat it read * and nothing else, and a fleet with a tick source that answered for NOBODY is reported as * blind — `0 of N readable` is the blind case this verb exists to refuse, not a quiet one. */ export type AwayTick = { seats: number; readable: number; states: Record; note: string }; export type AwayCoverage = { checked: number; measurable: number; blind: string[]; /** ⟨q-1c95f7d4⟩ the external tick behind this coverage number, named so the announcement can say what watched the fleet. */ tick?: AwayTick; /** ⟨q-5d1c8e04⟩ — standing seats on the board: present, not scored, and therefore not blind. */ roles?: number; /** Lanes that declare no per-agent branch: out of the population by their own statement. */ deliberate?: number; /** ⟨q-3d82f1a9⟩ — the board could not be read: rows present vs parsed, as stall_check stated it. */ unreadable?: { rowsPresent: number; rowsParsed: number; why: string }; at: string; }; export type AwayState = { on: boolean; project: string; /** Who is away. The verb is about DAVID's absence now. */ away: string; /** The agent leading in that absence — the aide. */ leadId: string; until?: string; at: string; /** Where decisions taken during the absence are written, so David returns to a diff. */ decisionLog: string; coverage: AwayCoverage; /** True when this was turned on knowing the fleet is unobserved. */ blindAcknowledged?: boolean; }; export function readAway(): Record { const f = awayFile(); if (!existsSync(f)) return {}; try { return JSON.parse(readFileSync(f, "utf8")) as Record; } catch { return {}; } } function writeAway(state: Record): void { mkdirSync(ROOT, { recursive: true }); writeFileSync(awayFile(), `${JSON.stringify(state, null, 2)}\n`); } /** * The refusal the LEAD gets, or null if the call is allowed. * * Only the lead is constrained, and only on the two verbs above. Everyone * else's lane is untouched: `coord_away` is not a freeze on the project, and a * worker whose tools stopped working because David went out would simply stop * calling them. */ export function awayRefusal(state: Record, agentId: string | undefined, tool: string): string | null { if (!agentId) return null; const held = Object.values(state).find((s) => s.on && s.leadId === agentId); if (!held) return null; const why = LEAD_REFUSED[tool]; if (!why) return null; return ( `'${tool}' is refused while you LEAD on '${held.project}' in David's absence: ${why} ` + `Leading grants DECISION authority — planning, priority, curation, roadmap, canon, releases — not execution authority. ` + `If this genuinely needs doing, it needs the coordinator, or David back. Record it in ${held.decisionLog} either way.` ); } /** * RETIRED WITH THE OLD MEANING. Kept as a no-op so the call site in * `registerTool` keeps compiling and the retirement is visible where the guard * used to be, rather than as an absence someone re-derives later. * * Under the old meaning the coordinator was the one away, so a second * coordinator joining was a fleet-shape change made behind its back. Under this * one the coordinator is present and working, and blocking a second coordinator * while DAVID is out would refuse a legitimate handoff at exactly the moment * nobody can approve it. * * RECORDED GAP, deliberately not solved here: retiring the duty-officer meaning * leaves COORDINATOR absence uncovered. If the coordinator goes down while David * is away, nothing covers the seat. That is a decision, not an oversight — it is * named so the next reader finds a choice rather than a hole. */ export function secondCoordinatorRefusal( _state: Record, _agentId: string, _roleId: string | undefined, ): string | null { return null; } export const coordAwaySchema = { project: z.string().min(1), // Omitted → a QUERY: report current state and mutate nothing (q-1abbbdbf). // `/coord-away` used to be a bare toggle read by a human deciding which // direction to call it — the incident this fixes is David typing it // meaning ARM while it was already armed, and the aide independently // reading the same typed command as RELEASE, with no state visible to // either of them before acting. State-it-aloud-first only works if there // is a state to read that does not itself flip the switch. on: z.boolean().optional(), /** The agent leading in David's absence — the aide. */ leadId: z.string().optional(), /** Repo-relative path where decisions taken during the absence are written. */ decisionLog: z.string().optional(), until: z.string().optional(), /** Repo to measure stall coverage against. */ repo: z.string().optional(), /** Turn it on knowing the fleet is unobserved. Recorded in the state. */ acknowledgeBlindFleet: z.boolean().optional(), /** Who is calling — the lead may turn it on and release it. */ agentId: z.string().optional(), }; async function measureCoverage(repo?: string): Promise { if (!repo) return null; try { const r = (await stallCheckTool({ repo })) as unknown as { ok?: boolean; checked?: number; measurable?: number; blind?: string[]; roles?: unknown[]; deliberate?: unknown[]; tick?: { seats?: unknown[]; readable?: number; states?: Record; note?: string }; boardParse?: { readable: boolean; rowsPresent: number; rowsParsed: number; why: string }; }; // ⟨q-3d82f1a9⟩ — AN UNREADABLE BOARD IS A NAMED STATE, not "could not measure". // The clock refused to answer because the board broke; arming on it would // promise a watched fleet over a table nobody can read. if (r?.boardParse && !r.boardParse.readable) { return { checked: 0, measurable: 0, blind: [], roles: 0, deliberate: 0, unreadable: { rowsPresent: r.boardParse.rowsPresent, rowsParsed: r.boardParse.rowsParsed, why: r.boardParse.why }, at: new Date().toISOString(), }; } if (!r?.ok) return null; return { checked: r.checked ?? 0, measurable: r.measurable ?? 0, blind: r.blind ?? [], tick: r.tick ? { seats: r.tick.seats?.length ?? 0, readable: r.tick.readable ?? 0, states: (r.tick.states ?? {}) as Record, note: r.tick.note ?? "" } : undefined, roles: r.roles?.length ?? 0, deliberate: r.deliberate?.length ?? 0, at: new Date().toISOString(), }; } catch { return null; } } export async function coordAwayTool(args: { project: string; on?: boolean; leadId?: string; decisionLog?: string; until?: string; repo?: string; acknowledgeBlindFleet?: boolean; agentId?: string; }) { const state = readAway(); const prior = state[args.project]; // QUERY — `on` omitted. Reads, never writes: no coverage measurement, no // refusal, no side effect. This is the call a card makes to state current // status aloud BEFORE deciding which direction to arm/release, so the // decision is never made blind the way it was on 2026-08-30/31. if (args.on === undefined) { if (!prior?.on) { return { ok: true as const, query: true as const, project: args.project, armed: false, state: prior ?? null, summary: prior ? `'${args.project}' is NOT armed — last released ${prior.at}.` : `'${args.project}' is NOT armed — never armed.`, }; } return { ok: true as const, query: true as const, project: args.project, armed: true, state: prior, coverageAge: coverageAgeOf(prior.coverage), summary: `'${args.project}' is ARMED — '${prior.leadId}' leads since ${prior.at}` + `${prior.until ? ` until ${prior.until}` : ""}. ` + `Coverage ${prior.coverage.measurable}/${prior.coverage.checked} lane(s) ${coverageAgeOf(prior.coverage).label}` + `${prior.coverage.roles ? ` · ${prior.coverage.roles} role(s) present, not scored` : ""}` + `${prior.blindAcknowledged ? " (ACKNOWLEDGED BLIND)" : ""}. ` + `Decisions logged to ${prior.decisionLog}.`, }; } if (args.on) { // A LEAD WITH NO NAME IS AN ABSENCE DESCRIBED AS COVERED, which is strictly // worse than an absence everyone can see is uncovered. if (!args.leadId) { return { ok: false as const, error: `coord-away ON requires a leadId — the agent leading in David's absence. Turning it on without naming one reports the fleet as led while leaving it unled.` }; } if (!args.decisionLog) { return { ok: false as const, error: `coord-away ON requires a decisionLog path. The point of leading through an absence is that the absence ENDS: ` + `David should return to a diff he can read, not a transcript he has to reconstruct. A log promised at the start ` + `and written at the end is a log nobody wrote.`, }; } if (prior?.on) { return { ok: false as const, error: `coord-away is already ON for '${args.project}' — '${prior.leadId}' leads since ${prior.at}. Release it before re-arming.` }; } // THE PRECONDITION: what could the clock actually SEE? const coverage = await measureCoverage(args.repo); if (!coverage) { return { ok: false as const, error: `cannot measure stall coverage for '${args.project}'${args.repo ? "" : " — pass 'repo'"}. ` + `Going away is a promise that the fleet is watched while nobody is watching, and an UNMEASURED promise ` + `is the one this verb exists to stop making. Fix the measurement, or pass acknowledgeBlindFleet:true to ` + `record that the promise is being made anyway.`, }; } // ⟨q-3d82f1a9⟩ — REFUSED OUTRIGHT, and not acknowledgeable: a blind fleet is // one the clock can see and cannot measure; an unreadable board is one the // clock cannot even see. "0 of 0" here is not idle, and no flag makes it so. if (coverage.unreadable) { return { ok: false as const, error: `REFUSING: docs/WORKSTREAMS.md is UNREADABLE — ${coverage.unreadable.rowsPresent} row(s) present in the Active Streams table, ` + `${coverage.unreadable.rowsParsed} parsed. ${coverage.unreadable.why} ` + `Away mode promises a watched fleet; a board the clock cannot read is not a quiet fleet, it is an unwatched one. Fix the row and re-arm.`, }; } // `checked > 0` MATTERS: an empty board is an IDLE fleet, not a blind one. // Without it, arming while nothing is in flight refuses with "the clock // covers 0 of 0" — a refusal that reads as a fleet nobody can see when in // fact there is nothing to see, and the only way past it would be to // acknowledge a blindness that does not exist. // ⟨q-5a71fc3b⟩ — below the threshold, not only at zero: "2 of 5" refuses. if (!meetsCoverageThreshold(coverage) && !args.acknowledgeBlindFleet) { return { ok: false as const, error: `REFUSING: the stall clock covers ${coverage.measurable} of ${coverage.checked} in-flight row(s); the threshold is that ${COVERAGE_THRESHOLD} ` + `(${coverage.checked - coverage.measurable} short)${coverage.blind.length ? ` — blind on: ${coverage.blind.join(", ")}` : ""}. ` + `${coverage.measurable === 0 ? "It would RUN and see nothing. " : "The seats it cannot see are exactly the ones an absence leaves unattended. "}` + `"The clock ran" and "the fleet is observed" are different facts, and this verb must not treat the first as the second. ` + `The usual cause is a board 'Branch · Worktree' cell holding a PATH rather than a branch ref: a path resolves for ` + `git and measures the wrong thing, so the check reports it unmeasurable rather than guessing. Fix those cells and ` + `coverage returns. If you mean to go anyway, pass acknowledgeBlindFleet:true — it is recorded in the state and in the announcement.` + tickSentence(coverage.tick), }; } state[args.project] = { on: true, project: args.project, away: "david", leadId: args.leadId, until: args.until, at: new Date().toISOString(), decisionLog: args.decisionLog, coverage, ...(!meetsCoverageThreshold(coverage) ? { blindAcknowledged: true } : {}), }; writeAway(state); return { ok: true as const, state: state[args.project], refused: LEAD_REFUSED, parked: PARKED_CATEGORIES, // A confirmation naming the transition, not just the imperative // direction (q-1abbbdbf) — "ON" and "already ON" read the same at a // glance; "OFF → ARMED" cannot be misread as a no-op or a repeat. transition: "OFF → ARMED" as const, announce: `OFF → ARMED. AGENT_ACTION: coord-away ON — David is away${args.until ? ` until ${args.until}` : ""}; '${args.leadId}' LEADS. ` + `Decides: planning · priority · curation · roadmap · canon · releases under standing authorisation. ` + `Never: merges, gates, or takes a code lane. Parks for David: ${PARKED_CATEGORIES.join(" · ")}. ` + `Decisions logged to ${args.decisionLog}. ` + `Stall coverage ${coverage.measurable}/${coverage.checked}${!meetsCoverageThreshold(coverage) ? ` — ACKNOWLEDGED BLIND${coverage.measurable === 0 ? ": the clock runs and sees nothing" : ""}${coverage.blind.length ? ` on: ${coverage.blind.join(", ")}` : ""}` : ""}.` + tickSentence(coverage.tick), }; } if (!prior?.on) return { ok: false as const, error: `coord-away is not on for '${args.project}' — nothing to release.` }; // RELEASE IS OBSERVING THAT DAVID IS BACK, not lifting a limit on yourself — // which is why the lead may do it and a duty officer could not. The parked // decisions are NOT unparked by it: they were never the lead's to make, and // release does not retroactively authorise one. state[args.project] = { ...prior, on: false, at: new Date().toISOString() }; writeAway(state); return { ok: true as const, state: state[args.project], transition: "ARMED → RELEASED" as const, announce: `ARMED → RELEASED. AGENT_ACTION: coord-away RELEASED on '${args.project}' — David is back; '${prior.leadId}' stands down as lead. ` + `Anything parked during the absence is still parked: release does not authorise it. Decisions taken: ${prior.decisionLog}.`, }; }