tickmarkr 1.92.1 → 1.93.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tickmarkr",
3
- "version": "1.92.1",
3
+ "version": "1.93.0",
4
4
  "description": "Spec in, verified work out.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -19,12 +19,13 @@ When working in a multi-agent terminal environment, decide your role before star
19
19
  - **Claude Code:** `herdr agent start orchestrator --kind claude --pane <root-pane-id> -- --permission-mode bypassPermissions`
20
20
  - **Codex:** `herdr agent start orchestrator --kind codex --pane <root-pane-id> -- --dangerously-bypass-approvals-and-sandbox` — the unsandboxed flag is REQUIRED, not optional: codex's `workspace-write` sandbox keeps `.git` refs read-only, so a sandboxed orchestrator's `tickmarkr run` dies at integration-branch creation (`git worktree add` cannot lock the ref). Do not downgrade this flag; the herdr pane and repo scope are the containment.
21
21
  - **Auxiliary agents you spawn (consultants, reviewers, scouts) follow the same forms.** Never launch a claude session in plan mode or default permission mode for autonomous work — both stall on per-command approval prompts nobody is watching; claude is always `--permission-mode bypassPermissions` (tickmarkr's own adapter uses exactly this for workers, judges, and consults). A read-only codex consultant may use `--sandbox read-only`; any codex session that must touch git needs the unsandboxed flag above.
22
+ - **Auxiliary seats run as the CLI's interactive TUI in their visible pane — never headless** (`claude -p` / `codex exec`): headless buffers output until exit so the pane renders idle for the entire run, is blind to SessionStart hook errors (a broken and a fixed hook both return green), and gives a stall watcher no midpoint — silent-time equals lifetime. Headless is for exit-code probes only (a quota check that wants `rc`), never for work anyone must watch.
22
23
 
23
24
  Outside a multi-agent terminal environment, run the loop directly.
24
25
 
25
26
  ## Stand-down (mission end and retirement)
26
27
 
27
- On each mission's terminal state, after the record commit and operator notification: the orchestrator stops every monitor and background task it started, prints one final stand-down line, and leaves nothing queued in its input box. A finished session with an armed watcher or pre-filled input is a loaded gun.
28
+ On each mission's terminal state, after the record commit and operator notification: the orchestrator stops every monitor and background task it started, sweeps the heartbeat/beat files its watchers wrote (a stale beat beside a live one reads as coverage to whoever globs the directory), prints one final stand-down line, and leaves nothing queued in its input box. A finished session with an armed watcher or pre-filled input is a loaded gun.
28
29
 
29
30
  ## Invariants
30
31
 
@@ -18,6 +18,7 @@ When working in a multi-agent terminal environment, decide your role before star
18
18
  - **Claude Code:** `herdr agent start orchestrator --kind claude --pane <root-pane-id> -- --permission-mode bypassPermissions`
19
19
  - **Codex:** `herdr agent start orchestrator --kind codex --pane <root-pane-id> -- --dangerously-bypass-approvals-and-sandbox` — the unsandboxed flag is REQUIRED, not optional: codex's `workspace-write` sandbox keeps `.git` refs read-only, so a sandboxed orchestrator's `tickmarkr run` dies at integration-branch creation (`git worktree add` cannot lock the ref). Do not downgrade this flag; the herdr pane and repo scope are the containment.
20
20
  - **Auxiliary agents you spawn (consultants, reviewers, scouts) follow the same forms.** Never launch a claude session in plan mode or default permission mode for autonomous work — both stall on per-command approval prompts nobody is watching; claude is always `--permission-mode bypassPermissions` (tickmarkr's own adapter uses exactly this for workers, judges, and consults). A read-only codex consultant may use `--sandbox read-only`; any codex session that must touch git needs the unsandboxed flag above.
21
+ - **Auxiliary seats run as the CLI's interactive TUI in their visible pane — never headless** (`claude -p` / `codex exec`): headless buffers output until exit so the pane renders idle for the entire run, is blind to SessionStart hook errors (a broken and a fixed hook both return green), and gives a stall watcher no midpoint — silent-time equals lifetime. Headless is for exit-code probes only (a quota check that wants `rc`), never for work anyone must watch.
21
22
 
22
23
  Outside a multi-agent terminal environment, run the loop directly.
23
24
 
@@ -60,7 +61,7 @@ When spawning consultants (agents gathering synthesis input for decisions like S
60
61
 
61
62
  ## Stand-down (mission end and retirement)
62
63
 
63
- - **Orchestrator, on terminal state** (green, failed, or parked), after the record commit and operator notification: stop every monitor and background task you started, print one final stand-down line, and leave NOTHING queued in your input box. A finished session with an armed watcher or pre-filled input is a loaded gun — a retired v1.40 orchestrator sat idle with "merge … tag, publish" unsent in its input; one stray Enter would have shipped a duplicate release.
64
+ - **Orchestrator, on terminal state** (green, failed, or parked), after the record commit and operator notification: stop every monitor and background task you started, sweep the heartbeat/beat files your watchers wrote (a stale beat beside a live one reads as coverage to whoever globs the directory), print one final stand-down line, and leave NOTHING queued in your input box. A finished session with an armed watcher or pre-filled input is a loaded gun — a retired v1.40 orchestrator sat idle with "merge … tag, publish" unsent in its input; one stray Enter would have shipped a duplicate release.
64
65
  - **Supervisor, when a mission completes** (and always before spawning the next orchestrator): verify the orchestrator stood down, then close its tab. Seeming input-box text in a retired pane can be the TUI's dim ghost-text suggestion, not queued input — confirm with an ANSI read (dim escape around the text) or type-one-char-and-read-back before treating it as the loaded gun; close the tab either way. The journal, execution record, OBS ledger, and memory hold the story; pane scrollback is disposable. Never leave a retired agent idle with watchers armed.
65
66
 
66
67
  ## The loop
@@ -12,6 +12,19 @@ to the user with evidence.
12
12
  The mission is the skill argument. If empty, ask the user what to run end-to-end before doing anything else.
13
13
  Requires `HERDR_ENV=1`; if unset, say so and stop.
14
14
 
15
+ **THE ENGINE IS THE DEFAULT EXECUTOR.** A mission that names a milestone, a phase, or a spec runs the
16
+ loop: `tickmarkr compile` → `plan` → `run` → `report`, and the JOURNAL is the record. `compile` ingests
17
+ GSD phase plans (`src/compile/gsd.ts`), so *"this repo uses GSD"* is not a reason to bypass it. The
18
+ supervised GSD flow (below) is the EXCEPTION: it exists only on an explicit operator order, recorded in
19
+ the brief WITH its costs named — no journal (so no per-task adapter/model record), no routing, no
20
+ enforced gate battery, no enforced cross-vendor review; each is re-implemented by hand or silently lost.
21
+ **Measured 2026-08-18 (P98):** the operator triggered this skill expecting the engine; the mission ran
22
+ as GSD legs instead — 12 seats, 11 of them one model checking that same model's work, and *"which model
23
+ ran each task"* was unanswerable from every mission artifact (no ruling, log, or brief named a model;
24
+ the answer took session-file archaeology). The flip from the engine (P89, journaled runs on disk) to
25
+ GSD legs (P92) had been RULED NOWHERE — no ledger entry decides it — and then propagated for six phases
26
+ through brief lineage. **An executor choice nobody made is still an executor choice, and it compounds.**
27
+
15
28
  ## Setup
16
29
 
17
30
  0. **Adopt before you build.** If this workspace already has a supervision hierarchy — an
@@ -22,11 +35,17 @@ Requires `HERDR_ENV=1`; if unset, say so and stop.
22
35
  status, and either ADOPT the
23
36
  existing orchestrator (updated brief, re-armed watchers) or, if the old hierarchy is dead, archive the
24
37
  stale brief and build fresh.
38
+ ⚠ **Adopting a hierarchy silently adopts its EXECUTOR CHOICE.** The P92→P98 GSD drift propagated
39
+ exactly this way: each overseer read the prior brief, reproduced "the same two-leg pattern as the
40
+ last three phases", and the unruled bypass of the engine became load-bearing through repetition.
41
+ At every adopt, re-derive the executor question — *"why is this milestone not compiled?"* — and if
42
+ the answer is not a recorded operator order, route the mission back through the engine.
25
43
  0a. **READ THE PROJECT MEMORY BEFORE YOU START — it already contains discipline you are about to re-earn.**
26
44
  `~/.claude/projects/<cwd-slug>/memory/` (slug = the absolute cwd with `/` → `-`). Read `MEMORY.md`, then
27
- `ls` the topic entries and open every one whose name concerns METHOD or DISCIPLINE rather than a shipped
28
- milestone — names like `*-discipline`, `*-drill`, `*-parity`, `*-least-permission`, `context-reset-*`,
29
- `consults-*`, `agent-*`.
45
+ `ls` the topic entries and open every one whose name concerns METHOD, DISCIPLINE, or a STANDING
46
+ OPERATOR LAYOUT/CONVENTION rather than a shipped milestone — names like `*-discipline`, `*-drill`,
47
+ `*-parity`, `*-least-permission`, `context-reset-*`, `consults-*`, `agent-*`, `*-tab-layout`,
48
+ `*-visible-*`, `*-panes*`, `user-tabs-*`.
30
49
  **Earned 2026-08-04, expensively.** That directory held `…-falsification-drill-discipline.md`, written
31
50
  three weeks earlier: *"a gate or grep-pin is assumed WRONG until a falsification drill proves it bites…
32
51
  run the drill that should redden it and SEE the red before trusting green."* That is Evidence discipline
@@ -35,8 +54,33 @@ Requires `HERDR_ENV=1`; if unset, say so and stop.
35
54
  mission-scoped brief. **A memory that exists and is never opened costs more than one that was never
36
55
  written, because everyone assumes the lesson is somewhere.** Entries may predate a project rename; search
37
56
  by concept, not by the current product name.
57
+ **And the sweep scope is itself a recorded defect: a filter limited to discipline names SKIPS the
58
+ layout canon, and that skip is paid.** The layout entry records a 2026-08-05 correction it caused —
59
+ *"widen the start-of-session read to include layout/convention entries, not only discipline ones"* —
60
+ and on 2026-08-17 the same skip put a planning seat inside the ORCH tab. A standing operator layout is
61
+ not cosmetic; it is how the operator reads the fleet, and it binds exactly like a discipline rule.
38
62
  1. Load the `herdr` skill. `herdr pane list` to map the workspace — the focused pane is yours. Rename your
39
63
  tab OVERSEER; create ONE tab ORCHESTRATOR.
64
+ **FIVE-TAB CANON (standing operator layout — corrected three times on 2026-07-27, layout approved
65
+ 2026-07-29, re-earned 2026-08-17):**
66
+ - `OVERSEER` — you, plus a visible pipeline watch pane running the SHIPPED surface (`tickmarkr ui`,
67
+ newest-run default; never a hand-rolled status loop — the operator flagged an ad-hoc shell-loop
68
+ watcher as "strange watch design"). A supervision watcher buried in an invisible background task
69
+ reads as "no watcher" to the operator even when armed.
70
+ - `ORCH` — the orchestrator, the run's watch board (split `--direction right`, ~0.6 to the board —
71
+ never `down`; the board needs full height for its task matrix), and the raw journal feed. **Nothing
72
+ else, ever: a work seat NEVER splits into the ORCH tab**, even when width arithmetic allows a second
73
+ column. Operator verbatim: *"in orch tab should be the orch and the watcher only."* Re-earned
74
+ 2026-08-17: a planning seat split beside the orchestrator, and the operator caught it, again.
75
+ - Worker/seat tabs — tickmarkr opens ONE TAB PER TASK itself; GSD-leg seats get the same treatment
76
+ (own tab, or a shared WORKERS tab), never the ORCH tab.
77
+ - `CONSULT · <topic>` — ONE shared tab for ALL consultants of a round, side-by-side splits; never one
78
+ tab per consultant; do NOT auto-close after adjudication (operator, 2026-08-09 — keep the round's
79
+ panes until the thread is confirmed finished or a successor round supersedes them).
80
+ - `REVIEW <task>` — reviewer panes.
81
+ Never multiply beyond these without asking. **Tabs you did not create are the OPERATOR'S — provenance
82
+ decides: never close, rename, reuse, or send input to one, however idle it looks.** An "idle
83
+ stale-looking" tab an overseer once swept was the operator's in-progress thinking (2026-07-14).
40
84
  **Live tab labels (standing operator rule, 2026-07-12):** on every decision or state change (role
41
85
  handoff, task done/merged, run end) rename the affected tabs — and keep labels SHORT: the role as the
42
86
  main name plus at most ONE hot-state token. Vocabulary: ORCH carries the milestone and progress
@@ -48,8 +92,9 @@ Requires `HERDR_ENV=1`; if unset, say so and stop.
48
92
  truncated brief silently drops policy. Write the full brief to `<repo>/.tickmarkr/overseer/ORCH-BRIEF.md`
49
93
  (inside the tickmarkr state dir — already self-gitignored, no exclude step needed), then send one line:
50
94
  `herdr pane run <orch> "Read .tickmarkr/overseer/ORCH-BRIEF.md and follow it exactly."` The brief MUST contain: the
51
- mission, the pane mechanics below, rules 1–2, and require a verbatim one-sentence acknowledgment of the
52
- human-checkpoint rule before anything is dispatched.
95
+ mission, the five-tab canon from step 1, the pane mechanics below, rules 1–2, the GSD-leg rules
96
+ (below) whenever the mission dispatches `/gsd:*` legs, and require a verbatim one-sentence
97
+ acknowledgment of the human-checkpoint rule before anything is dispatched.
53
98
  **⚠ HARVEST BEFORE YOU DELETE.** At mission end the brief dir goes — but a long mission accumulates
54
99
  *method guards* in that brief (how to know a thing, not what is true of this spec), and deleting them
55
100
  re-earns each one at full price on the next mission. So before removing the dir: lift every durable,
@@ -192,12 +237,68 @@ is a lossy summary nobody trusts while a clean session re-oriented from disk-ver
192
237
  good, not after. If your own context cannot be read by the watcher, say so to the operator and ask for the
193
238
  number — an unmeasured budget is not a small budget.
194
239
 
240
+ ## Supervising GSD legs — when the mission dispatches `/gsd:*` instead of `tickmarkr run`
241
+
242
+ Milestones that alternate GSD legs (`/gsd:plan-phase N`, `/gsd:execute-phase N`) under this hierarchy get
243
+ none of the engine's dispatcher, routing, or gates — every guarantee `tickmarkr run` provides has to be
244
+ demanded in the brief instead. **This path is the EXCEPTION and requires the recorded operator order
245
+ named in the default-executor rule at the top of this skill.** Five guarantees get dropped every time
246
+ they are left implicit:
247
+
248
+ 0. **A seats ledger stands in for the journal's assignment records.** Every seat spawn — orchestrator,
249
+ planner, checker, executor, verifier, consult — appends ONE JSON line to
250
+ `<state-dir>/overseer/seats.jsonl` in the same act as the spawn:
251
+ `{"ts":"<iso>","seat":"<name>","pane":"<id>","tab":"<label>","adapter":"<cli>","model":"<model>","role":"<role>","brief":"<path>"}`.
252
+ The journal answers *"which model ran each task"* in one line; without this file the answer is
253
+ session-file archaeology, and the model monoculture it would have exposed stays invisible (measured
254
+ 2026-08-18: 12 seats, no artifact naming any seat's model). ⚠ Interim per rule 27 — removal
255
+ condition: the engine runs the milestone and the journal is the record.
256
+
257
+ 1. **GSD's Agent-tool default is OVERRIDDEN — name the trap in every seat brief.** `/gsd:plan-phase` and
258
+ `/gsd:execute-phase` fan their work out to in-process Task subagents: invisible to every watcher tier,
259
+ billed to the seat's own context, visible only in the seat's status footer. Measured 2026-08-17 (P98
260
+ leg 1): one planning seat ran FIVE in-process subagents to ≈855k tokens — two of them an unexplained
261
+ duplicate respawn pair — and the operator caught it from the footer, not from any tier of supervision.
262
+ The violation was first recorded 2026-07-10, and that record already states the fix: *"generic
263
+ 'visible panes' wording is not enough — name the GSD trap."* The concrete mechanism is claude-code
264
+ TEAMMATES (the seat's footer roster; `Teammate @<name> finished` notices): they run INSIDE the
265
+ parent seat's turn, so the seat cannot drain its message queue until every teammate returns —
266
+ **unwatchable and unsteerable are the same defect** (the queued-message law, Pane mechanics).
267
+ Measured 2026-08-18: two freeze-class directives sat queued behind a planner's teammate fan-out
268
+ while the plan set they froze was still editable from that queue, and THE OPERATOR ended the turn
269
+ by hand (Esc, twice) because no seat owned the interrupt. Every GSD seat brief states: subagents
270
+ run as visible herdr panes in per-task tabs (`herdr agent start …`), and a seat report whose work
271
+ was produced by invisible subagents is rejected on read.
272
+ 2. **Seats are interactive TUI, never headless `-p`.** The P92–P96 exec lane —
273
+ `cat brief | claude -p … ` in a visible pane — satisfied visibility in the letter only: `claude -p`
274
+ buffers output until exit, so the pane renders idle for the entire run; it is blind to SessionStart
275
+ hook errors (a broken and a fixed hook both return green); and its silence has no midpoint for a
276
+ stall watcher to catch — silent-time equals lifetime. Standing operator rule since 2026-07-13:
277
+ consults and one-off LLM calls run as the CLI's real interactive TUI in a visible named pane.
278
+ Headless is for exit-code probes — a quota check that wants `rc`, never work anyone must watch.
279
+ 3. **Buy seat diversity from the live capability matrix, at every dispatch.** When one vendor's model
280
+ quota collapses, the reflex is to collapse every seat onto the surviving model and hold the
281
+ cross-vendor CLI back for a late probe — P97 ran planner, checker and verifier as one family that
282
+ way, and three same-family passes confirmed one wrong anchored conclusion with the refuting fact in
283
+ the room. `<state-dir>/doctor.json` already lists every installed+authed adapter and its models (nine
284
+ were authed on 2026-08-17 while every seat ran claude). Priority when independence is scarce:
285
+ **verifier > checker > planner > executors** — the independent seat goes cross-vendor
286
+ (`herdr agent start … --kind codex`), ruled at dispatch, never debated under time pressure.
287
+ 4. **Gate every exec lane with the shipped battery, not hand-rolled greps.**
288
+ `tickmarkr verify --base <ref> --criteria <file>` is the standalone form of the engine's own gates —
289
+ build/test/lint diffed against a recorded baseline, evidence, scope, plus the semantic judges — one
290
+ fail-closed verdict, no daemon, no retries. A per-lane grep gate re-implements a weaker version of
291
+ this and passes on source text the screen never renders, which is exactly the class the acceptance
292
+ judge exists to reject. One command per lane, named in the lane's own brief.
293
+
195
294
  ## Pane mechanics that bite
196
295
 
197
296
  - **Verified send protocol**: `herdr agent send` writes WITHOUT Enter, and `pane run`'s Enter can be swallowed
198
297
  by bracketed-paste on long payloads. Robust sequence: read the pane (bare prompt required) → send-text →
199
298
  sleep 2–3s → send-keys Enter → read back (input empty / agent `working`). Never report "briefed" without
200
- the read-back. Long content goes in a brief file, never pane text.
299
+ the read-back. Long content goes in a brief file, never pane text. `scripts/seat-send.sh` encodes
300
+ this whole path — size guard, atomic prompt, prompt-line read-back, optional interrupt — and never
301
+ auto-resends.
201
302
  **PROBE THE READ-BACK WITH THE SHORTEST DISTINCTIVE TOKEN — a commit hash, a pid, an OBS id — NEVER a
202
303
  sentence.** A long phrase crosses the pane's render wrap boundary, so grepping for it returns zero on a
203
304
  message that arrived intact, and **a badly-probed successful send is byte-identical to a truncated one.**
@@ -206,6 +307,35 @@ number — an unmeasured budget is not a small budget.
206
307
  (OBS-396): a grep for the full sentence returned 0 while a grep for one word of the same sentence
207
308
  returned 1. This trap lives *inside* the verification step above, which is why it survives — the rule
208
309
  that is supposed to catch dropped sends is the rule that manufactures the phantom.
310
+ **AND A CONTENT PROBE — TOKEN, TAIL, FULL TEXT — CANNOT VERIFY DELIVERY AT ALL, only presence.**
311
+ Measured 2026-08-18: `herdr pane read` includes the INPUT BOX, so an unsubmitted message renders
312
+ identically to a submitted one and every content-based probe returns the same answer in both
313
+ states. A freeze hold was "verified delivered" by three independent content methods and had never
314
+ been submitted; the receiving seat stopped 19 minutes later without ever reading it — the frozen
315
+ set was protected by nothing but the operator's manual Esc. **The prompt line is the only state
316
+ that discriminates** — text sitting on `❯` is exactly what will not run — and a delivery report
317
+ is prompt-line state alone, or nothing: a decorative check beside a real one reads as
318
+ corroboration (two greens, one of which was never capable of disagreeing).
319
+ - **A MESSAGE TO A WORKING SEAT IS A QUEUED MESSAGE, AND THE QUEUE DRAINS ONLY AT TURN BOUNDARIES.**
320
+ Delivery is not arrival: `agent prompt` to a `working` claude seat lands in its queue (`Press up to
321
+ edit queued messages` on the seat's prompt line is the tell) and is READ only when the current turn
322
+ ends — and with in-process teammates a turn runs 20–40 minutes, so steering latency equals subagent
323
+ runtime. Measured 2026-08-17/18 (P98 leg 1): a FREEZE HOLD and a checker-release directive stacked
324
+ behind a planner's teammate fan-out — the freeze forbade edits its own queue could still trigger —
325
+ and the OPERATOR ended the turn by hand. Three consequences:
326
+ - **A queued hold is not a hold.** A freeze-class or superseding directive to a `working` seat is
327
+ delivered by INTERRUPT, and the interrupt is the SUPERVISING tier's move, never left to the
328
+ operator: `send-keys esc` → re-read status → esc once more if still working (two, bounded) →
329
+ verify idle → prompt → verify. The interrupt loses the seat's in-flight step; for a
330
+ correctness-class directive that is the price, and it is cheaper than a voided verdict.
331
+ - **Never stack a correction behind a stale directive.** A queued message executes in a context that
332
+ no longer matches the one it was written in. When conditions change, do not append another
333
+ message — interrupt, then send ONE directive that NAMES the queue and overrides it (*"your queue
334
+ holds X and Y; act on neither; current state is Z"*), and require the seat to report what its
335
+ queue held before acting. The receiving seat re-validates every queued item against CURRENT state.
336
+ - Five distinct send failures in one leg — front-truncation, sitting unsubmitted, two silent losses,
337
+ a probe that mistook its own echo for a reply — is what a prose send protocol costs under load:
338
+ run `scripts/seat-send.sh` instead.
209
339
  - **Guard-before-Enter** (race-safe prompt answering): chain with `&&` — pane get shows `blocked` && pane
210
340
  read shows the expected option under the cursor && only then send-keys. If no longer `blocked`, someone
211
341
  already answered; do nothing.
@@ -235,6 +365,15 @@ number — an unmeasured budget is not a small budget.
235
365
  unaffected (one more reason to prefer them).
236
366
  - Stale typed input is unclearable via CLI — supersede it:
237
367
  `pane run "<-- disregard everything before this arrow (stale draft). ACTUAL: <message>"`.
368
+ **But DISCRIMINATE before you supersede or file it: text on an idle seat's prompt line has FOUR
369
+ authors** — the seat's own draft, an operator, another agent's `agent send` (writes WITHOUT Enter),
370
+ and claude-code's AUTOSUGGEST, which renders context-plausible ghost text BYTE-IDENTICAL to a typed
371
+ draft in text-format reads (OBS-482). The check is mechanical and only works at observation time:
372
+ `agent read --format ansi` — dim/grey SGR around the text = autosuggest ghost, NOT input. Measured
373
+ 2026-08-17 (D-206): an unattributed instruction was found in an orchestrator's box, superseded
374
+ defensively, and its origin stayed UNRESOLVED — the one probe that discriminates was not taken while
375
+ the text still sat there. An origin question you can close in ten seconds at the pane becomes
376
+ permanently open the moment anyone clears the box.
238
377
 
239
378
  ## Supervision watcher
240
379
 
@@ -324,6 +463,12 @@ nowhere.
324
463
  It wakes when every named file exists AND ends with its terminal marker, and on timeout it reports each
325
464
  file as READY / PARTIAL / ABSENT so a quiet arm still proves the watcher was alive. Tell each seat, in its
326
465
  brief, the exact marker its report must end with — you cannot watch for a marker you never demanded.
466
+ **Arm on the marker YOU demanded, verified against the FILE — never on the seat's report of its own
467
+ marker.** Measured 2026-08-17: a seat reported its sweep "ends `SWEEP-END`"; the file on disk ended
468
+ `ORDER4-END`. A watcher armed on the reported marker never fires while the artifact sits COMPLETE, and
469
+ that hang is byte-identical to a seat still working. The script prints each unfinished file's actual
470
+ last line on every timeout heartbeat — read it there, and when in doubt `tail -1` the artifact, never
471
+ the transcript's claim about it.
327
472
 
328
473
  **Arm it in the same call as the spawn, not the next one.** A watcher armed "after I finish this step"
329
474
  leaves a gap exactly as wide as however long you stay busy, and you will be busy — you just spawned work.
@@ -713,6 +858,11 @@ twice.** They are mission-independent on purpose: nothing here names a task, a l
713
858
  **Write beats to a conventional path inside the repository the other tier already reads**, one file per
714
859
  tier, and state the path when you report. Then have the reader name the tiers that are ABSENT, never
715
860
  the ones present: a list of what IS armed is producible by a seat whose watchers are all dead.
861
+ **And a beat OUTLIVES its mission unless something sweeps it.** Namespace beats per phase/leg and
862
+ sweep them at stand-down: measured 2026-08-17, a beats directory held SEVEN stale beats from prior
863
+ phases beside four live tiers, and a stale beat beside live ones reads as coverage to anyone globbing
864
+ the directory — the outliving-its-trigger failure (rule 11) in file form. Only age distinguishes a
865
+ frozen beat from a live one, so the reader states ages, and the writer removes what it retires.
716
866
  30. **A JOURNAL WATCHER ON A RESUMABLE RUN MUST SCOPE TO THE CURRENT ENGAGEMENT.** A resumed run's journal
717
867
  still contains the PREVIOUS `run-end`. A watcher that greps the whole file for its terminal event finds
718
868
  that old one immediately, concludes the run is over, and exits — on every resume, which is exactly when
@@ -0,0 +1,140 @@
1
+ #!/bin/bash
2
+ # Deliver ONE directive to a seat with VERIFIED submission — the send path that failed five distinct
3
+ # ways in one leg (2026-08-17, P98): front-truncation at the PTY (~1024B), text sitting unsubmitted,
4
+ # two silent losses, and probes that mistook presence for delivery. A prose protocol loses one step
5
+ # under load; this encodes the steps.
6
+ #
7
+ # seat-send.sh <agent-name-or-pane> <message>
8
+ #
9
+ # TKR_INTERRUPT=1 end a `working` seat's turn first (Esc, at most twice, verified between) so the
10
+ # directive is READ now instead of queued behind the turn. A message to a working
11
+ # claude seat lands in its queue and drains only at the turn boundary — with
12
+ # in-process teammates that is 20–40 minutes, so A QUEUED HOLD IS NOT A HOLD.
13
+ # Measured 2026-08-18: two freeze-class directives sat queued behind a planner's
14
+ # teammate fan-out and THE OPERATOR pressed Esc by hand because no seat owned the
15
+ # interrupt. The interrupt loses the seat's in-flight step; for a correctness-class
16
+ # directive that is the price, and it is cheaper than a voided verdict.
17
+ #
18
+ # ⚠ WHY THE READ-BACK IS THE PROMPT LINE AND NOTHING ELSE (measured 2026-08-18, three methods failing
19
+ # identically): `herdr pane read` INCLUDES THE INPUT BOX, so an unsubmitted message renders exactly
20
+ # like a submitted one and every content-based probe — token grep, tail token, full-text match —
21
+ # returns the same answer in both states. A freeze hold was "verified delivered" three ways and had
22
+ # never been submitted; the receiving seat stopped 19 minutes later without ever reading it. Only the
23
+ # PROMPT LINE discriminates: text sitting on `❯` is exactly what will not run. Prompt-line state
24
+ # alone, or nothing — a decorative check beside a real one reads as corroboration.
25
+ #
26
+ # First output line is the machine-greppable verdict:
27
+ # DELIVERED_SUBMITTED | QUEUED_BEHIND_TURN | SEND_UNSUBMITTED | INTERRUPT_FAILED | TARGET_GONE
28
+ # | REFUSED_SIZE | REFUSED_BOX_OCCUPIED
29
+ #
30
+ # This verifies SUBMISSION — the text left the input box — never that the seat understood or acted.
31
+ # Read the seat's ARTIFACT for that; a send receipt is not a response.
32
+ set -u
33
+
34
+ TARGET="${1:?usage: seat-send.sh <agent-name-or-pane> <message>}"
35
+ MSG="${2:?message required}"
36
+
37
+ # PTY input front-truncates around 1KB and a truncated brief silently drops policy — the drop is at
38
+ # the FRONT, so what survives still reads as a complete message. Long content goes in a FILE; the
39
+ # pane gets one line pointing at it.
40
+ if [ "${#MSG}" -gt 900 ]; then
41
+ echo "REFUSED_SIZE ${#MSG} bytes — write a brief file and send a one-line pointer instead"; exit 2
42
+ fi
43
+
44
+ status_of() {
45
+ herdr agent get "$TARGET" 2>/dev/null \
46
+ | sed -n 's/.*"agent_status":"\([a-z_]*\)".*/\1/p' | head -1
47
+ }
48
+
49
+ # The rendered `❯` line — the only state that discriminates submitted from sitting. head -1 returns
50
+ # the first rendered line, i.e. a PREFIX of a wrapped draft, so callers compare prefixes, never
51
+ # whole sentences (OBS-396: a full-sentence match returns false on a message that arrived intact).
52
+ prompt_line() {
53
+ herdr agent read "$TARGET" --source visible --lines 14 2>/dev/null \
54
+ | sed -n 's/^[[:space:]]*❯[[:space:]]*//p' | head -1 | sed 's/[[:space:]]*$//'
55
+ }
56
+
57
+ # Is $1 a rendered prefix of our message? (first line of a wrapped draft = prefix of MSG)
58
+ is_ours() {
59
+ case "$MSG" in "$1"*) return 0 ;; *) return 1 ;; esac
60
+ }
61
+
62
+ # GHOST DISCRIMINATOR (OBS-482): claude-code AUTOSUGGEST renders its suggestion on the prompt line
63
+ # wrapped in SGR dim; typed text renders default. A dim-wrapped line is rendering, not state.
64
+ is_ghost() {
65
+ herdr agent read "$TARGET" --source visible --lines 14 --format ansi 2>/dev/null \
66
+ | grep -F -- "❯" | grep -F -- "$1" | head -1 | grep -q "$(printf '\033')\[2m"
67
+ }
68
+
69
+ S=$(status_of)
70
+ if [ -z "$S" ]; then
71
+ echo "TARGET_GONE $TARGET — agent get returned nothing (pane closed, or the name resolves nowhere)"
72
+ exit 1
73
+ fi
74
+
75
+ # A pre-existing draft in the box is a decision, not an obstacle: `agent prompt` would APPEND to it
76
+ # and submit both. Superseding someone else's text needs an author and the arrow form — refuse and
77
+ # surface it rather than silently stomping it (D-206: an unattributed line in a box, origin never
78
+ # resolved because it was cleared).
79
+ PRE=$(prompt_line)
80
+ if [ -n "$PRE" ] && ! is_ghost "$PRE"; then
81
+ echo "REFUSED_BOX_OCCUPIED $TARGET — input box already holds: $PRE"
82
+ echo " ANSI-verify first (dim SGR = autosuggest ghost, ignorable). A real draft is superseded by a"
83
+ echo " DECISION, not by this script: send the arrow form naming what you are overriding."
84
+ exit 2
85
+ fi
86
+
87
+ QUEUED=""
88
+ if [ "$S" = "working" ]; then
89
+ if [ "${TKR_INTERRUPT:-0}" = "1" ]; then
90
+ # Esc at most twice, VERIFIED between: the first Esc can merely dismiss a UI surface or stop one
91
+ # step (the operator needed two on 2026-08-18). Unbounded Esc into an idle seat starts clearing
92
+ # state that is not yours to clear.
93
+ for _ in 1 2; do
94
+ herdr agent send-keys "$TARGET" esc >/dev/null 2>&1
95
+ sleep 3
96
+ S=$(status_of)
97
+ [ "$S" != "working" ] && break
98
+ done
99
+ if [ "$S" = "working" ]; then
100
+ echo "INTERRUPT_FAILED $TARGET still working after 2x esc — NOT sending; a directive queued now lands after the turn, which is the failure you set TKR_INTERRUPT to avoid"
101
+ exit 1
102
+ fi
103
+ else
104
+ QUEUED=1
105
+ fi
106
+ fi
107
+
108
+ # Atomic text+Enter honouring the pane's live bracketed-paste mode — never send-text + separate
109
+ # Enter, which is the "sitting unsubmitted" failure by construction.
110
+ herdr agent prompt "$TARGET" "$MSG" >/dev/null 2>&1
111
+
112
+ sleep 2
113
+ PL=$(prompt_line)
114
+ if [ -n "$PL" ] && is_ours "$PL"; then
115
+ # The text is SITTING, not sent — the swallowed-Enter shape. Submitting the existing draft is not a
116
+ # re-send (nothing is duplicated); one bounded Enter, then re-read.
117
+ herdr agent send-keys "$TARGET" enter >/dev/null 2>&1
118
+ sleep 2
119
+ PL=$(prompt_line)
120
+ fi
121
+
122
+ if [ -n "$PL" ] && is_ours "$PL"; then
123
+ echo "SEND_UNSUBMITTED $TARGET — the message is SITTING on the prompt line after send + one Enter."
124
+ echo " Do NOT re-send (it appends and submits both). Read the pane; supersede with the arrow form."
125
+ exit 1
126
+ fi
127
+
128
+ if [ -n "$PL" ]; then
129
+ # Not ours: either autosuggest ghost (dim SGR — benign) or a draft that appeared under us.
130
+ echo "DELIVERED_SUBMITTED $TARGET — prompt line clear of our text; NOTE it now holds: $PL"
131
+ echo " ANSI-verify (dim = ghost). If typed, treat per D-206: discriminate before anyone clears it."
132
+ exit 0
133
+ fi
134
+
135
+ if [ -n "$QUEUED" ]; then
136
+ echo "QUEUED_BEHIND_TURN $TARGET — submitted into a working seat's queue; it is READ at turn end. A freeze-class or superseding directive belongs behind TKR_INTERRUPT=1."
137
+ else
138
+ echo "DELIVERED_SUBMITTED $TARGET"
139
+ fi
140
+ exit 0
@@ -115,7 +115,7 @@ while :; do
115
115
  echo "WAKE: ${#ready[@]} of $# artifact(s) complete with marker '$MARKER' — ${#pending[@]} still outstanding"
116
116
  for f in ${ready[@]+"${ready[@]}"}; do echo " READY $(wc -c <"$f" | tr -d ' ') bytes sha1 $(shasum -a 1 "$f" | cut -c1-12) $f"; done
117
117
  for f in ${pending[@]+"${pending[@]}"}; do
118
- if [ -s "$f" ]; then echo " PARTIAL $(wc -c <"$f" | tr -d ' ') bytes, no marker yet $f"
118
+ if [ -s "$f" ]; then echo " PARTIAL $(wc -c <"$f" | tr -d ' ') bytes, no marker yet $f last-line: $(tail -n 1 "$f" | tr -d '\0' | cut -c1-72)"
119
119
  else echo " NOT STARTED $f <- check whether that seat is BLOCKED; a stalled seat writes nothing"; fi
120
120
  done
121
121
  exit 0
@@ -147,7 +147,10 @@ while :; do
147
147
  echo "WAKE: cap ${CAP}s reached — ${#ready[@]} of $# complete, re-arm"
148
148
  for f in ${ready[@]+"${ready[@]}"}; do echo " READY $(wc -c <"$f" | tr -d ' ') bytes $f"; done
149
149
  for f in ${pending[@]+"${pending[@]}"}; do
150
- if [ -s "$f" ]; then echo " PARTIAL $(wc -c <"$f" | tr -d ' ') bytes, no '$MARKER' yet $f"
150
+ # PARTIAL prints the file's ACTUAL last line: a seat that reports the wrong marker for its own
151
+ # artifact leaves the file complete while this watcher waits forever, and the mismatch is visible
152
+ # only here (measured 2026-08-17: demanded/reported `SWEEP-END`, written `ORDER4-END`).
153
+ if [ -s "$f" ]; then echo " PARTIAL $(wc -c <"$f" | tr -d ' ') bytes, no '$MARKER' yet $f last-line: $(tail -n 1 "$f" | tr -d '\0' | cut -c1-72)"
151
154
  else echo " ABSENT $f"; fi
152
155
  done
153
156
  exit 0
@@ -162,7 +162,7 @@ while [ "$elapsed" -lt "$CAP" ]; do
162
162
  && printf '%s' "$T" | grep -qEi -- "$AUTO_ALLOW_RE"; then
163
163
  auto=$((auto + 1))
164
164
  herdr agent prompt "$TARGET" \
165
- " <-- disregard everything before this arrow (stale unsubmitted draft, auto-detected). ACTUAL: proceed exactly as that draft said: ${T}" \
165
+ " <-- [watch-pending-input auto-supersede] disregard everything before this arrow (stale unsubmitted draft, auto-detected). ACTUAL: proceed exactly as that draft said: ${T}" \
166
166
  >/dev/null 2>&1
167
167
  echo "$(date '+%H:%M:%S') auto-superseded #${auto}: ${T}" >> "${AUTO_LOG}"
168
168
  streak=0
@@ -198,4 +198,20 @@ while [ "$elapsed" -lt "$CAP" ]; do
198
198
  esac
199
199
  done
200
200
 
201
+ # The negative alone is not evidence: a draft can arrive inside the final poll window, and a draft
202
+ # sitting in the box while the seat is WORKING is invisible to the loop above by design (working
203
+ # resets the streak). Measured 2026-08-17 (D-206): this watcher capped with "no sustained pending
204
+ # input in 1800s" while an unattributed instruction sat in the target's box, and blind-vs-timing
205
+ # could not be distinguished afterwards. So the cap line carries one FINAL unsustained read — the
206
+ # last frame of the window, whatever the seat's status — plus the styled bytes so ghost text is
207
+ # discriminable at the only moment it can be.
201
208
  echo "WATCH_CAP_REACHED $TARGET status=$(status_of) — no sustained pending input in ${CAP}s"
209
+ FINAL=$(pending_text)
210
+ if [ -n "$FINAL" ]; then
211
+ echo " final read (UNSUSTAINED — present at cap, not confirmed across polls): $FINAL"
212
+ echo " styling evidence (dim/grey SGR = autosuggest ghost, NOT a draft):"
213
+ herdr agent read "$TARGET" --source visible --lines 14 --format ansi 2>/dev/null \
214
+ | grep -F -- "$FINAL" | head -2 | cat -v | sed 's/^/ /'
215
+ else
216
+ echo " final read: input box empty"
217
+ fi