tickmarkr 2.6.7 → 2.6.8

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.
@@ -12,8 +12,9 @@ Use the canonical loop skill's [cockpit, parked decisions and printed twins](../
12
12
  for each target: delivered views are **1 Home, 4 Run, 5 Evidence**; Fleet/Bootstrap and
13
13
  Plan/Health remain follow-ons with CLI entries. The recorded 1/3 merged, human T2, blocked
14
14
  T3 case stays PARTIAL despite tip pass. Review the Run confirmation, read back the appended
15
- `task-approved` receipt, and explicitly launch `tickmarkr resume <runId>` with the host-owned split form below when no live owner exists;
16
- approval records permission and never dispatches work itself. Non-TTY readers keep `status`,
15
+ `task-approved` receipt, and explicitly launch `tickmarkr resume <runId>` with the detached launch below when no live owner exists;
16
+ approval records permission and never dispatches work itself. A waive needs an `OOB: executed`
17
+ read at the task head (the walkthrough's record example); a static CLEAN never funds one. Non-TTY readers keep `status`,
17
18
  `report` and default-watch line output, with `--watch --plain` as the TTY fallback.
18
19
  Keep `name: tickmarkr-auto` and the installed links to canonical `skills/` intact.
19
20
 
@@ -92,26 +93,41 @@ After sending, **confirm delivery** by reading the target pane and verifying the
92
93
  1. **Prepare** — confirm the target list. Run the [binary preflight](#binary-preflight-before-compile-or-run). Check `git status`, confirm no tickmarkr run is active, and work from a non-main branch.
93
94
  2. **Compile** — run `tickmarkr compile <spec-or-directory>`. Fix source-spec defects instead of editing the generated graph.
94
95
  3. **Plan** — run `tickmarkr plan`. Review routes, capability-floor warnings, and human gates before execution.
95
- 4. **Run** — launch `tickmarkr run` using the [host-owned split form](#host-owned-daemon-and-detached-beats), and start detached beats with their run-end stop items. A watch ending the seat's turn is no watch: keep a **blocking journal consumer** alive for the run's terminal events — the shipped watcher below, or a foreground `until grep` on the run's terminal events — and ensure it is re-armed at most every twenty minutes. Never rely on a `Monitor`-only wake. Watch the run journal rather than polling agents, using the shipped watcher — `.claude/skills/tickmarkr-overseer/scripts/watch-journal.sh <state-dir>/runs 20 28800` — which takes a line baseline at arm time, then wakes ONCE on `run-end`, `task-human`, `task-failed` or `consult-verdict` and grades the run-end summary against every execution clause for you; the debt clause is yours — read CURRENT `tickmarkr status <runId>` (step 5). Re-arm after every wake. ⛔ Never `tail -F | grep -m1` (run-end is the journal's last line, so tail never notices the broken pipe and the watcher hangs forever) and never a pane-level done wait (it fires on every agent turn end, not mission end). ⚠ A bare whole-file `grep -q '"event":"run-end"'` is the trap the watcher exists to avoid: on a resume it matches the PREVIOUS run's run-end and returns instantly, so a re-armed watcher reads as coverage that does not exist. Resolve blocked interactions in the relevant agent session.
96
+ 4. **Run** — launch `tickmarkr run` using the [detached launch](#host-owned-daemon-and-detached-beats), and start detached beats with their run-end stop items. A watch ending the seat's turn is no watch: keep a **blocking journal consumer** alive for the run's terminal events — the shipped watcher below, or a foreground `until grep` on the run's terminal events — and ensure it is re-armed at most every twenty minutes. Never rely on a `Monitor`-only wake. Watch the run journal rather than polling agents, using the shipped watcher — `.claude/skills/tickmarkr-overseer/scripts/watch-journal.sh <state-dir>/runs 20 28800` — which takes a line baseline at arm time, then wakes ONCE on `run-end`, `task-human`, `task-failed` or `consult-verdict` and grades the run-end summary's execution clauses for you — `EXECUTION COMPLETE` or `NOT GREEN`, never green, because a run-end record is history; the debt clause is yours — read CURRENT `tickmarkr status <runId>` (step 5). Re-arm after every wake. ⛔ Never `tail -F | grep -m1` (run-end is the journal's last line, so tail never notices the broken pipe and the watcher hangs forever) and never a pane-level done wait (it fires on every agent turn end, not mission end). ⚠ A bare whole-file `grep -q '"event":"run-end"'` is the trap the watcher exists to avoid: on a resume it matches the PREVIOUS run's run-end and returns instantly, so a re-armed watcher reads as coverage that does not exist. Resolve blocked interactions in the relevant agent session.
96
97
  5. **Verify and consolidate** — continue only after a green run. A run is green when the run-end event exists in the journal, the tip verify is not "failed", the summary's `failed`, `human`, `blocked` and `pending` buckets are all empty, and CURRENT `tickmarkr status <runId>` reads its owed checks outstanding empty AND known (`outstanding 0`) — a run with a parked task is partial, not green. Empty execution buckets alone are not green either (D-660): every bucket empty and the tip passed, but an operator waived one review, leaves one accepted-risk review check owed — status reads `outstanding 1 (T7 review)` and `run`/`resume` still exit 0 on execution alone, so the run is execution complete, not green. It turns green only when a `tickmarkr verify --record <runId>` discharge moves CURRENT status to `outstanding 0`, including a discharge landing after run-end — the historical run-end record keeps the old count, so never read debt from it; `outstanding unknown` is never green. Tickmarkr consolidates accepted work on `tickmarkr/<runId>` and never signs off to the main branch. A human controls any later release merge.
97
98
  6. **Record** — `tickmarkr report <runId> --md` prints Markdown to stdout; redirect explicitly beside the spec (for example `tickmarkr report <runId> --md > feature.record.md`) and commit the execution record when the repository tracks those records.
98
99
  7. **Continue** — move to the next requested target. If a target fails or is parked, stop with the journal evidence rather than silently skipping it.
99
100
 
100
101
  ## Host-owned daemon and detached beats
101
102
 
102
- Launch the daemon as a SPLIT of the orchestrator's own pane in the ORCH tab, owned by the
103
- host PTY. Never launch it as an agent harness background task or in a separate tab. The daemon
104
- self-places its board from that split, keeping both daemon and board in the ORCH tab.
103
+ Launch the daemon DETACHED, with no pane of its own: one `tickmarkr run` or `tickmarkr resume <runId>`
104
+ process in its own session, its stdout and stderr appended to `<state-dir>/daemon.log`, and the
105
+ ORCH's recorded address as its board anchor, so the daemon self-places its one board beside the
106
+ ORCH in the ORCH tab. Never launch it in a visible split, pane or tab, as an agent harness
107
+ background task, or tied to an agent session: the launching shell may end, and the daemon keeps
108
+ running and logging. The detach wrapper is `node`, which tickmarkr already requires — no private
109
+ script, no extra visible daemon pane and no CLI log flag:
105
110
 
106
- - **On Orca (`TERM_PROGRAM=Orca` and non-empty `ORCA_TERMINAL_HANDLE`)**:
107
- `orca terminal split --terminal "$ORCA_TERMINAL_HANDLE" --direction vertical --command "tickmarkr run"`.
108
- - **On herdr (`HERDR_ENV=1`)**: use `herdr pane split` on the orchestrator's own pane, then
109
- `herdr pane run <new> "tickmarkr run"` in the returned split.
111
+ ```bash
112
+ # On Orca: <ORCH handle> is the ORCH terminal handle
113
+ cd <repo> && ORCA_TERMINAL_HANDLE="<ORCH handle>" node -e "require('child_process').spawn(process.argv[1], process.argv.slice(2), { detached: true, stdio: 'inherit' }).unref()" tickmarkr run >> <state-dir>/daemon.log 2>&1 < /dev/null
114
+ cd <repo> && ORCA_TERMINAL_HANDLE="<ORCH handle>" node -e "require('child_process').spawn(process.argv[1], process.argv.slice(2), { detached: true, stdio: 'inherit' }).unref()" tickmarkr resume <runId> >> <state-dir>/daemon.log 2>&1 < /dev/null
115
+ # On herdr: <ORCH pane id> is the ORCH pane id
116
+ cd <repo> && HERDR_PANE_ID="<ORCH pane id>" node -e "require('child_process').spawn(process.argv[1], process.argv.slice(2), { detached: true, stdio: 'inherit' }).unref()" tickmarkr run >> <state-dir>/daemon.log 2>&1 < /dev/null
117
+ cd <repo> && HERDR_PANE_ID="<ORCH pane id>" node -e "require('child_process').spawn(process.argv[1], process.argv.slice(2), { detached: true, stdio: 'inherit' }).unref()" tickmarkr resume <runId> >> <state-dir>/daemon.log 2>&1 < /dev/null
118
+ ```
119
+
120
+ - **On Orca (`TERM_PROGRAM=Orca` and non-empty `ORCA_TERMINAL_HANDLE`)**: run the
121
+ `ORCA_TERMINAL_HANDLE` lines; `<ORCH handle>` is the ORCH's own `$ORCA_TERMINAL_HANDLE`.
122
+ - **On herdr (`HERDR_ENV=1`)**: run the `HERDR_PANE_ID` lines; `<ORCH pane id>` is the ORCH's own
123
+ `$HERDR_PANE_ID`.
110
124
 
111
- For `resume`, use the same host-owned split form with `tickmarkr resume <runId>` in place of
112
- `tickmarkr run`. After either launch, read this repository's lock pid and walk its ppid chain
113
- in the process table to the host PTY; verify no agent session is an ancestor. If an agent is
114
- an ancestor, stop that launch and relaunch through the host split before continuing.
125
+ After either launch, read this repository's lock pid from `<state-dir>/graph.lock` and confirm it:
126
+ `kill -0 <pid>` succeeds, `ps -o pgid= -p <pid>` prints that same pid (its own session group;
127
+ ppid 1 is never proof on its own), walk its ppid chain and verify no agent session is an ancestor,
128
+ and read `<state-dir>/daemon.log`. No live holder means the launch failed: read the log, fix the
129
+ cause and run the same line again — never a second daemon beside a live holder, and never a
130
+ visible split to watch it; the daemon-placed board is the live surface.
115
131
 
116
132
  Detached beats are product-owned, one per (tier, seat), from the repository root, through the
117
133
  shipped lifecycle verbs — never a hand-rolled setsid plus nohup wrapper, pidfile or shell loop:
@@ -133,5 +149,33 @@ generation and identity still match, and reads back DISARMED. Never put beats in
133
149
  background task or a visible tab. Include a run-end `beat stop` item for every beat started, also
134
150
  on failure, park or handoff, and require its DISARMED read-back.
135
151
 
152
+ **Legacy beat migration.** `beat start` refuses a legacy (pre-lifecycle, unowned) arm nonzero and
153
+ prints its exit under the RECORDED seat; `beat stop` refuses it too, so a stop followed by a start
154
+ is a dead end, never a migration. When the refusal names `stop pid <N> first`, that pid is the old
155
+ `--loop` writer: stop exactly that pid yourself (`kill <N>`, then confirm `kill -0 <N>` fails) —
156
+ tickmarkr never signals a process it did not launch. Then run the printed step under the seat it
157
+ names, start again and read back ARMED:
158
+
159
+ ```bash
160
+ cd <repo> && tickmarkr beat <tier> --seat <recorded seat> --stand-down
161
+ cd <repo> && tickmarkr beat start <tier> --seat <seat>
162
+ cd <repo> && tickmarkr beat status <tier> --seat <seat>
163
+ ```
164
+
165
+ **Crash recovery.** A crashed owner, a writer killed inside its claimed tick or a killed recovery
166
+ taker leaves its claim, removal lock or stage directory behind. `beat status` only observes: it
167
+ exits nonzero, names the dead pids, prints the command that recovers them and changes no file —
168
+ status never recovers. Run the command it prints; the next ordinary `stop` or `start` recovers what
169
+ was left, with a notice naming each dead pid, and the start reads back ARMED:
170
+
171
+ ```bash
172
+ cd <repo> && tickmarkr beat status <tier> --seat <seat>
173
+ cd <repo> && tickmarkr beat stop <tier> --seat <seat>
174
+ cd <repo> && tickmarkr beat start <tier> --seat <seat>
175
+ ```
176
+
177
+ Never delete a claim, lock or stage file by hand. A live or unprovable holder refuses BUSY naming
178
+ it: wait for that holder to finish or exit, then run the same command.
179
+
136
180
  Keep only watchers that must WAKE the seat in the harness. Re-arm them on each wake and at
137
181
  their harness cap; daemon and beat lifetimes must not depend on that cap.
@@ -88,25 +88,40 @@ When spawning consultants (agents gathering synthesis input for decisions like S
88
88
  1. **Prepare** — start from the requested spec. Run the [binary preflight](#binary-preflight-before-compile-or-run). Check `git status`, confirm no tickmarkr run is active, and work from a non-main branch.
89
89
  2. **Compile** — run `tickmarkr compile <spec>`. Correct compilation errors in the spec, never in the generated graph.
90
90
  3. **Plan** — run `tickmarkr plan`. Review the routing table, capability-floor warnings, and every human gate, including work that each gate blocks.
91
- 4. **Run** — launch `tickmarkr run` using the [host-owned split form](#host-owned-daemon-and-detached-beats), and start detached beats with their run-end stop items. A watch ending the seat's turn is no watch: keep a **blocking journal consumer** alive for the run's terminal events — the shipped watcher below, or a foreground `until grep` on the run's terminal events — and ensure it is re-armed at most every twenty minutes. Never rely on a `Monitor`-only wake. Watch the run journal rather than polling agents, using the shipped watcher — `.claude/skills/tickmarkr-overseer/scripts/watch-journal.sh <state-dir>/runs 20 28800` — which takes a line baseline at arm time, then wakes ONCE on `run-end`, `task-human`, `task-failed` or `consult-verdict` and grades the run-end summary against every execution clause for you; the debt clause is yours — read CURRENT `tickmarkr status <runId>` (step 5). Re-arm after every wake. ⛔ Never `tail -F | grep -m1` (run-end is the journal's last line, so tail never notices the broken pipe and the watcher hangs forever) and never a pane-level done wait (it fires on every agent turn end, not mission end). ⚠ A bare whole-file `grep -q '"event":"run-end"'` is the trap the watcher exists to avoid: on a resume it matches the PREVIOUS run's run-end and returns instantly, so a re-armed watcher reads as coverage that does not exist. Resolve blocked interactions in the agent session; do not turn them into proxy questions.
91
+ 4. **Run** — launch `tickmarkr run` using the [detached launch](#host-owned-daemon-and-detached-beats), and start detached beats with their run-end stop items. A watch ending the seat's turn is no watch: keep a **blocking journal consumer** alive for the run's terminal events — the shipped watcher below, or a foreground `until grep` on the run's terminal events — and ensure it is re-armed at most every twenty minutes. Never rely on a `Monitor`-only wake. Watch the run journal rather than polling agents, using the shipped watcher — `.claude/skills/tickmarkr-overseer/scripts/watch-journal.sh <state-dir>/runs 20 28800` — which takes a line baseline at arm time, then wakes ONCE on `run-end`, `task-human`, `task-failed` or `consult-verdict` and grades the run-end summary's execution clauses for you — `EXECUTION COMPLETE` or `NOT GREEN`, never green, because a run-end record is history; the debt clause is yours — read CURRENT `tickmarkr status <runId>` (step 5). Re-arm after every wake. ⛔ Never `tail -F | grep -m1` (run-end is the journal's last line, so tail never notices the broken pipe and the watcher hangs forever) and never a pane-level done wait (it fires on every agent turn end, not mission end). ⚠ A bare whole-file `grep -q '"event":"run-end"'` is the trap the watcher exists to avoid: on a resume it matches the PREVIOUS run's run-end and returns instantly, so a re-armed watcher reads as coverage that does not exist. Resolve blocked interactions in the agent session; do not turn them into proxy questions.
92
92
  5. **Verify and consolidate** — accept only a green run. A run is green when the run-end event exists in the journal, the tip verify is not "failed", the summary's `failed`, `human`, `blocked` and `pending` buckets are all empty, and CURRENT `tickmarkr status <runId>` reads its owed checks outstanding empty AND known (`outstanding 0`) — a run with a parked task is partial, not green. Empty execution buckets alone are not green either (D-660): every bucket empty and the tip passed, but an operator waived one review, leaves one accepted-risk review check owed — status reads `outstanding 1 (T7 review)` and `run`/`resume` still exit 0 on execution alone, so the run is execution complete, not green. It turns green only when a `tickmarkr verify --record <runId>` discharge moves CURRENT status to `outstanding 0`, including a discharge landing after run-end — the historical run-end record keeps the old count, so never read debt from it; `outstanding unknown` is never green. Tickmarkr consolidates accepted task work on `tickmarkr/<runId>`; it never signs off to the main branch. A human may later merge that integration branch through the repository's normal release process.
93
93
  6. **Record** — `tickmarkr report <runId> --md` prints Markdown to stdout. Redirect it explicitly beside the source spec (for example `tickmarkr report <runId> --md > feature.record.md`) and commit the execution record when the repository tracks those records. Then [stand down](#stand-down-mission-end-and-retirement).
94
94
 
95
95
  ## Host-owned daemon and detached beats
96
96
 
97
- Launch the daemon as a SPLIT of the orchestrator's own pane in the ORCH tab, owned by the
98
- host PTY. Never launch it as an agent harness background task or in a separate tab. The daemon
99
- self-places its board from that split, keeping both daemon and board in the ORCH tab.
97
+ Launch the daemon DETACHED, with no pane of its own: one `tickmarkr run` or `tickmarkr resume <runId>`
98
+ process in its own session, its stdout and stderr appended to `<state-dir>/daemon.log`, and the
99
+ ORCH's recorded address as its board anchor, so the daemon self-places its one board beside the
100
+ ORCH in the ORCH tab. Never launch it in a visible split, pane or tab, as an agent harness
101
+ background task, or tied to an agent session: the launching shell may end, and the daemon keeps
102
+ running and logging. The detach wrapper is `node`, which tickmarkr already requires — no private
103
+ script, no extra visible daemon pane and no CLI log flag:
100
104
 
101
- - **On Orca (`TERM_PROGRAM=Orca` and non-empty `ORCA_TERMINAL_HANDLE`)**:
102
- `orca terminal split --terminal "$ORCA_TERMINAL_HANDLE" --direction vertical --command "tickmarkr run"`.
103
- - **On herdr (`HERDR_ENV=1`)**: use `herdr pane split` on the orchestrator's own pane, then
104
- `herdr pane run <new> "tickmarkr run"` in the returned split.
105
+ ```bash
106
+ # On Orca: <ORCH handle> is the ORCH terminal handle
107
+ cd <repo> && ORCA_TERMINAL_HANDLE="<ORCH handle>" node -e "require('child_process').spawn(process.argv[1], process.argv.slice(2), { detached: true, stdio: 'inherit' }).unref()" tickmarkr run >> <state-dir>/daemon.log 2>&1 < /dev/null
108
+ cd <repo> && ORCA_TERMINAL_HANDLE="<ORCH handle>" node -e "require('child_process').spawn(process.argv[1], process.argv.slice(2), { detached: true, stdio: 'inherit' }).unref()" tickmarkr resume <runId> >> <state-dir>/daemon.log 2>&1 < /dev/null
109
+ # On herdr: <ORCH pane id> is the ORCH pane id
110
+ cd <repo> && HERDR_PANE_ID="<ORCH pane id>" node -e "require('child_process').spawn(process.argv[1], process.argv.slice(2), { detached: true, stdio: 'inherit' }).unref()" tickmarkr run >> <state-dir>/daemon.log 2>&1 < /dev/null
111
+ cd <repo> && HERDR_PANE_ID="<ORCH pane id>" node -e "require('child_process').spawn(process.argv[1], process.argv.slice(2), { detached: true, stdio: 'inherit' }).unref()" tickmarkr resume <runId> >> <state-dir>/daemon.log 2>&1 < /dev/null
112
+ ```
113
+
114
+ - **On Orca (`TERM_PROGRAM=Orca` and non-empty `ORCA_TERMINAL_HANDLE`)**: run the
115
+ `ORCA_TERMINAL_HANDLE` lines; `<ORCH handle>` is the ORCH's own `$ORCA_TERMINAL_HANDLE`.
116
+ - **On herdr (`HERDR_ENV=1`)**: run the `HERDR_PANE_ID` lines; `<ORCH pane id>` is the ORCH's own
117
+ `$HERDR_PANE_ID`.
105
118
 
106
- For `resume`, use the same host-owned split form with `tickmarkr resume <runId>` in place of
107
- `tickmarkr run`. After either launch, read this repository's lock pid and walk its ppid chain
108
- in the process table to the host PTY; verify no agent session is an ancestor. If an agent is
109
- an ancestor, stop that launch and relaunch through the host split before continuing.
119
+ After either launch, read this repository's lock pid from `<state-dir>/graph.lock` and confirm it:
120
+ `kill -0 <pid>` succeeds, `ps -o pgid= -p <pid>` prints that same pid (its own session group;
121
+ ppid 1 is never proof on its own), walk its ppid chain and verify no agent session is an ancestor,
122
+ and read `<state-dir>/daemon.log`. No live holder means the launch failed: read the log, fix the
123
+ cause and run the same line again — never a second daemon beside a live holder, and never a
124
+ visible split to watch it; the daemon-placed board is the live surface.
110
125
 
111
126
  Detached beats are product-owned, one per (tier, seat), from the repository root, through the
112
127
  shipped lifecycle verbs — never a hand-rolled setsid plus nohup wrapper, pidfile or shell loop:
@@ -128,6 +143,34 @@ generation and identity still match, and reads back DISARMED. Never put beats in
128
143
  background task or a visible tab. Include a run-end `beat stop` item for every beat started, also
129
144
  on failure, park or handoff, and require its DISARMED read-back.
130
145
 
146
+ **Legacy beat migration.** `beat start` refuses a legacy (pre-lifecycle, unowned) arm nonzero and
147
+ prints its exit under the RECORDED seat; `beat stop` refuses it too, so a stop followed by a start
148
+ is a dead end, never a migration. When the refusal names `stop pid <N> first`, that pid is the old
149
+ `--loop` writer: stop exactly that pid yourself (`kill <N>`, then confirm `kill -0 <N>` fails) —
150
+ tickmarkr never signals a process it did not launch. Then run the printed step under the seat it
151
+ names, start again and read back ARMED:
152
+
153
+ ```bash
154
+ cd <repo> && tickmarkr beat <tier> --seat <recorded seat> --stand-down
155
+ cd <repo> && tickmarkr beat start <tier> --seat <seat>
156
+ cd <repo> && tickmarkr beat status <tier> --seat <seat>
157
+ ```
158
+
159
+ **Crash recovery.** A crashed owner, a writer killed inside its claimed tick or a killed recovery
160
+ taker leaves its claim, removal lock or stage directory behind. `beat status` only observes: it
161
+ exits nonzero, names the dead pids, prints the command that recovers them and changes no file —
162
+ status never recovers. Run the command it prints; the next ordinary `stop` or `start` recovers what
163
+ was left, with a notice naming each dead pid, and the start reads back ARMED:
164
+
165
+ ```bash
166
+ cd <repo> && tickmarkr beat status <tier> --seat <seat>
167
+ cd <repo> && tickmarkr beat stop <tier> --seat <seat>
168
+ cd <repo> && tickmarkr beat start <tier> --seat <seat>
169
+ ```
170
+
171
+ Never delete a claim, lock or stage file by hand. A live or unprovable holder refuses BUSY naming
172
+ it: wait for that holder to finish or exit, then run the same command.
173
+
131
174
  Keep only watchers that must WAKE the seat in the harness. Re-arm them on each wake and at
132
175
  their harness cap; daemon and beat lifetimes must not depend on that cap.
133
176
 
@@ -146,7 +189,7 @@ run/task, original park `#L`, actor/reason, exact argv, consequence and enactor.
146
189
  confirms; `n`/Esc cancel; Enter never confirms. Read the receipt's appended `task-approved`
147
190
  line and actor/reason back from the journal. Approval records permission, never dispatch,
148
191
  a passed gate or task completion. With no live owner it says **approved; resume required**:
149
- exit the observer and launch `tickmarkr resume <runId>` explicitly with the same host-owned split form above. A matching live daemon
192
+ exit the observer and launch `tickmarkr resume <runId>` explicitly with the same detached launch above. A matching live daemon
150
193
  enacts at its next task boundary; if a different live run owns the repository lock, wait
151
194
  for that run to end before resuming this one.
152
195
 
@@ -174,7 +217,15 @@ Run offers only validated park verbs: human/attempt-cap/other non-gate parks all
174
217
  infra allows approve or `--recheck`; review gate-fail allows `--waive`, `--uphold` or
175
218
  `--recheck`; other gate-fail allows waive/recheck. Waive satisfies only the identified
176
219
  failed gate, uphold funds a fixed attempt carrying review findings, and recheck reruns the
177
- declared battery without satisfying a gate. A stall park that recorded a `reapFailure`
220
+ declared battery without satisfying a gate. A waive needs EXECUTED evidence: mark every
221
+ out-of-band (OOB) read behind it `OOB: static` (diff and source read, nothing run) or
222
+ `OOB: executed` (the finding's claim run at the task's head commit, exit code and log on file).
223
+ Static CLEAN never backs a waive — a static read that missed an executed material defect looks
224
+ like one that found none, and a static read relabelled executed is a false record. When the
225
+ finding's reproduction, executed at the task head, fails, the ruling is uphold or recheck; waive
226
+ only when it passes and the record names it, keeping the park/gate binding and the owed check
227
+ (`outstanding 1 (T2 review)` until `tickmarkr verify --record <runId>`):
228
+ `D-NNN WAIVE T2 review — park <line>@<ts> --gate review — OOB: executed — <reproduction> at <task-head-sha>: exit 0, log <path>`. A stall park that recorded a `reapFailure`
178
229
  (unreadable or surviving worker census) allows approve or `--recheck --park <line>@<ts>`:
179
230
  recheck re-verifies that attempt's owned census and gates its harvested commits with no
180
231
  worker only with an explicitly recorded empty survivors array (`[]`) — a missing,
@@ -26,6 +26,30 @@ again on the new token; never re-issue the old one (D-470: an unbound approve wa
26
26
  gate). A failed task keeps its recheck with its bound failure token: status prints
27
27
  `failed — T3 — failure <line>@<ts>`, and `tickmarkr approve <runId> T3 --recheck --park <line>@<ts>`
28
28
  re-gates its landed commits with no worker.
29
+
30
+ **A waive is funded by EXECUTED evidence, never by a static read.** Every out-of-band (OOB) read
31
+ behind a ruling records its mode: `OOB: static` — the diff and source were read and nothing ran — or
32
+ `OOB: executed` — the finding's claim was run against the task's head commit (a test, a reproduction
33
+ or a command) with its exit code and log path on file. Only an executed read backs a waive. A static
34
+ CLEAN never does, however confident: a static read that missed an executed material defect reads
35
+ exactly like one that found nothing, and a static read relabelled `executed` is a false record.
36
+ Replay it before ruling. The reviewer's material finding says a killed taker strands the claim; a
37
+ static OOB read of the diff reports CLEAN; the finding's reproduction, executed at the task head,
38
+ exits 1 with the strand on disk — that static CLEAN would have waived a real defect, so the ruling
39
+ is uphold or recheck. Waive only on a supported executed read: the same reproduction at the task head
40
+ exits 0, its log is on file, and the record names both. The waive keeps its park and gate binding and
41
+ its debt: CURRENT `tickmarkr status <runId>` reads `outstanding 1 (T2 review)` until
42
+ `tickmarkr verify --record <runId>` discharges it. Record example:
43
+
44
+ ```text
45
+ D-NNN WAIVE T2 review — park <line>@<ts> --gate review
46
+ OOB: executed — `<reproduction>` at <task-head-sha>: exit 0, log <path>
47
+ finding: <the reviewer's material claim> — not reproduced by that execution
48
+ debt: outstanding 1 (T2 review) until `tickmarkr verify --record <runId>`
49
+ ```
50
+
51
+ A record whose OOB line reads `static`, or names no command, exit code and log, funds no waive: the
52
+ ruling is uphold or recheck.
29
53
  Manual UI retains the receipt; the daemon-owned board gracefully stands down only its own
30
54
  presence before closing its owned pane. Non-TTY supervision keeps `status`, `report` and
31
55
  default-watch line output (`--watch --plain` is also available on a TTY). Keep the canonical
@@ -121,13 +145,13 @@ through brief lineage. **An executor choice nobody made is still an executor cho
121
145
  tab OVERSEER; create ONE tab ORCHESTRATOR.
122
146
  **FIVE-TAB CANON (standing operator layout — corrected three times on 2026-07-27, layout approved
123
147
  2026-07-29, re-earned 2026-08-17):**
124
- - `OVERSEER` — you. Do not add a live run surface here: launch the daemon from the ORCH tab
125
- using the host-owned split form below; it self-places the shipped board there.
126
- - `ORCH` — the orchestrator, its host-owned daemon split, and the daemon-placed, run-id-pinned shipped board BESIDE it: the board
148
+ - `OVERSEER` — you. Do not add a live run surface here: launch the daemon detached at the ORCH's
149
+ recorded address (the detached launch below); it self-places the shipped board in the ORCH tab.
150
+ - `ORCH` — the orchestrator and the daemon-placed, run-id-pinned shipped board BESIDE it (the daemon itself runs detached, with no pane): the board
127
151
  takes the RIGHT half of the tab and the orchestrator's own narration keeps the LEFT half. (It was a
128
152
  full-width board above a narration rail until 2026-08-25; the operator changed it, because a task
129
153
  table is a few rows and it was spending height it did not need while squeezing the narration.) **Look for that `role: "watch"` pane; never hand-place or hand-roll a live
130
- run surface. Only the orchestrator, daemon and board belong here: a work seat NEVER splits into the ORCH tab.** Operator verbatim:
154
+ run surface. Only the orchestrator and board belong here: a work seat NEVER splits into the ORCH tab.** Operator verbatim:
131
155
  *"in orch tab should be the orch and the watcher only."* Re-earned 2026-08-17: a planning seat split
132
156
  beside the orchestrator, and the operator caught it, again. The daemon owns this vertical stack and
133
157
  places it the same way at every terminal width; neither the worker-pane halving floor, nor a
@@ -147,7 +171,7 @@ through brief lineage. **An executor choice nobody made is still an executor cho
147
171
  fraction (`ORCH · v1.19 4/5`, updated on every task-done); tickmarkr opens ONE TAB PER TASK, labelled
148
172
  with the task id and holding that task's worker plus its judge/review/consult panes (tickmarkr
149
173
  updates it). Never long context strings or ✓-chains.
150
- - **On Orca (`TERM_PROGRAM=Orca` and non-empty `ORCA_TERMINAL_HANDLE`)**: Do not load the `herdr` skill and do not map a Herdr workspace. Work from the current Orca terminal context. Keep the overseer in the launching terminal, name it in the same act — `orca terminal rename --terminal "$ORCA_TERMINAL_HANDLE" --title "OVERSEER · <version>"` (tab title; see the seat-name law under Seat-spawn recipes) — and inspect terminals with `orca terminal list --json`. Launch the daemon in a host-owned split of the orchestrator terminal using the form below; its self-placed watch board stays in the ORCH tab.
174
+ - **On Orca (`TERM_PROGRAM=Orca` and non-empty `ORCA_TERMINAL_HANDLE`)**: Do not load the `herdr` skill and do not map a Herdr workspace. Work from the current Orca terminal context. Keep the overseer in the launching terminal, name it in the same act — `orca terminal rename --terminal "$ORCA_TERMINAL_HANDLE" --title "OVERSEER · <version>"` (tab title; see the seat-name law under Seat-spawn recipes) — and inspect terminals with `orca terminal list --json`. Launch the daemon detached at the orchestrator terminal's recorded handle using the detached launch below — no pane of its own; its self-placed watch board stays in the ORCH tab.
151
175
  2. **Orchestrator**: First consume a successful `tickmarkr fleet --pick consult` in the seat repository, per the Fleet selection contract below; then launch the returned adapter/model with your agent host.
152
176
  - **On herdr (`HERDR_ENV=1`)**: Spawning on current herdr is two-step — the one-shot `agent start --cwd` form was removed in the herdr CLI redesign and now fails with `unknown option` (OBS-138): first create the pane with `herdr tab create --workspace <ws> --cwd <repo> --label "ORCH · <version>"` and parse `result.root_pane.pane_id` from its JSON, then start the agent in it. For Claude Code, use `herdr agent start orchestrator --kind claude --pane <root-pane-id> -- --permission-mode bypassPermissions` (append `--model <picked-model>` after the `--` from the Fleet receipt). For Codex, use `herdr agent start orchestrator --kind codex --pane <root-pane-id> -- --dangerously-bypass-approvals-and-sandbox` (add `--model <picked-model>` from the Fleet receipt). The unsandboxed flag is REQUIRED: codex's `workspace-write` sandbox keeps `.git` refs read-only, so a sandboxed orchestrator's `tickmarkr run` dies at integration-branch creation — do not downgrade it. Workers you never spawn — tickmarkr spawns its own visible worker panes. Auxiliary agents you do 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 --settings '{"promptSuggestionEnabled":false}'`. **For a codex consultant, use `-a never --sandbox workspace-write` — NOT `--sandbox read-only`.** ⚠ **`--sandbox read-only` CONTRADICTS this skill's own completion protocol and will hang the seat.** Every seat you spawn is told to deliver an ARTIFACT ending in a terminal MARKER, because that is the only completion signal the artifact watcher can key on (`done` is turn end). A read-only sandbox cannot write that artifact, so codex blocks on `Would you like to make the following edits?` for its OWN report — and the report exists ONLY in the pending edit, so abandoning the prompt destroys the work rather than merely delaying it. Measured 2026-08-28: a consultant spawned `--sandbox read-only` finished a 14,604-byte verdict, sat blocked on the write, and the operator saw the prompt before the supervising tier did. `read-only` is correct ONLY for a seat that writes nothing at all — which, under the artifact+marker rule, is no seat this skill tells you to spawn. When the prompt does appear, answer **"Yes, and don't ask again for these files"** rather than plain yes: plain yes re-blocks on the next write of the same file. **That `--settings` pair is not cosmetic and it is not optional:** claude-code's AUTOSUGGEST renders context-plausible ghost text into an idle seat's prompt line that is BYTE-IDENTICAL to a typed draft in text-format reads (OBS-482), so a supervising tier cannot tell a seat's own unsent work from a rendering artifact without `agent read --format ansi`. Turning the suggester off at spawn removes the ambiguity at its source instead of paying for the discrimination at every read. Verified against the shipped binary: `claude --settings '{"promptSuggestionEnabled":false}' -p …` exits 0 with a real response, and the key appears in the binary's own settings schema. **For kimi, pass `-y`** (`herdr agent start <name> --kind kimi --pane <id> -- -y`) — the adapter already launches its own workers that way (`src/adapters/kimi.ts:204`), and a kimi seat spawned without it sits on an approval prompt having done nothing. **Herdr cannot see that state**: it reports a kimi pane as `agent_status: working` with `screen_detection_skipped: true` while the prompt is up, so the BLOCKED-STATE watcher below is blind on this vendor and the spawn flag is the ONLY control. Every vendor you spawn needs its auto-approve form named here; a vendor absent from this list is a seat that will hang.
153
177
  - **On Orca (`TERM_PROGRAM=Orca` and non-empty `ORCA_TERMINAL_HANDLE`)**: Seats spawn with `orca terminal create` on a path worktree selector with a command:
@@ -197,20 +221,20 @@ through brief lineage. **An executor choice nobody made is still an executor cho
197
221
  The orchestrator's run log, handoff, and plan live inside its sandbox root and are artifacts of that orchestrator.
198
222
  The overseer reads them from there, using the path the orchestrator reports; do not substitute the
199
223
  overseer's repository or a machine-global planning directory. The orchestrator brief must say: if the seat's
200
- sandbox denies writes under `.git`, report the denial to the overseer and stop; the overseer launches the daemon itself; use the host-owned split of the ORCH pane, never the overseer tab;
224
+ sandbox denies writes under `.git`, report the denial to the overseer and stop; the overseer launches the daemon itself; use the detached launch at the recorded ORCH address, never a pane and never the overseer tab;
201
225
  and the daemon host is never sandboxed.
202
226
 
203
227
  **Sandbox-denial daemon launch (OVERSEER):** use the ORCH address recorded at seat creation,
204
228
  re-resolved by title if stale; the launching overseer's own terminal is not the target.
205
229
 
206
230
  - **On Orca (`TERM_PROGRAM=Orca` and non-empty `ORCA_TERMINAL_HANDLE`)**: record the ORCH's
207
- `result.terminal.handle` from its seat-create receipt. For the fallback, run
208
- `orca terminal split --terminal <ORCH handle> --direction vertical --command "tickmarkr run"`.
231
+ `result.terminal.handle` from its seat-create receipt. For the fallback, run the detached
232
+ `ORCA_TERMINAL_HANDLE="<ORCH handle>"` line of the detached launch below with that recorded handle.
209
233
  Here `$ORCA_TERMINAL_HANDLE` names the OVERSEER's own terminal; use it only when the ORCH
210
234
  launches its own daemon, never when the OVERSEER launches on the ORCH's behalf.
211
235
  - **On herdr (`HERDR_ENV=1`)**: record the ORCH's `result.root_pane.pane_id` from its tab-create
212
- receipt. For the fallback, use `herdr pane split` targeting that recorded ORCH pane id, then
213
- `herdr pane run <new> "tickmarkr run"`; never target the OVERSEER's pane.
236
+ receipt. For the fallback, run the detached `HERDR_PANE_ID="<ORCH pane id>"` line of the detached
237
+ launch below with that recorded ORCH pane id; never target the OVERSEER's pane.
214
238
 
215
239
  The same recorded ORCH address is required for a fallback `tickmarkr resume <runId>`.
216
240
 
@@ -308,23 +332,38 @@ For Leg-2 (both hosts), a Codex reviewer under `workspace-write` must be briefed
308
332
  `<repo>/.tickmarkr/overseer/verdicts/<task>.md`, and its verdict must be written there before it is read.
309
333
  ## Host-owned daemon and detached beats
310
334
 
311
- Launch the daemon as a SPLIT of the orchestrator's own pane in the ORCH tab, owned by the
312
- host PTY. Never launch it as an agent harness background task or in a separate tab. The daemon
313
- self-places its board from that split, keeping both daemon and board in the ORCH tab.
335
+ Launch the daemon DETACHED, with no pane of its own: one `tickmarkr run` or `tickmarkr resume <runId>`
336
+ process in its own session, its stdout and stderr appended to `<state-dir>/daemon.log`, and the
337
+ ORCH's recorded address as its board anchor, so the daemon self-places its one board beside the
338
+ ORCH in the ORCH tab. Never launch it in a visible split, pane or tab, as an agent harness
339
+ background task, or tied to an agent session: the launching shell may end, and the daemon keeps
340
+ running and logging. The detach wrapper is `node`, which tickmarkr already requires — no private
341
+ script, no extra visible daemon pane and no CLI log flag:
314
342
 
315
- - **On Orca (`TERM_PROGRAM=Orca` and non-empty `ORCA_TERMINAL_HANDLE`)**: only when the ORCH
316
- launches its own daemon, use
317
- `orca terminal split --terminal "$ORCA_TERMINAL_HANDLE" --direction vertical --command "tickmarkr run"`.
318
- When the OVERSEER launches the sandbox-denial fallback, substitute the ORCH handle recorded
319
- at seat creation: `orca terminal split --terminal <ORCH handle> --direction vertical --command "tickmarkr run"`.
320
- - **On herdr (`HERDR_ENV=1`)**: use `herdr pane split` on the orchestrator's own pane, then
321
- `herdr pane run <new> "tickmarkr run"` in the returned split. When the OVERSEER launches the
322
- fallback, target the ORCH pane id recorded at seat creation, never the OVERSEER's pane.
343
+ ```bash
344
+ # On Orca: <ORCH handle> is the ORCH terminal handle
345
+ cd <repo> && ORCA_TERMINAL_HANDLE="<ORCH handle>" node -e "require('child_process').spawn(process.argv[1], process.argv.slice(2), { detached: true, stdio: 'inherit' }).unref()" tickmarkr run >> <state-dir>/daemon.log 2>&1 < /dev/null
346
+ cd <repo> && ORCA_TERMINAL_HANDLE="<ORCH handle>" node -e "require('child_process').spawn(process.argv[1], process.argv.slice(2), { detached: true, stdio: 'inherit' }).unref()" tickmarkr resume <runId> >> <state-dir>/daemon.log 2>&1 < /dev/null
347
+ # On herdr: <ORCH pane id> is the ORCH pane id
348
+ cd <repo> && HERDR_PANE_ID="<ORCH pane id>" node -e "require('child_process').spawn(process.argv[1], process.argv.slice(2), { detached: true, stdio: 'inherit' }).unref()" tickmarkr run >> <state-dir>/daemon.log 2>&1 < /dev/null
349
+ cd <repo> && HERDR_PANE_ID="<ORCH pane id>" node -e "require('child_process').spawn(process.argv[1], process.argv.slice(2), { detached: true, stdio: 'inherit' }).unref()" tickmarkr resume <runId> >> <state-dir>/daemon.log 2>&1 < /dev/null
350
+ ```
323
351
 
324
- For `resume`, use the same host-owned split form with `tickmarkr resume <runId>` in place of
325
- `tickmarkr run`. After either launch, read this repository's lock pid and walk its ppid chain
326
- in the process table to the host PTY; verify no agent session is an ancestor. If an agent is
327
- an ancestor, stop that launch and relaunch through the host split before continuing.
352
+ - **On Orca (`TERM_PROGRAM=Orca` and non-empty `ORCA_TERMINAL_HANDLE`)**: run the
353
+ `ORCA_TERMINAL_HANDLE` lines. Only when the ORCH
354
+ launches its own daemon is `<ORCH handle>` its own `$ORCA_TERMINAL_HANDLE`. When the OVERSEER
355
+ launches the sandbox-denial fallback, substitute the ORCH handle recorded
356
+ at seat creation; the OVERSEER's own `$ORCA_TERMINAL_HANDLE` would put the board in its tab.
357
+ - **On herdr (`HERDR_ENV=1`)**: run the `HERDR_PANE_ID` lines; `<ORCH pane id>` is the ORCH's own
358
+ `$HERDR_PANE_ID`. When the OVERSEER launches the fallback, substitute the ORCH pane id recorded
359
+ at seat creation, never the OVERSEER's pane.
360
+
361
+ After either launch, read this repository's lock pid from `<state-dir>/graph.lock` and confirm it:
362
+ `kill -0 <pid>` succeeds, `ps -o pgid= -p <pid>` prints that same pid (its own session group;
363
+ ppid 1 is never proof on its own), walk its ppid chain and verify no agent session is an ancestor,
364
+ and read `<state-dir>/daemon.log`. No live holder means the launch failed: read the log, fix the
365
+ cause and run the same line again — never a second daemon beside a live holder, and never a
366
+ visible split to watch it; the daemon-placed board is the live surface.
328
367
 
329
368
  Detached beats are product-owned, one per (tier, seat), from the repository root, through the
330
369
  shipped lifecycle verbs — never a hand-rolled setsid plus nohup wrapper, pidfile or shell loop:
@@ -346,6 +385,34 @@ generation and identity still match, and reads back DISARMED. Never put beats in
346
385
  background task or a visible tab. Include a run-end `beat stop` item for every beat started, also
347
386
  on failure, park or handoff, and require its DISARMED read-back.
348
387
 
388
+ **Legacy beat migration.** `beat start` refuses a legacy (pre-lifecycle, unowned) arm nonzero and
389
+ prints its exit under the RECORDED seat; `beat stop` refuses it too, so a stop followed by a start
390
+ is a dead end, never a migration. When the refusal names `stop pid <N> first`, that pid is the old
391
+ `--loop` writer: stop exactly that pid yourself (`kill <N>`, then confirm `kill -0 <N>` fails) —
392
+ tickmarkr never signals a process it did not launch. Then run the printed step under the seat it
393
+ names, start again and read back ARMED:
394
+
395
+ ```bash
396
+ cd <repo> && tickmarkr beat <tier> --seat <recorded seat> --stand-down
397
+ cd <repo> && tickmarkr beat start <tier> --seat <seat>
398
+ cd <repo> && tickmarkr beat status <tier> --seat <seat>
399
+ ```
400
+
401
+ **Crash recovery.** A crashed owner, a writer killed inside its claimed tick or a killed recovery
402
+ taker leaves its claim, removal lock or stage directory behind. `beat status` only observes: it
403
+ exits nonzero, names the dead pids, prints the command that recovers them and changes no file —
404
+ status never recovers. Run the command it prints; the next ordinary `stop` or `start` recovers what
405
+ was left, with a notice naming each dead pid, and the start reads back ARMED:
406
+
407
+ ```bash
408
+ cd <repo> && tickmarkr beat status <tier> --seat <seat>
409
+ cd <repo> && tickmarkr beat stop <tier> --seat <seat>
410
+ cd <repo> && tickmarkr beat start <tier> --seat <seat>
411
+ ```
412
+
413
+ Never delete a claim, lock or stage file by hand. A live or unprovable holder refuses BUSY naming
414
+ it: wait for that holder to finish or exit, then run the same command.
415
+
349
416
  Keep only watchers that must WAKE the seat in the harness. Re-arm them on each wake and at
350
417
  their harness cap; daemon and beat lifetimes must not depend on that cap.
351
418
 
@@ -461,8 +528,9 @@ journal tail to decide what happens next, or sweeping orphans — you have taken
461
528
  ensure it is re-armed at most every twenty minutes. Never rely on a `Monitor`-only wake.
462
529
  - **On herdr (`HERDR_ENV=1`)**: Use the shipped journal instruments:
463
530
  **All four are covered by one shipped instrument** — `scripts/watch-journal.sh <runs-dir> [poll] [cap]
464
- [events-csv]` — which arms on a line baseline, wakes once, and grades a `run-end` against every green
465
- clause. `scripts/watch-parks.sh` stays the park-specific wake for THIS seat (it counts parks and speaks
531
+ [events-csv]` — which arms on a line baseline, wakes once, and grades a `run-end`'s execution clauses —
532
+ `EXECUTION COMPLETE` or `NOT GREEN`, never green: green is CURRENT `tickmarkr status <runId>` at
533
+ `outstanding 0`. `scripts/watch-parks.sh` stays the park-specific wake for THIS seat (it counts parks and speaks
466
534
  about rulings); the two overlap on `task-human` deliberately, and arming both is coverage, not a bug.
467
535
  - **On Orca (`TERM_PROGRAM=Orca` and non-empty `ORCA_TERMINAL_HANDLE`)**: Arm a file/journal consumer for the same terminal events and inspect the seat with `orca terminal read --terminal <handle> --screen --json`; re-arm after every wake.
468
536
  - **Daemon liveness ≠ journal activity.** A dead daemon emits no events, so journal watchers sleep through
@@ -1194,7 +1262,8 @@ wrote, so a stray shell loop left running by a predecessor seat kept a stood-dow
1194
1262
  shell or not, never does. The pre-2.1.3 forms `while :; do tickmarkr beat overseer; sleep 10; done`
1195
1263
  and `tickmarkr beat overseer --stand-down` are preserved here only as older migration warnings: both
1196
1264
  are now rejected outright because neither declares which seat the tier speaks for. Do not copy or
1197
- run either legacy form.
1265
+ run either legacy form. To leave an arm a legacy writer left, run the **Legacy beat migration** steps in Host-owned
1266
+ daemon and detached beats: the printed `--stand-down` under the recorded seat, then `beat start`.
1198
1267
 
1199
1268
  One beat per LIVE PROCESS, deliberately: `--loop`'s recorded pid is that process's own, so the tier's
1200
1269
  liveness is exactly as verifiable as the process table — stop the loop, or let it die, and the
@@ -82,19 +82,21 @@ report() {
82
82
  done=$(bucket done "$line"); failed=$(bucket failed "$line")
83
83
  human=$(bucket human "$line"); blocked=$(bucket blocked "$line")
84
84
  pending=$(bucket pending "$line")
85
- # GREEN IS A CONJUNCTION, AND THE SHORT FORM OF IT IS WRONG. "run-end plus tip verify" passes a run
86
- # that ended `done=[T1,T3,T4] human=[T2]` — three delivered, one PARKED — and calling that green is
87
- # how a park becomes invisible. Grade every clause here so the reader never has to remember to.
85
+ # NO RUN-END RECORD CERTIFIES GREEN. "run-end plus tip verify" passes a run that ended
86
+ # `done=[T1,T3,T4] human=[T2]` — three delivered, one PARKED — and even empty buckets leave the debt
87
+ # clause open: a waived review is still owed (D-660), and a `verify --record` discharge that lands
88
+ # AFTER run-end moves only CURRENT status. This row is history, so the best it can say is
89
+ # EXECUTION COMPLETE; green is read from CURRENT `tickmarkr status <runId>` at `outstanding 0`.
88
90
  if [ "$tv" != "failed" ] && [ -z "$failed$human$blocked$pending" ]; then
89
- verdict="GREEN"
91
+ verdict="EXECUTION COMPLETE"
90
92
  else
91
93
  verdict="NOT GREEN"
92
94
  fi
93
95
  echo "RUN_END $run — $verdict (tipVerify=${tv:-unknown})"
94
96
  echo " done=[${done}] failed=[${failed}] human=[${human}] blocked=[${blocked}] pending=[${pending}]"
95
- [ "$verdict" = "GREEN" ] \
96
- && echo " all four buckets empty and tip verify is not failed — this run is green" \
97
- || echo " a non-empty bucket above is the reason; name it, never report this run as green"
97
+ [ "$verdict" = "EXECUTION COMPLETE" ] \
98
+ && echo " execution buckets empty and tip verify is not failed — not yet green: read CURRENT \`tickmarkr status $run\`; only \`outstanding 0\` is green, \`outstanding N (...)\` and \`outstanding unknown\` are not, and this run-end record's own count is historical" \
99
+ || echo " a non-empty bucket or a failed tip above is the reason; name it, never report this run as green"
98
100
  ;;
99
101
  task-human)
100
102
  echo "TASK_HUMAN $(field taskId "$line") — $run"