tickmarkr 2.6.3 → 2.6.5

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.
Files changed (79) hide show
  1. package/README.md +10 -2
  2. package/dist/adapters/catalog-remote.d.ts +23 -0
  3. package/dist/adapters/catalog-remote.js +38 -0
  4. package/dist/adapters/claude-code.d.ts +1 -1
  5. package/dist/adapters/claude-code.js +4 -1
  6. package/dist/adapters/codex-commit-check.d.ts +25 -0
  7. package/dist/adapters/codex-commit-check.js +108 -0
  8. package/dist/adapters/codex.d.ts +2 -1
  9. package/dist/adapters/codex.js +26 -5
  10. package/dist/adapters/model-discovery-check.d.ts +33 -0
  11. package/dist/adapters/model-discovery-check.js +123 -0
  12. package/dist/adapters/model-lints.d.ts +2 -1
  13. package/dist/adapters/model-lints.js +52 -1
  14. package/dist/adapters/model-windows.d.ts +2 -1
  15. package/dist/adapters/model-windows.js +18 -7
  16. package/dist/adapters/prompt.js +3 -2
  17. package/dist/adapters/types.d.ts +13 -0
  18. package/dist/cli/commands/approve.js +37 -6
  19. package/dist/cli/commands/doctor.d.ts +4 -0
  20. package/dist/cli/commands/doctor.js +20 -3
  21. package/dist/cli/commands/fleet.d.ts +10 -1
  22. package/dist/cli/commands/fleet.js +279 -95
  23. package/dist/cli/commands/init.js +10 -4
  24. package/dist/cli/commands/plan.js +65 -4
  25. package/dist/cli/commands/report.d.ts +39 -2
  26. package/dist/cli/commands/report.js +227 -10
  27. package/dist/cli/commands/resume.js +2 -4
  28. package/dist/cli/commands/run.d.ts +11 -0
  29. package/dist/cli/commands/run.js +33 -5
  30. package/dist/cli/commands/status.js +30 -3
  31. package/dist/cli/commands/verify.d.ts +1 -1
  32. package/dist/cli/commands/verify.js +356 -123
  33. package/dist/compile/native.js +130 -0
  34. package/dist/config/config.d.ts +25 -7
  35. package/dist/config/config.js +92 -11
  36. package/dist/config/fleet-overlay.d.ts +2 -1
  37. package/dist/config/fleet-overlay.js +77 -4
  38. package/dist/drivers/orca.d.ts +19 -1
  39. package/dist/drivers/orca.js +161 -31
  40. package/dist/gates/cache.d.ts +4 -0
  41. package/dist/gates/cache.js +10 -2
  42. package/dist/gates/llm.d.ts +4 -1
  43. package/dist/gates/llm.js +6 -1
  44. package/dist/gates/review.js +8 -3
  45. package/dist/gates/run-gates.d.ts +3 -2
  46. package/dist/gates/run-gates.js +91 -50
  47. package/dist/route/router.d.ts +11 -0
  48. package/dist/route/router.js +29 -8
  49. package/dist/run/daemon.js +158 -22
  50. package/dist/run/git.js +9 -0
  51. package/dist/run/journal.d.ts +101 -1
  52. package/dist/run/journal.js +352 -2
  53. package/dist/run/launchservices-check.d.ts +56 -0
  54. package/dist/run/launchservices-check.js +117 -0
  55. package/dist/run/operator-state.d.ts +22 -2
  56. package/dist/run/operator-state.js +20 -3
  57. package/dist/run/title-environment.d.ts +45 -0
  58. package/dist/run/title-environment.js +127 -0
  59. package/dist/run/title-preload.cjs +38 -0
  60. package/dist/run/title-preload.d.cts +1 -0
  61. package/dist/tui/cockpit/board.js +12 -6
  62. package/dist/tui/cockpit/keys.d.ts +8 -0
  63. package/dist/tui/cockpit/keys.js +4 -1
  64. package/dist/tui/cockpit/layout.d.ts +2 -2
  65. package/dist/tui/cockpit/layout.js +2 -2
  66. package/dist/tui/cockpit/live-runtime.js +46 -20
  67. package/dist/tui/cockpit/live-store.d.ts +30 -1
  68. package/dist/tui/cockpit/live-store.js +134 -18
  69. package/dist/tui/cockpit/log-view.d.ts +28 -0
  70. package/dist/tui/cockpit/log-view.js +60 -0
  71. package/dist/tui/cockpit/run-view.js +4 -0
  72. package/dist/tui/cockpit/shell.d.ts +2 -1
  73. package/dist/tui/cockpit/shell.js +6 -4
  74. package/dist/tui/ink/fleet-app.d.ts +12 -6
  75. package/dist/tui/ink/fleet-app.js +17 -11
  76. package/package.json +1 -1
  77. package/skills/tickmarkr-auto/SKILL.md +33 -3
  78. package/skills/tickmarkr-loop/SKILL.md +55 -5
  79. package/skills/tickmarkr-overseer/SKILL.md +83 -8
@@ -12,7 +12,7 @@ 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 `tickmarkr resume <runId>` when no live owner exists;
15
+ `task-approved` receipt, and explicitly launch `tickmarkr resume <runId>` with the host-owned split form below when no live owner exists;
16
16
  approval records permission and never dispatches work itself. Non-TTY readers keep `status`,
17
17
  `report` and default-watch line output, with `--watch --plain` as the TTY fallback.
18
18
  Keep `name: tickmarkr-auto` and the installed links to canonical `skills/` intact.
@@ -92,7 +92,37 @@ After sending, **confirm delivery** by reading the target pane and verifying the
92
92
  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
93
  2. **Compile** — run `tickmarkr compile <spec-or-directory>`. Fix source-spec defects instead of editing the generated graph.
94
94
  3. **Plan** — run `tickmarkr plan`. Review routes, capability-floor warnings, and human gates before execution.
95
- 4. **Run** — run `tickmarkr run`. 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 green clause for you. 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
- 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", and the summary's `failed`, `human`, `blocked` and `pending` buckets are all empty — a run with a parked task is partial, not green. Tickmarkr consolidates accepted work on `tickmarkr/<runId>` and never signs off to the main branch. A human controls any later release merge.
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
+ 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
97
  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
98
  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
+ ## Host-owned daemon and detached beats
101
+
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.
105
+
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.
110
+
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.
115
+
116
+ Start `tickmarkr beat <tier> --seat <seat> --loop` as a DETACHED background process, one per
117
+ (tier, seat), from the repository root. Use setsid plus nohup (or the host's equivalent detached
118
+ session facility), redirect output to a file, and record a pidfile under the repository's state
119
+ dir. Verify ppid 1 and no agent-session ancestor. Never put beats in a harness background task
120
+ or a visible tab. The lifecycle must be idempotent: start checks the pidfile against the live
121
+ process payload and returns an "already running" no-op for the same (repo, tier, seat); status
122
+ checks that payload and beat freshness; stop sends `--stand-down` for the same tier and seat,
123
+ verifies the loop exits within one interval, then removes its pidfile. A stale or reused pid
124
+ must never be killed without matching its live payload. Include a run-end stop item for every
125
+ beat started, also on failure, park or handoff; verify DISARMED and remove retired beat files.
126
+
127
+ Keep only watchers that must WAKE the seat in the harness. Re-arm them on each wake and at
128
+ their harness cap; daemon and beat lifetimes must not depend on that cap.
@@ -88,10 +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** — run `tickmarkr run`. 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 green clause for you. 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
- 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", and the summary's `failed`, `human`, `blocked` and `pending` buckets are all empty — a run with a parked task is partial, not 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.
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.
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
+ ## Host-owned daemon and detached beats
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.
100
+
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
+
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.
110
+
111
+ Start `tickmarkr beat <tier> --seat <seat> --loop` as a DETACHED background process, one per
112
+ (tier, seat), from the repository root. Use setsid plus nohup (or the host's equivalent detached
113
+ session facility), redirect output to a file, and record a pidfile under the repository's state
114
+ dir. Verify ppid 1 and no agent-session ancestor. Never put beats in a harness background task
115
+ or a visible tab. The lifecycle must be idempotent: start checks the pidfile against the live
116
+ process payload and returns an "already running" no-op for the same (repo, tier, seat); status
117
+ checks that payload and beat freshness; stop sends `--stand-down` for the same tier and seat,
118
+ verifies the loop exits within one interval, then removes its pidfile. A stale or reused pid
119
+ must never be killed without matching its live payload. Include a run-end stop item for every
120
+ beat started, also on failure, park or handoff; verify DISARMED and remove retired beat files.
121
+
122
+ Keep only watchers that must WAKE the seat in the harness. Re-arm them on each wake and at
123
+ their harness cap; daemon and beat lifetimes must not depend on that cap.
124
+
95
125
  ## Cockpit, parked decisions and printed twins
96
126
 
97
127
  The shipped cockpit is **1 Home, 4 Run, 5 Evidence**: `tickmarkr ui [runId]` defaults to
@@ -107,7 +137,7 @@ run/task, original park `#L`, actor/reason, exact argv, consequence and enactor.
107
137
  confirms; `n`/Esc cancel; Enter never confirms. Read the receipt's appended `task-approved`
108
138
  line and actor/reason back from the journal. Approval records permission, never dispatch,
109
139
  a passed gate or task completion. With no live owner it says **approved; resume required**:
110
- exit the observer and run `tickmarkr resume <runId>` explicitly. A matching live daemon
140
+ exit the observer and launch `tickmarkr resume <runId>` explicitly with the same host-owned split form above. A matching live daemon
111
141
  enacts at its next task boundary; if a different live run owns the repository lock, wait
112
142
  for that run to end before resuming this one.
113
143
 
@@ -122,8 +152,14 @@ own bound failure token: status prints `failed — T3 — failure <line>@<ts>`,
122
152
  `tickmarkr approve <runId> T3 --recheck --park <line>@<ts>` re-gates its landed commits. Preserve resume refusals and repair the named source/config issue
123
153
  (including deny/prefer conflicts); never edit the compiled graph to force a result. Resume
124
154
  makes CURRENT TIP PENDING; historical GATES RAN does not prove completion. The completed
125
- case has 3/3 recorded merges, a latest run-end, a nonfailed known tip result and empty
126
- `failed`, `human`, `blocked`, `pending` buckets. A mismatched graph is “not comparable.”
155
+ case has 3/3 recorded merges, a latest run-end, a nonfailed known tip result, empty
156
+ `failed`, `human`, `blocked`, `pending` buckets, and CURRENT status reading `outstanding 0`
157
+ — owed checks outstanding empty AND known. Replay D-660 before calling it green: the same
158
+ 3/3 with every bucket empty, after T2's review was waived, reads `outstanding 1 (T2 review)`;
159
+ `resume` exits 0 and its final line says `execution complete; outstanding 1 (T2 review)`, not
160
+ `verified`. It is green only once `tickmarkr verify --record <runId>` discharges that check and
161
+ CURRENT status reads `outstanding 0` — even after run-end, whose record still carries 1.
162
+ `outstanding unknown` (a legacy waiver) is never green. A mismatched graph is “not comparable.”
127
163
 
128
164
  Run offers only validated park verbs: human/attempt-cap/other non-gate parks allow approve;
129
165
  infra allows approve or `--recheck`; review gate-fail allows `--waive`, `--uphold` or
@@ -171,6 +207,20 @@ repair and catalog refresh; default doctor and `--fix` still probe. `scope <inte
171
207
  --preview` performs no probes, model calls or writes; authoring requires confirmation or
172
208
  `--yes`. These actions remain CLI entries until their follow-on views ship.
173
209
 
210
+ **macOS host failure: a launchservicesd restart (D-787/D-789).** If GUI apps suddenly read "The
211
+ application … is not open anymore" during a local suite, launchservicesd crashed and respawned with an
212
+ empty app registry. That was measured on 2026-09-30: `0x1000600b` Mach port exhaustion under node
213
+ `process.title` check-ins, crash at 17:15:00Z and respawn at 17:15:09Z. `tickmarkr doctor` and standalone
214
+ `tickmarkr verify` record the daemon's pid/start through the separate `src/run/launchservices-check.ts`
215
+ module: one bounded `/bin/ps` read with a 5000 ms kill ceiling. doctor's wiring is five lines in
216
+ `src/cli/commands/doctor.ts`. The identity lives on the first `doctor.json` record, and one advisory
217
+ prints and persists when it changed. Unknown evidence keeps the prior identity, and verdicts and retries
218
+ never change. Confirm the identity with `/bin/ps -o pid=,lstart= -p "$(pgrep -x launchservicesd)"`: it
219
+ is young beside Dock and Finder. Confirm the log with `/usr/bin/log show --start '<that lstart minus two
220
+ minutes>'`, looking for `0x1000600b` then `Successfully spawned launchservicesd`. Type the full path: a bare
221
+ `log` is a zsh builtin that prints nothing. Then quit and relaunch the affected apps, or log out /
222
+ restart once the run is stood down; a restart ends agent sessions and wipes `/private/tmp`.
223
+
174
224
  Keep this skill's canonical identity `tickmarkr-loop`; sibling skills link to this
175
225
  walkthrough. In this repository `skills/` is canonical and installed `.claude/skills/`
176
226
  files resolve to it; change the canonical source, never create a second help skill.
@@ -121,13 +121,13 @@ through brief lineage. **An executor choice nobody made is still an executor cho
121
121
  tab OVERSEER; create ONE tab ORCHESTRATOR.
122
122
  **FIVE-TAB CANON (standing operator layout — corrected three times on 2026-07-27, layout approved
123
123
  2026-07-29, re-earned 2026-08-17):**
124
- - `OVERSEER` — you. Do not add a second live run surface: the daemon self-places the shipped board
125
- ABOVE the supervising seat that invokes the run.
126
- - `ORCH` — the orchestrator with the daemon-placed, run-id-pinned shipped board BESIDE it: the board
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
127
127
  takes the RIGHT half of the tab and the orchestrator's own narration keeps the LEFT half. (It was a
128
128
  full-width board above a narration rail until 2026-08-25; the operator changed it, because a task
129
129
  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. Nothing else, ever: a work seat NEVER splits into the ORCH tab.** Operator verbatim:
130
+ run surface. Only the orchestrator, daemon and board belong here: a work seat NEVER splits into the ORCH tab.** Operator verbatim:
131
131
  *"in orch tab should be the orch and the watcher only."* Re-earned 2026-08-17: a planning seat split
132
132
  beside the orchestrator, and the operator caught it, again. The daemon owns this vertical stack and
133
133
  places it the same way at every terminal width; neither the worker-pane halving floor, nor a
@@ -147,12 +147,13 @@ through brief lineage. **An executor choice nobody made is still an executor cho
147
147
  fraction (`ORCH · v1.19 4/5`, updated on every task-done); tickmarkr opens ONE TAB PER TASK, labelled
148
148
  with the task id and holding that task's worker plus its judge/review/consult panes (tickmarkr
149
149
  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`. The daemon self-places the watch board as a horizontal split of the launching terminal (`ORCA_TERMINAL_HANDLE`).
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.
151
151
  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
152
  - **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
153
  - **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:
154
154
  `orca terminal create --worktree path:<repo> --title "ORCH · <version>" --command "<agent-cmd>" --json`
155
155
  For Claude Code: `orca terminal create --worktree path:<repo> --title "ORCH · <version>" --command "claude --model <picked-model> --permission-mode bypassPermissions --settings '{\"promptSuggestionEnabled\":false}'" --json`. For Codex: `orca terminal create --worktree path:<repo> --title "ORCH · <version>" --command "codex --model <picked-model> --dangerously-bypass-approvals-and-sandbox" --json`. Parse `result.terminal.handle` from the create receipt.
156
+ Require the orchestrator to use the host-owned daemon and detached-beat lifecycle below for both run and resume, and to report the lock pid ancestry check.
156
157
  3. **Standing instructions travel as a brief FILE, never as pane text** — PTY input truncates at ~1024B and a
157
158
  truncated brief silently drops policy. Write the full brief to `<repo>/.tickmarkr/overseer/ORCH-BRIEF.md`
158
159
  (inside the tickmarkr state dir — already self-gitignored, no exclude step needed), then announce the brief file:
@@ -196,9 +197,23 @@ through brief lineage. **An executor choice nobody made is still an executor cho
196
197
  The orchestrator's run log, handoff, and plan live inside its sandbox root and are artifacts of that orchestrator.
197
198
  The overseer reads them from there, using the path the orchestrator reports; do not substitute the
198
199
  overseer's repository or a machine-global planning directory. The orchestrator brief must say: if the seat's
199
- sandbox denies writes under `.git`, report the denial to the overseer and stop; the overseer launches the daemon itself;
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;
200
201
  and the daemon host is never sandboxed.
201
202
 
203
+ **Sandbox-denial daemon launch (OVERSEER):** use the ORCH address recorded at seat creation,
204
+ re-resolved by title if stale; the launching overseer's own terminal is not the target.
205
+
206
+ - **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"`.
209
+ Here `$ORCA_TERMINAL_HANDLE` names the OVERSEER's own terminal; use it only when the ORCH
210
+ launches its own daemon, never when the OVERSEER launches on the ORCH's behalf.
211
+ - **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.
214
+
215
+ The same recorded ORCH address is required for a fallback `tickmarkr resume <runId>`.
216
+
202
217
  **On herdr (`HERDR_ENV=1`)**, after every `agent start`, wait for and read the model banner. Only then
203
218
  deliver the brief with `pane run`, then read the transcript back and verify that the brief's first words are
204
219
  present before any deadline is armed. **On Orca (`TERM_PROGRAM=Orca` and non-empty
@@ -291,6 +306,40 @@ Kimi `-y`, artifact plus terminal marker, and verified file-brief delivery. Flee
291
306
  nothing the tab did not already hold. Brief delivery travels as a file announced by `orca terminal send --terminal <handle> --text "<announcement>" --enter --wait-submit 15 --json`. Read `result.send.prompt.stages` and treat the brief as delivered only when a `turn_started` stage is present; `accepted: true` proves input acceptance, not a started turn; never resend on `accepted` alone.
292
307
  For Leg-2 (both hosts), a Codex reviewer under `workspace-write` must be briefed with an in-worktree verdict path such as
293
308
  `<repo>/.tickmarkr/overseer/verdicts/<task>.md`, and its verdict must be written there before it is read.
309
+ ## Host-owned daemon and detached beats
310
+
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.
314
+
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.
323
+
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.
328
+
329
+ Start `tickmarkr beat <tier> --seat <seat> --loop` as a DETACHED background process, one per
330
+ (tier, seat), from the repository root. Use setsid plus nohup (or the host's equivalent detached
331
+ session facility), redirect output to a file, and record a pidfile under the repository's state
332
+ dir. Verify ppid 1 and no agent-session ancestor. Never put beats in a harness background task
333
+ or a visible tab. The lifecycle must be idempotent: start checks the pidfile against the live
334
+ process payload and returns an "already running" no-op for the same (repo, tier, seat); status
335
+ checks that payload and beat freshness; stop sends `--stand-down` for the same tier and seat,
336
+ verifies the loop exits within one interval, then removes its pidfile. A stale or reused pid
337
+ must never be killed without matching its live payload. Include a run-end stop item for every
338
+ beat started, also on failure, park or handoff; verify DISARMED and remove retired beat files.
339
+
340
+ Keep only watchers that must WAKE the seat in the harness. Re-arm them on each wake and at
341
+ their harness cap; daemon and beat lifetimes must not depend on that cap.
342
+
294
343
  ## Supervising tickmarkr as the executor — WHO DOES WHAT
295
344
 
296
345
  When the mission runs `/tickmarkr-auto` (tickmarkr dispatches the workers), supervision changes shape —
@@ -411,6 +460,29 @@ journal tail to decide what happens next, or sweeping orphans — you have taken
411
460
  its death. Liveness comes from the lock's OWN pid (`kill -0`), never a command-name grep. Recovery is
412
461
  `tickmarkr resume <runId>` — **the orchestrator's command, not yours** — and note that resume REPLAYS the
413
462
  journal's `baseRef`, so a fix landed on the base branch is unreachable by the running run.
463
+ - **macOS host: a launchservicesd restart is a host failure, not a kill spree (D-787/D-789).** Measured
464
+ 2026-09-30: during a local full suite launchservicesd crashed at 17:15:00Z (Mach IPC name space
465
+ exhausted, `0x1000600b`, under node `process.title` check-ins) and launchd respawned it at 17:15:09Z
466
+ with an empty app registry. Every GUI app launched before then read "The application … is not open
467
+ anymore", though none had been killed. Replay it from evidence, in this order:
468
+ 1. **Daemon identity.** `/bin/ps -o pid=,lstart= -p "$(pgrep -x launchservicesd)"` beside Dock and
469
+ Finder: a lone young launchservicesd is the tell. Record its pid/lstart before a run and at every lap.
470
+ 2. **Recorded log.** `/usr/bin/log show --start '<that lstart minus two minutes>' --predicate 'process ==
471
+ "launchservicesd" OR process == "launchd"'`, looking for `0x1000600b`, then `service inactive` and
472
+ `Successfully spawned launchservicesd`. Always type the full `/usr/bin/log`: in zsh a bare `log` is a
473
+ builtin that prints nothing, and that silence was once misread as a rotated log (D-787 add.1).
474
+ 3. **Crash report.** `~/Library/Logs/DiagnosticReports/ExcUserFault_node-*.ips` at the same second
475
+ (`uv_set_process_title → _LSApplicationCheckIn`).
476
+ `tickmarkr doctor` and standalone `tickmarkr verify` read step 1 themselves. The check lives in its own
477
+ module, `src/run/launchservices-check.ts`: one bounded `/bin/ps` read with a 5000 ms kill ceiling.
478
+ doctor's wiring is five lines in `src/cli/commands/doctor.ts` around `writeDoctor`. A changed pid or
479
+ start persists one advisory in the `launchServices` object on the FIRST `doctor.json` record. An
480
+ unreadable read is unknown and keeps the prior identity. The advisory never changes a verdict or a
481
+ retry, and the check never reads `/usr/bin/log`, so steps 2–3 stay yours. **The remedy is the
482
+ operator's call, made actionable by that evidence:** quit and relaunch each affected app, or log out /
483
+ restart the Mac. A restart ends every agent session and wipes `/private/tmp`, so stand the run down at
484
+ a clean boundary first and write the handoff; after the reboot, re-resolve seat handles and re-arm
485
+ watchers. Never kill or restart launchservicesd yourself.
414
486
  - **SWEEPING A WORKER ORPHANS ITS CLEANUP, NOT JUST ITS WORK — kill the process GROUP.** Measured
415
487
  2026-08-06: a worker running a legitimate load experiment had spawned CPU burners and held a trailing
416
488
  `kill` line. It was SIGTERM'd as an orphan; **the cleanup never ran**, and **59 surviving burner shells
@@ -1095,9 +1167,11 @@ watching it. Two thirds of that line were constants, not measurements.
1095
1167
  The beat is one shipped command and it arms through two explicit verbs, `--new-arm` and `--loop` —
1096
1168
  never a bare invocation. `--loop` already implies `--new-arm`: it creates a durable arm and beats in
1097
1169
  this process every 10 seconds, exiting on its own within one interval after a stand-down. Run it from
1098
- the repo root as its own `run_in_background` Bash call:
1170
+ the repo root as a detached setsid+nohup process with a pidfile, following the idempotent
1171
+ start/stop/status lifecycle in Host-owned daemon and detached beats; never as a harness task or visible tab:
1099
1172
 
1100
1173
  ```bash
1174
+ # Payload for the detached start above; do not execute as a foreground or harness task.
1101
1175
  cd <repo> && tickmarkr beat overseer --seat <overseer-agent-or-pane> --loop
1102
1176
  cd <repo> && tickmarkr beat overseer --seat <overseer-agent-or-pane> --stand-down # deliberately hand off; --loop exits
1103
1177
  ```
@@ -1156,6 +1230,7 @@ but its process ownership still needs a live check. So:
1156
1230
  legacy bare shell wrapper: unlike `--loop`, it cannot observe the marker and was the form that
1157
1231
  re-armed a stand-down.
1158
1232
 
1233
+ Keep only watchers that must WAKE this seat in the harness, re-armed at their cap.
1159
1234
  Arm the bundled watcher as its OWN Bash call with `run_in_background` — chaining it after other commands
1160
1235
  with `&` orphans it from the wake chain. It prints one wake reason and exits; re-arm after every wake.
1161
1236
 
@@ -1478,7 +1553,7 @@ Create specialist seats using `orca terminal create --worktree path:<repo> --com
1478
1553
  states an act and lets the successor read a fact, and it survived into a handoff exactly once before
1479
1554
  costing two unwatched consult verdicts (OBS-622). The admissible form names the lifetime and the
1480
1555
  work it leaves the successor — *"watchers armed by this session (journal, artifact, dialog, beat);
1481
- they die with it — re-arm on adopt"* — and, per rule 11, says which tier's watchers were NOT armed.
1556
+ harness watchers die with it — re-arm on adopt; detached beats require explicit stop"* — and, per rule 11, says which tier's watchers were NOT armed.
1482
1557
  A detached watcher is the one exception and must be labelled as such, with its heartbeat file, since
1483
1558
  it outlives the seat instead.
1484
1559
  - **SWEEP THE PANES THE RUN LEFT.** A daemon killed by a signal flushes its journal and releases its