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.
- package/dist/cli/commands/beat.d.ts +3 -3
- package/dist/cli/commands/beat.js +39 -19
- package/dist/cli/commands/fleet.js +2 -0
- package/dist/cli/commands/plan.js +16 -18
- package/dist/cli/commands/verify.js +4 -2
- package/dist/cli/help.js +5 -2
- package/dist/drivers/herdr.js +2 -2
- package/dist/drivers/orca.d.ts +12 -4
- package/dist/drivers/orca.js +200 -39
- package/dist/gates/evidence.js +3 -3
- package/dist/gates/review.d.ts +1 -1
- package/dist/gates/review.js +62 -13
- package/dist/gates/run-gates.d.ts +3 -0
- package/dist/gates/run-gates.js +115 -20
- package/dist/gates/test-manifest.d.ts +36 -2
- package/dist/gates/test-manifest.js +55 -10
- package/dist/run/beat-lifecycle.d.ts +109 -24
- package/dist/run/beat-lifecycle.js +1173 -216
- package/dist/run/daemon.d.ts +4 -0
- package/dist/run/daemon.js +111 -33
- package/dist/run/git-trust.d.ts +78 -3
- package/dist/run/git-trust.js +175 -19
- package/dist/run/git.d.ts +24 -3
- package/dist/run/git.js +140 -21
- package/dist/run/merge.js +2 -2
- package/dist/run/verification-job.d.ts +2 -0
- package/dist/run/verification-job.js +32 -10
- package/dist/tui/ink/fleet-app.d.ts +7 -2
- package/dist/tui/ink/fleet-app.js +56 -14
- package/package.json +1 -1
- package/skills/tickmarkr-auto/SKILL.md +58 -14
- package/skills/tickmarkr-loop/SKILL.md +65 -14
- package/skills/tickmarkr-overseer/SKILL.md +97 -28
- package/skills/tickmarkr-overseer/scripts/watch-journal.sh +9 -7
|
@@ -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
|
|
16
|
-
approval records permission and never dispatches work itself.
|
|
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 [
|
|
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
|
|
103
|
-
|
|
104
|
-
|
|
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
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
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
|
-
|
|
112
|
-
`
|
|
113
|
-
|
|
114
|
-
|
|
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 [
|
|
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
|
|
98
|
-
|
|
99
|
-
|
|
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
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
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
|
-
|
|
107
|
-
`
|
|
108
|
-
|
|
109
|
-
|
|
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
|
|
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
|
|
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
|
|
125
|
-
|
|
126
|
-
- `ORCH` — the orchestrator
|
|
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
|
|
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
|
|
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
|
|
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
|
-
`
|
|
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,
|
|
213
|
-
|
|
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
|
|
312
|
-
|
|
313
|
-
|
|
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
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
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
|
-
|
|
325
|
-
`
|
|
326
|
-
|
|
327
|
-
|
|
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`
|
|
465
|
-
|
|
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
|
-
#
|
|
86
|
-
#
|
|
87
|
-
#
|
|
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="
|
|
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" = "
|
|
96
|
-
&& echo "
|
|
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"
|