tickmarkr 2.8.6 → 2.8.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.
Files changed (40) hide show
  1. package/dist/adapters/grok.js +5 -9
  2. package/dist/adapters/prompt.js +9 -1
  3. package/dist/adapters/types.d.ts +1 -0
  4. package/dist/adapters/types.js +12 -0
  5. package/dist/cli/commands/compile.js +1 -1
  6. package/dist/cli/commands/init.d.ts +10 -0
  7. package/dist/cli/commands/init.js +13 -3
  8. package/dist/cli/commands/report.js +21 -12
  9. package/dist/cli/commands/status.js +14 -2
  10. package/dist/cli/commands/verify.js +29 -5
  11. package/dist/cli/commands/version.d.ts +1 -1
  12. package/dist/cli/commands/version.js +36 -2
  13. package/dist/cli/help.d.ts +1 -0
  14. package/dist/cli/help.js +6 -3
  15. package/dist/compile/collateral.d.ts +3 -1
  16. package/dist/compile/collateral.js +8 -16
  17. package/dist/compile/native.js +7 -2
  18. package/dist/drivers/subprocess.js +9 -4
  19. package/dist/gates/acceptance.d.ts +8 -0
  20. package/dist/gates/acceptance.js +54 -37
  21. package/dist/gates/baseline.js +47 -7
  22. package/dist/gates/run-gates.js +31 -22
  23. package/dist/report/bundle.d.ts +5 -2
  24. package/dist/report/bundle.js +11 -6
  25. package/dist/report/compare.d.ts +9 -0
  26. package/dist/report/compare.js +58 -40
  27. package/dist/report/operator-record.js +4 -3
  28. package/dist/run/daemon.d.ts +7 -3
  29. package/dist/run/daemon.js +157 -58
  30. package/dist/run/git.d.ts +5 -0
  31. package/dist/run/git.js +51 -1
  32. package/dist/run/journal.js +11 -2
  33. package/dist/run/repair-disposition.d.ts +2 -2
  34. package/dist/run/repair-disposition.js +35 -4
  35. package/dist/run/stall.d.ts +2 -0
  36. package/dist/run/stall.js +12 -4
  37. package/package.json +1 -1
  38. package/skills/tickmarkr-auto/SKILL.md +10 -8
  39. package/skills/tickmarkr-loop/SKILL.md +10 -8
  40. package/skills/tickmarkr-overseer/SKILL.md +10 -8
@@ -4,7 +4,7 @@ import type { GateResult } from "../gates/types.js";
4
4
  export type RepairDispositionKind = "fund-repair" | "scope-request" | "authoring" | "none";
5
5
  export interface RepairDisposition {
6
6
  kind: RepairDispositionKind;
7
- /** validated inventory paths only — the sole paths a caller may put in an executable command */
7
+ /** Queue row 235 (D-2134): validated inventory paths or explicitly declared oracle landings. */
8
8
  paths: string[];
9
9
  reason: string;
10
10
  /** which evidence decided: a gate name, "worker" for a refusal, absent for the ordinary disposition */
@@ -33,7 +33,7 @@ export interface RepairDispositionInput {
33
33
  /** task tree plus diff, deleted tracked paths included */
34
34
  inventory: Iterable<string>;
35
35
  }
36
- /** Lexing never starts in the middle of a word (same lexer as the daemon's scope-red disposition). */
36
+ /** Queue row 230: tsc's position, numeric or baseline-normalized (#), ends a path; named parentheses stay. */
37
37
  export declare const namedPaths: (text: string) => string[];
38
38
  /** Binds candidates to the SUPPLIED inventory and nothing else: an exact path, else a unique
39
39
  * path-suffix. Anything unbound or bound twice comes back as a non-executable diagnostic. */
@@ -1,8 +1,8 @@
1
1
  import { filesGlob } from "../graph/files-glob.js";
2
2
  const failed = (g) => !(g.pass || g.meta?.skipped === true) || g.meta?.infra === true;
3
3
  const REFUSAL_RE = /outside|out.of.scope|allowlist|scope expansion|not (?:in|own)|unowned/i;
4
- /** Lexing never starts in the middle of a word (same lexer as the daemon's scope-red disposition). */
5
- export const namedPaths = (text) => [...text.matchAll(/(?:^|[\s`'"])((?:[A-Za-z0-9_@.()[\]-]+\/)+[A-Za-z0-9_@.[\]-]+|[A-Za-z0-9_@-]+(?:\.[A-Za-z0-9_-]+)+)(?=$|[\s`'"),:;.!?])/g)]
4
+ /** Queue row 230: tsc's position, numeric or baseline-normalized (#), ends a path; named parentheses stay. */
5
+ export const namedPaths = (text) => [...text.matchAll(/(?:^|[\s`'"])((?:[A-Za-z0-9_@.()[\]-]+\/)+(?:[A-Za-z0-9_@.[\]-]|\([A-Za-z0-9_@.[\]-]*\))+|(?:[A-Za-z0-9_@-]|\([A-Za-z0-9_@.[\]-]*\))+(?:\.(?:[A-Za-z0-9_-]|\([A-Za-z0-9_@.[\]-]*\))+)+)(?=$|[\s`'"),:;.!?]|\((?:\d+|#),\s*(?:\d+|#)\))/g)]
6
6
  .map((match) => match[1].replace(/^\.\//, "").replace(/\.$/, ""))
7
7
  .filter((path) => !path.split("/").includes(".."));
8
8
  /** Binds candidates to the SUPPLIED inventory and nothing else: an exact path, else a unique
@@ -48,12 +48,42 @@ export function classifyRepairDisposition(input) {
48
48
  return { paths: r.resolved.filter((path) => !allowed(path)), diagnostics: r.diagnostics };
49
49
  };
50
50
  const owned = (text) => resolveScopeHints(namedPaths(text), inventory).resolved.some(allowed);
51
+ // Queue row 235 (D-2134): acceptance prose quotes declarations; only execution and typed evidence
52
+ // attribute a repair. A misplaced declared landing is authoritative even when it was never written.
53
+ const acceptanceEvidence = (g) => {
54
+ if (Array.isArray(g.meta?.oracles)) {
55
+ const paths = [];
56
+ const landings = [];
57
+ for (const oracle of g.meta.oracles) {
58
+ if (!oracle || typeof oracle !== "object")
59
+ continue;
60
+ if (typeof oracle.output === "string")
61
+ paths.push(...namedPaths(oracle.output));
62
+ if (["absent", "elsewhere", "untitled"].includes(oracle.failure) && typeof oracle.landing === "string" && !allowed(oracle.landing)) {
63
+ landings.push(oracle.landing);
64
+ }
65
+ }
66
+ return { paths, landings };
67
+ }
68
+ const judgment = g.meta?.judgment;
69
+ const criteria = Array.isArray(judgment) ? judgment
70
+ : judgment && typeof judgment === "object" && "criteria" in judgment ? judgment.criteria : undefined;
71
+ const paths = Array.isArray(criteria) ? criteria.flatMap((row) => row?.met === false && Array.isArray(row.paths) ? row.paths.filter((path) => typeof path === "string") : []) : [];
72
+ return { paths, landings: [] };
73
+ };
51
74
  const refusal = input.refusalSummary !== undefined && REFUSAL_RE.test(input.refusalSummary)
52
75
  ? unowned(input.refusalSummary) : { paths: [], diagnostics: [] };
53
76
  // A test gate names where the ASSERTION lives — a detection site, not the code that needs fixing.
54
77
  const sites = reds.filter((g) => g.gate === "test").map((g) => unowned(g.details));
55
78
  const others = reds.filter((g) => g.gate !== "test");
56
- const hints = others.map((g) => unowned(g.details));
79
+ const evidence = others.map((g) => g.gate === "acceptance" ? acceptanceEvidence(g) : undefined);
80
+ const hints = others.map((g, i) => {
81
+ const e = evidence[i];
82
+ if (!e)
83
+ return unowned(g.details);
84
+ const r = resolveScopeHints(e.paths.filter((path) => !allowed(path)), inventory);
85
+ return { paths: [...e.landings, ...r.resolved.filter((path) => !allowed(path))], diagnostics: r.diagnostics };
86
+ });
57
87
  const diagnostics = [refusal, ...sites, ...hints].flatMap((r) => r.diagnostics)
58
88
  .filter((d, i, all) => all.findIndex((o) => o.candidate === d.candidate) === i);
59
89
  const uniq = (rs) => [...new Set(rs.flatMap((r) => r.paths))].sort();
@@ -62,7 +92,8 @@ export function classifyRepairDisposition(input) {
62
92
  reason: `worker refusal names unowned paths: ${refusal.paths.join(", ")}` };
63
93
  }
64
94
  const repair = uniq(hints);
65
- if (repair.length && !others.some((g) => owned(g.details))) {
95
+ if (repair.length && !others.some((g, i) => evidence[i]
96
+ ? resolveScopeHints(evidence[i].paths, inventory).resolved.some(allowed) : owned(g.details))) {
66
97
  const review = others.some((g) => g.gate === "review");
67
98
  return { kind: review ? "scope-request" : "authoring", paths: repair, source: others[0].gate, diagnostics,
68
99
  reason: `files[] repair hint: ${repair.join(", ")}` };
@@ -3,6 +3,7 @@
3
3
  export declare function normalizeStallSnapshot(text: string): string;
4
4
  export interface StallProgressSample {
5
5
  paneText: string;
6
+ receivedBytes?: number;
6
7
  seedSubmitted?: boolean;
7
8
  contextTokens?: number;
8
9
  }
@@ -102,6 +103,7 @@ export declare const PANE_READ_ROWS = 1000;
102
103
  */
103
104
  export declare class StallProgressTracker {
104
105
  private transcriptRows;
106
+ private receivedBytes;
105
107
  private rawWindowLines;
106
108
  private seedSubmitted;
107
109
  private contextTokens;
package/dist/run/stall.js CHANGED
@@ -613,6 +613,7 @@ export const PANE_READ_ROWS = 1000;
613
613
  */
614
614
  export class StallProgressTracker {
615
615
  transcriptRows = 0; // non-empty high-water over the bounded read — the growth signal
616
+ receivedBytes = 0;
616
617
  rawWindowLines = 0; // raw-line high-water — the SATURATION signal (see the getter)
617
618
  seedSubmitted = false;
618
619
  contextTokens;
@@ -654,6 +655,14 @@ export class StallProgressTracker {
654
655
  rowsAdvanced = true;
655
656
  this.rowGrowthAt = now; // raw signal — advances even when the report below is suppressed
656
657
  }
658
+ // Queue row 233 (D-2130): print-mode progress is the driver's received-byte count, never a
659
+ // row high-water over the bounded tail. A driver reporting no count keeps the row measure.
660
+ const bytes = sample.receivedBytes;
661
+ let outputAdvanced = rowsAdvanced;
662
+ if (bytes !== undefined && Number.isFinite(bytes) && bytes >= 0) {
663
+ outputAdvanced = bytes > this.receivedBytes;
664
+ this.receivedBytes = Math.max(this.receivedBytes, bytes);
665
+ }
657
666
  let seedAdvanced = false;
658
667
  if (sample.seedSubmitted && !this.seedSubmitted) {
659
668
  this.seedSubmitted = true;
@@ -674,10 +683,9 @@ export class StallProgressTracker {
674
683
  }
675
684
  if (tokensAdvanced || seedAdvanced)
676
685
  return true;
677
- if (rowsAdvanced) {
678
- // T1: row growth past the flat-token cap is cosmetic — the paid-work counter has not moved,
679
- // so the inactivity window must NOT re-arm on it. A tracker that never sees a token sample
680
- // (unmetered adapter) keeps the old row-growth behavior.
686
+ if (outputAdvanced) {
687
+ // Queue row 233 (D-2130): both output measures obey the existing flat-token cap;
688
+ // an unmetered adapter can keep re-arming on output indefinitely, up to its hard deadline.
681
689
  if (this.lastTokenGrowthAt !== undefined && now - this.lastTokenGrowthAt >= rowRearmTokenFlatMs)
682
690
  return false;
683
691
  return true;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tickmarkr",
3
- "version": "2.8.6",
3
+ "version": "2.8.8",
4
4
  "description": "Spec in, verified work out.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -20,16 +20,18 @@ Keep `name: tickmarkr-auto` and the installed links to canonical `skills/` intac
20
20
 
21
21
  ## Two-tier by default — role check before the loop
22
22
 
23
+ `<label>` means the version only in the tickmarkr source repository (`package.json` named `tickmarkr`); elsewhere it means the mission or run, never the installed binary version.
24
+
23
25
  When working in a multi-agent terminal environment (herdr or Orca), decide your role before starting:
24
26
 
25
- - **Orchestrator:** your session was started to execute the mission. Rename your own tab/pane `ORCH · <version>` (short labels: ≤20 chars, `ROLE · token`) and run the loop below.
27
+ - **Orchestrator:** your session was started to execute the mission. Rename your own tab/pane `ORCH · <label>` (short labels: ≤20 chars, `ROLE · token`) and run the loop below.
26
28
  - **Supervisor with a live orchestrator:** do not start a second run. Relay the mission to the existing orchestrator with a [verified handoff](#verified-handoffs-agent-to-agent-messaging), then supervise it as OVERSEER.
27
- - **Primary session without an orchestrator:** rename your own tab `OVERSEER · <version>` and your agent `overseer`, spawn one child orchestration session with your host's launch form, label its tab `ORCH · <version>` and name its agent, give it the mission and these rules verbatim, then supervise it. Do not drive a duplicate single-tier run yourself. Before spawning, confirm any PREVIOUS orchestrator has stood down (monitors stopped, input box empty — dim ghost-text suggestions are UI, not queued input; ANSI-verify before alarming) and close its tab — the journal, records, and ledger hold the story; scrollback is disposable.
28
- - **On herdr (`HERDR_ENV=1`)**: **spawning on current herdr is two-step** — the one-shot `agent start --cwd/--tab/--no-focus` form was removed in the herdr CLI redesign and now fails with `unknown option` (OBS-138). First create the pane: `herdr tab create --workspace <ws> --cwd <repo> --label "ORCH · <version>"` (tab create does not steal focus unless `--focus` is passed; parse `result.root_pane.pane_id` from its JSON), then start the agent in it:
29
+ - **Primary session without an orchestrator:** rename your own tab `OVERSEER · <label>` and your agent `overseer`, spawn one child orchestration session with your host's launch form, label its tab `ORCH · <label>` and name its agent, give it the mission and these rules verbatim, then supervise it. Do not drive a duplicate single-tier run yourself. Before spawning, confirm any PREVIOUS orchestrator has stood down (monitors stopped, input box empty — dim ghost-text suggestions are UI, not queued input; ANSI-verify before alarming) and close its tab — the journal, records, and ledger hold the story; scrollback is disposable.
30
+ - **On herdr (`HERDR_ENV=1`)**: **spawning on current herdr is two-step** — the one-shot `agent start --cwd/--tab/--no-focus` form was removed in the herdr CLI redesign and now fails with `unknown option` (OBS-138). First create the pane: `herdr tab create --workspace <ws> --cwd <repo> --label "ORCH · <label>"` (tab create does not steal focus unless `--focus` is passed; parse `result.root_pane.pane_id` from its JSON), then start the agent in it:
29
31
  - **Claude Code:** `herdr agent start orchestrator --kind claude --pane <root-pane-id> -- --permission-mode bypassPermissions`
30
32
  - **Codex:** `herdr agent start orchestrator --kind codex --pane <root-pane-id> -- --dangerously-bypass-approvals-and-sandbox` — the unsandboxed flag is REQUIRED, not optional: codex's `workspace-write` sandbox keeps `.git` refs read-only, so a sandboxed orchestrator's `tickmarkr run` dies at integration-branch creation (`git worktree add` cannot lock the ref). Do not downgrade this flag; the herdr pane and repo scope are the containment.
31
33
  - **Auxiliary seats run as the CLI's interactive TUI in their visible pane — never headless** (`claude -p` / `codex exec`): headless buffers output until exit so the pane renders idle for the entire run, is blind to SessionStart hook errors (a broken and a fixed hook both return green), and gives a stall watcher no midpoint — silent-time equals lifetime. Headless is for exit-code probes only (a quota check that wants `rc`), never for work anyone must watch.
32
- - **On Orca (`TERM_PROGRAM=Orca` and non-empty `ORCA_TERMINAL_HANDLE`)**: spawn it with `orca terminal create --worktree path:<repo> --title "ORCH · <version>" --command "<agent-cmd>" --json` and parse `result.terminal.handle` from the create receipt. **Auxiliary seats run as the CLI's interactive TUI in their visible terminal — never headless**, for the reasons the herdr branch gives.
34
+ - **On Orca (`TERM_PROGRAM=Orca` and non-empty `ORCA_TERMINAL_HANDLE`)**: spawn it with `orca terminal create --worktree path:<repo> --title "ORCH · <label>" --command "<agent-cmd>" --json` and parse `result.terminal.handle` from the create receipt. **Auxiliary seats run as the CLI's interactive TUI in their visible terminal — never headless**, for the reasons the herdr branch gives.
33
35
  - **Auxiliary agents you spawn (consultants, reviewers, scouts) follow the same forms.** Never launch a claude session in plan mode or default permission mode for autonomous work — both stall on per-command approval prompts nobody is watching; claude is always `--permission-mode bypassPermissions` (tickmarkr's own adapter uses exactly this for workers, judges, and consults). A read-only codex consultant may use `--sandbox read-only`; any codex session that must touch git needs the unsandboxed flag above.
34
36
 
35
37
  With neither herdr nor Orca there is no role to decide and no seat to spawn: follow [On a plain host (neither herdr nor Orca)](#on-a-plain-host-neither-herdr-nor-orca) and run the loop directly, single-tier.
@@ -83,11 +85,11 @@ Run every requested target in order without seeking routine confirmation. Stop o
83
85
 
84
86
  ## Binary preflight (before compile or run)
85
87
 
86
- Before `tickmarkr compile` or `tickmarkr run`, compare the installed binary against the repository's `package.json` version:
88
+ Before `tickmarkr compile` or `tickmarkr run`:
87
89
 
88
- 1. Run `tickmarkr version` (one line, machine-parseable).
89
- 2. Read the `version` field from the repository's `package.json`.
90
- 3. If the binary and repository do not **agree on the entire version** (including the patch; e.g. binary `2.1.0` vs repo `2.1.1`), **stop immediately** and tell the operator to update the global install (`npm i -g tickmarkr@latest`), or to install this repository's build as a REAL COPY — `npm pack`, then `npm i -g ./<tarball>`. Do not compile, plan, or run on hope.
90
+ 1. Run `tickmarkr version --check` and **stop immediately on a non-zero exit**.
91
+ 2. In a consumer repository, it compares installed agent guidance with this binary's packaged block and skills, never the app's manifest version. Refresh drifted guidance with `tickmarkr init --agent --force`.
92
+ 3. Only in the tickmarkr source repository (`package.json` named `tickmarkr`), the check compares its version with the binary. If the binary and repository do not **agree on the entire version** (including the patch; e.g. binary `2.1.0` vs repo `2.1.1`), **stop immediately** and tell the operator to update the global install (`npm i -g tickmarkr@latest`), or to install this repository's build as a REAL COPY — `npm pack`, then `npm i -g ./<tarball>`. Do not compile, plan, or run on hope.
91
93
  > ⚠ **Never `npm i -g .` on the repository directory, and never link it.** npm SYMLINKS a directory install, which makes the working tree itself the machine-wide binary: every later build — including a gate's own `npm run build` — silently hot-swaps the CLI for every repository on the machine, with no version change to notice it by. Measured 2026-08-29: a verify build gate rewrote the shared binary while another repository's daemon was mid-run against it, and a positive control that rebuilds at a pre-fix ref would have installed the very defect it was proving fixed, machine-wide (OBS-771). Verify an install by comparing the global and repo **inodes** — they must DIFFER — never by `tickmarkr version`, which cannot go red when nothing is bumped.
92
94
 
93
95
  A stale binary silently skips daemon gates shipped in newer releases — the v1.38 run exposed this when a global `1.36.0` binary missed the daemon tip-verify gate entirely (OBS-38). Preflight failure is always stop-and-report; never proceed-and-hope.
@@ -9,16 +9,18 @@ Use this for any spec that `tickmarkr compile` accepts. It is SDD-agnostic: use
9
9
 
10
10
  ## Two-tier by default — role check before the loop
11
11
 
12
+ `<label>` means the version only in the tickmarkr source repository (`package.json` named `tickmarkr`); elsewhere it means the mission or run, never the installed binary version.
13
+
12
14
  When working in a multi-agent terminal environment (herdr or Orca), decide your role before starting:
13
15
 
14
- - **Orchestrator:** your session was started to execute the mission. Rename your own tab/pane `ORCH · <version>` (short labels: ≤20 chars, `ROLE · token`) and run the loop below.
16
+ - **Orchestrator:** your session was started to execute the mission. Rename your own tab/pane `ORCH · <label>` (short labels: ≤20 chars, `ROLE · token`) and run the loop below.
15
17
  - **Supervisor with a live orchestrator:** do not start a second run. Relay the mission to the existing orchestrator with a [verified handoff](#verified-handoffs-agent-to-agent-messaging), then supervise it as OVERSEER.
16
- - **Primary session without an orchestrator:** rename your own tab `OVERSEER · <version>` and your agent `overseer`, spawn one child orchestration session with your host's launch form, label its tab `ORCH · <version>` and name its agent, give it the mission and these rules verbatim, then supervise it. Do not drive a duplicate single-tier run yourself. Before spawning, confirm any PREVIOUS orchestrator has [stood down](#stand-down-mission-end-and-retirement) and close its tab.
17
- - **On herdr (`HERDR_ENV=1`)**: **spawning on current herdr is two-step** — the one-shot `agent start --cwd/--tab/--no-focus` form was removed in the herdr CLI redesign and now fails with `unknown option` (OBS-138). First create the pane: `herdr tab create --workspace <ws> --cwd <repo> --label "ORCH · <version>"` (tab create does not steal focus unless `--focus` is passed; parse `result.root_pane.pane_id` from its JSON), then start the agent in it:
18
+ - **Primary session without an orchestrator:** rename your own tab `OVERSEER · <label>` and your agent `overseer`, spawn one child orchestration session with your host's launch form, label its tab `ORCH · <label>` and name its agent, give it the mission and these rules verbatim, then supervise it. Do not drive a duplicate single-tier run yourself. Before spawning, confirm any PREVIOUS orchestrator has [stood down](#stand-down-mission-end-and-retirement) and close its tab.
19
+ - **On herdr (`HERDR_ENV=1`)**: **spawning on current herdr is two-step** — the one-shot `agent start --cwd/--tab/--no-focus` form was removed in the herdr CLI redesign and now fails with `unknown option` (OBS-138). First create the pane: `herdr tab create --workspace <ws> --cwd <repo> --label "ORCH · <label>"` (tab create does not steal focus unless `--focus` is passed; parse `result.root_pane.pane_id` from its JSON), then start the agent in it:
18
20
  - **Claude Code:** `herdr agent start orchestrator --kind claude --pane <root-pane-id> -- --permission-mode bypassPermissions`
19
21
  - **Codex:** `herdr agent start orchestrator --kind codex --pane <root-pane-id> -- --dangerously-bypass-approvals-and-sandbox` — the unsandboxed flag is REQUIRED, not optional: codex's `workspace-write` sandbox keeps `.git` refs read-only, so a sandboxed orchestrator's `tickmarkr run` dies at integration-branch creation (`git worktree add` cannot lock the ref). Do not downgrade this flag; the herdr pane and repo scope are the containment.
20
22
  - **Auxiliary seats run as the CLI's interactive TUI in their visible pane — never headless** (`claude -p` / `codex exec`): headless buffers output until exit so the pane renders idle for the entire run, is blind to SessionStart hook errors (a broken and a fixed hook both return green), and gives a stall watcher no midpoint — silent-time equals lifetime. Headless is for exit-code probes only (a quota check that wants `rc`), never for work anyone must watch.
21
- - **On Orca (`TERM_PROGRAM=Orca` and non-empty `ORCA_TERMINAL_HANDLE`)**: spawn it with `orca terminal create --worktree path:<repo> --title "ORCH · <version>" --command "<agent-cmd>" --json` and parse `result.terminal.handle` from the create receipt. **Auxiliary seats run as the CLI's interactive TUI in their visible terminal — never headless**, for the reasons the herdr branch gives.
23
+ - **On Orca (`TERM_PROGRAM=Orca` and non-empty `ORCA_TERMINAL_HANDLE`)**: spawn it with `orca terminal create --worktree path:<repo> --title "ORCH · <label>" --command "<agent-cmd>" --json` and parse `result.terminal.handle` from the create receipt. **Auxiliary seats run as the CLI's interactive TUI in their visible terminal — never headless**, for the reasons the herdr branch gives.
22
24
  - **Auxiliary agents you spawn (consultants, reviewers, scouts) follow the same forms.** Never launch a claude session in plan mode or default permission mode for autonomous work — both stall on per-command approval prompts nobody is watching; claude is always `--permission-mode bypassPermissions` (tickmarkr's own adapter uses exactly this for workers, judges, and consults). A read-only codex consultant may use `--sandbox read-only`; any codex session that must touch git needs the unsandboxed flag above.
23
25
 
24
26
  With neither herdr nor Orca there is no role to decide and no seat to spawn: follow [On a plain host (neither herdr nor Orca)](#on-a-plain-host-neither-herdr-nor-orca) and run the loop directly, single-tier.
@@ -69,11 +71,11 @@ Proceed through the loop without seeking routine confirmation. Stop only for a b
69
71
 
70
72
  ## Binary preflight (before compile or run)
71
73
 
72
- Before `tickmarkr compile` or `tickmarkr run`, compare the installed binary against the repository's `package.json` version:
74
+ Before `tickmarkr compile` or `tickmarkr run`:
73
75
 
74
- 1. Run `tickmarkr version` (one line, machine-parseable).
75
- 2. Read the `version` field from the repository's `package.json`.
76
- 3. If the binary and repository do not **agree on the entire version** (including the patch; e.g. binary `2.1.0` vs repo `2.1.1`), **stop immediately** and tell the operator to update the global install (`npm i -g tickmarkr@latest`), or to install this repository's build as a REAL COPY — `npm pack`, then `npm i -g ./<tarball>`. Do not compile, plan, or run on hope.
76
+ 1. Run `tickmarkr version --check` and **stop immediately on a non-zero exit**.
77
+ 2. In a consumer repository, it compares installed agent guidance with this binary's packaged block and skills, never the app's manifest version. Refresh drifted guidance with `tickmarkr init --agent --force`.
78
+ 3. Only in the tickmarkr source repository (`package.json` named `tickmarkr`), the check compares its version with the binary. If the binary and repository do not **agree on the entire version** (including the patch; e.g. binary `2.1.0` vs repo `2.1.1`), **stop immediately** and tell the operator to update the global install (`npm i -g tickmarkr@latest`), or to install this repository's build as a REAL COPY — `npm pack`, then `npm i -g ./<tarball>`. Do not compile, plan, or run on hope.
77
79
  > ⚠ **Never `npm i -g .` on the repository directory, and never link it.** npm SYMLINKS a directory install, which makes the working tree itself the machine-wide binary: every later build — including a gate's own `npm run build` — silently hot-swaps the CLI for every repository on the machine, with no version change to notice it by. Measured 2026-08-29: a verify build gate rewrote the shared binary while another repository's daemon was mid-run against it, and a positive control that rebuilds at a pre-fix ref would have installed the very defect it was proving fixed, machine-wide (OBS-771). Verify an install by comparing the global and repo **inodes** — they must DIFFER — never by `tickmarkr version`, which cannot go red when nothing is bumped.
78
80
 
79
81
  A stale binary silently skips daemon gates shipped in newer releases — the v1.38 run exposed this when a global `1.36.0` binary missed the daemon tip-verify gate entirely (OBS-38). Preflight failure is always stop-and-report; never proceed-and-hope.
@@ -5,6 +5,8 @@ description: "Use when the user asks to oversee/supervise/babysit an autonomous
5
5
 
6
6
  # Overseer (tickmarkr)
7
7
 
8
+ `<label>` means the version only in the tickmarkr source repository (`package.json` named `tickmarkr`); elsewhere it means the mission or run, never the installed binary version.
9
+
8
10
  Become the OVERSEER for this workspace or session. Do no heavy work directly — build and supervise a two-tier
9
11
  hierarchy of VISIBLE agents (you → orchestrator → tickmarkr's own worker fleet), and route human decisions
10
12
  to the user with evidence.
@@ -203,15 +205,15 @@ replaces the two-tier setup.
203
205
  **Live tab labels (standing operator rule, 2026-07-12):** on every decision or state change (role
204
206
  handoff, task done/merged, run end) rename the affected tabs — and keep labels SHORT: the role as the
205
207
  main name plus at most ONE hot-state token. Vocabulary: ORCH carries the milestone and progress
206
- fraction (`ORCH · v1.19 4/5`, updated on every task-done); tickmarkr opens ONE TAB PER TASK, labelled
208
+ fraction (`ORCH · <label> 4/5`, updated on every task-done); tickmarkr opens ONE TAB PER TASK, labelled
207
209
  with the task id and holding that task's worker plus its judge/review/consult panes (tickmarkr
208
210
  updates it). Never long context strings or ✓-chains.
209
- - **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.
211
+ - **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 · <label>"` (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.
210
212
  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.
211
- - **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.
213
+ - **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 · <label>"` 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.
212
214
  - **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:
213
- `orca terminal create --worktree path:<repo> --title "ORCH · <version>" --command "<agent-cmd>" --json`
214
- 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.
215
+ `orca terminal create --worktree path:<repo> --title "ORCH · <label>" --command "<agent-cmd>" --json`
216
+ For Claude Code: `orca terminal create --worktree path:<repo> --title "ORCH · <label>" --command "claude --model <picked-model> --permission-mode bypassPermissions --settings '{\"promptSuggestionEnabled\":false}'" --json`. For Codex: `orca terminal create --worktree path:<repo> --title "ORCH · <label>" --command "codex --model <picked-model> --dangerously-bypass-approvals-and-sandbox" --json`. Parse `result.terminal.handle` from the create receipt.
215
217
  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.
216
218
  3. **Standing instructions travel as a brief FILE, never as pane text** — PTY input truncates at ~1024B and a
217
219
  truncated brief silently drops policy. Write the full brief to `<repo>/.tickmarkr/overseer/ORCH-BRIEF.md`
@@ -221,7 +223,7 @@ replaces the two-tier setup.
221
223
  - **On Orca (`TERM_PROGRAM=Orca` and non-empty `ORCA_TERMINAL_HANDLE`)**: Briefs travel as a file announced by `terminal send` with enter and a submit wait whose receipt is read for observed submission rather than input acceptance:
222
224
  `orca terminal send --terminal <handle> --text "Read .tickmarkr/overseer/ORCH-BRIEF.md and follow it exactly." --enter --wait-submit 15 --json`
223
225
  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.
224
- The brief MUST contain: the mission, the five-tab canon (on herdr) or seat layout (on Orca), the pane mechanics below, rules 1–2, the GSD-leg rules
226
+ The brief MUST contain: **Seat hygiene — in a multi-tier session the tiers clear each other at 50% context or more after writing a handoff; a new spec or run is a mission boundary, so hand off and clear before the next mission; copy this duty into every brief and handoff; on a single-tier plain host write your handoff and ask the operator to clear the session**, then the mission, the five-tab canon (on herdr) or seat layout (on Orca), the pane mechanics below, rules 1–2, the GSD-leg rules
225
227
  (below) whenever the mission dispatches `/gsd:*` legs, and require a verbatim one-sentence
226
228
  acknowledgment of the human-checkpoint rule before anything is dispatched.
227
229
  *method guards* in that brief (how to know a thing, not what is true of this spec), and deleting them
@@ -329,7 +331,7 @@ Kimi `-y`, artifact plus terminal marker, and verified file-brief delivery. Flee
329
331
  a `git worktree add` the overseer made for a spec branch, a throwaway clone — `terminal create` hangs and
330
332
  returns "Timed out waiting for terminal handle after creation". The lawful form for such a checkout is a
331
333
  terminal in a managed worktree with a cwd change in the command:
332
- `orca terminal create --worktree path:<managed-repo> --title "ORCH · <version>" --command "cd <checkout> && <agent-cmd>" --json`.
334
+ `orca terminal create --worktree path:<managed-repo> --title "ORCH · <label>" --command "cd <checkout> && <agent-cmd>" --json`.
333
335
  **An Orca-hosted run's checkout is created by Orca** (`orca worktree create --repo id:<repo> --base-branch <spec-branch>`),
334
336
  never by `git worktree add`: the orca driver asks `orca worktree current` from inside every task worktree and refuses
335
337
  (`selector_not_found`) on an unmanaged checkout — every task fails pre-launch and the board cannot place (OBS-1061,
@@ -353,7 +355,7 @@ Kimi `-y`, artifact plus terminal marker, and verified file-brief delivery. Flee
353
355
  Code's turn summary, Codex's last prompt) and is NOT the seat's name. This is vendor-neutral by
354
356
  construction — no per-vendor env var, no agent command, no launch-form ritual — which is what makes
355
357
  it the same law as Herdr's tab label. So: **the overseer names its OWN seat at Setup 1** with
356
- `orca terminal rename --terminal "$ORCA_TERMINAL_HANDLE" --title "OVERSEER · <version>"` (the seat
358
+ `orca terminal rename --terminal "$ORCA_TERMINAL_HANDLE" --title "OVERSEER · <label>"` (the seat
357
359
  it was invoked in is the one seat nobody else launches), every seat it spawns is named by
358
360
  `create --title`, and a live-label change (progress fraction, hot-state token) is `rename` again.
359
361
  **Prove a name by the field that holds:** `tabs[].title` in