tickmarkr 2.8.5 → 2.8.7
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/adapters/codex-commit-check.d.ts +4 -3
- package/dist/adapters/codex-commit-check.js +42 -28
- package/dist/adapters/codex.d.ts +2 -2
- package/dist/adapters/codex.js +38 -32
- package/dist/adapters/grok.js +5 -9
- package/dist/adapters/model-lints.js +4 -3
- package/dist/adapters/prompt.js +9 -1
- package/dist/adapters/types.d.ts +10 -1
- package/dist/adapters/types.js +12 -0
- package/dist/cli/commands/approve.js +9 -28
- package/dist/cli/commands/init.d.ts +10 -0
- package/dist/cli/commands/init.js +38 -20
- package/dist/cli/commands/status.js +9 -6
- package/dist/cli/commands/version.d.ts +1 -1
- package/dist/cli/commands/version.js +36 -2
- package/dist/cli/help.d.ts +1 -0
- package/dist/cli/help.js +6 -3
- package/dist/compile/gsd.js +4 -3
- package/dist/compile/native.js +8 -6
- package/dist/drivers/subprocess.js +9 -4
- package/dist/gates/baseline.js +51 -10
- package/dist/gates/cache.js +2 -4
- package/dist/graph/schema.js +3 -1
- package/dist/run/daemon.js +147 -35
- package/dist/run/git-trust.d.ts +11 -0
- package/dist/run/git-trust.js +1 -1
- package/dist/run/git.d.ts +18 -1
- package/dist/run/git.js +73 -2
- package/dist/run/journal.d.ts +15 -1
- package/dist/run/journal.js +43 -6
- package/dist/run/repair-disposition.d.ts +1 -1
- package/dist/run/repair-disposition.js +2 -2
- package/dist/run/stall.d.ts +2 -0
- package/dist/run/stall.js +12 -4
- package/package.json +1 -1
- package/skills/tickmarkr-auto/SKILL.md +10 -8
- package/skills/tickmarkr-loop/SKILL.md +10 -8
- package/skills/tickmarkr-overseer/SKILL.md +10 -8
package/dist/run/journal.js
CHANGED
|
@@ -97,17 +97,44 @@ export function recordedBinding(value) {
|
|
|
97
97
|
const { line, ts } = value;
|
|
98
98
|
return typeof line === "number" && Number.isSafeInteger(line) && line > 0 && typeof ts === "string" ? { line, ts } : undefined;
|
|
99
99
|
}
|
|
100
|
-
/**
|
|
100
|
+
/**
|
|
101
|
+
* The newest failed gate before a park row — the gate a waive of that park would satisfy.
|
|
102
|
+
* Queue row 225 (D-2111): a gate-fail park with no task-dispatch, gate-result (passing or failing) or
|
|
103
|
+
* unvoided task-approved row of its task since its previous task-human — a resume's re-park at the same
|
|
104
|
+
* review round cap — inherits that park's failed gate, the walk continuing past it. A task-approved row
|
|
105
|
+
* an approval-refused row of the task voids before the newer park (its lines name it, or it names none)
|
|
106
|
+
* does not end the walk; refusals are read here directly, never through foldDecisions (which calls this).
|
|
107
|
+
*/
|
|
101
108
|
export function failedGateBeforePark(events, taskId, parkIndex) {
|
|
109
|
+
let inherits = events[parkIndex]?.data.kind === "gate-fail";
|
|
110
|
+
let refused = new Set();
|
|
102
111
|
for (let i = parkIndex - 1; i >= 0; i -= 1) {
|
|
103
112
|
const event = events[i];
|
|
104
113
|
if (event.taskId !== taskId)
|
|
105
114
|
continue;
|
|
106
|
-
if (event.event === "task-
|
|
115
|
+
if (event.event === "task-human") {
|
|
116
|
+
// Only a gate-fail predecessor has a failed gate to hand down; any other kind ends the walk as today.
|
|
117
|
+
if (!inherits || event.data.kind !== "gate-fail")
|
|
118
|
+
return undefined;
|
|
119
|
+
// The walk now reads the previous park's own span, under that park's own refusals.
|
|
120
|
+
refused = new Set();
|
|
121
|
+
}
|
|
122
|
+
else if (event.event === APPROVAL_REFUSED) {
|
|
123
|
+
if (refused !== "all")
|
|
124
|
+
refused = Array.isArray(event.data.lines) ? new Set([...refused, ...event.data.lines]) : "all";
|
|
125
|
+
}
|
|
126
|
+
else if (event.event === "task-approved") {
|
|
127
|
+
if (!inherits || (refused !== "all" && !refused.has(physicalLine(events, i))))
|
|
128
|
+
return undefined;
|
|
129
|
+
}
|
|
130
|
+
else if (event.event === "task-dispatch") {
|
|
107
131
|
return undefined;
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
132
|
+
}
|
|
133
|
+
else if (event.event === "gate-result") {
|
|
134
|
+
if (event.data.pass === false && typeof event.data.gate === "string" && GATE_NAMES.includes(event.data.gate)) {
|
|
135
|
+
return event.data.gate;
|
|
136
|
+
}
|
|
137
|
+
inherits = false; // a passing gate-result keeps the park's own span
|
|
111
138
|
}
|
|
112
139
|
}
|
|
113
140
|
return undefined;
|
|
@@ -321,6 +348,16 @@ export function upheldFeedbackByTask(events) {
|
|
|
321
348
|
}
|
|
322
349
|
return upheld;
|
|
323
350
|
}
|
|
351
|
+
/**
|
|
352
|
+
* Queue row 225 (D-2111): a restore names upheld feedback only when it is FUNDED — the task carries an
|
|
353
|
+
* active uphold brief AND an effective task-approved row newer than its newest task-human. A restore
|
|
354
|
+
* that only re-parks at the same cap announces nothing, so a spent uphold is never re-announced.
|
|
355
|
+
* `decided` is already read through the decision fold (effectiveEvents).
|
|
356
|
+
*/
|
|
357
|
+
export function restoresUpheldFeedback(decided, taskId) {
|
|
358
|
+
const newest = (event) => decided.reduce((at, e, i) => e.taskId === taskId && e.event === event ? i : at, -1);
|
|
359
|
+
return newest("task-approved") > newest("task-human") && upheldFeedbackByTask(decided).has(taskId);
|
|
360
|
+
}
|
|
324
361
|
const CONSULT_ACTIONS = ["retry", "reroute", "decompose", "human"];
|
|
325
362
|
const consultAction = (action) => typeof action === "string" && CONSULT_ACTIONS.includes(action)
|
|
326
363
|
? action
|
|
@@ -2008,7 +2045,7 @@ export class Journal {
|
|
|
2008
2045
|
const folded = !decisionRow && data.fatal === true ? [...priorEvents, { ts: new Date().toISOString(), event, data }] : priorEvents;
|
|
2009
2046
|
return decisionRow ? reduced : { ...reduced, owedChecks: foldOwedChecks(folded, join(this.dir, "..", "..", "..")) };
|
|
2010
2047
|
})()
|
|
2011
|
-
: event === "resume-restore" && rowTaskId &&
|
|
2048
|
+
: event === "resume-restore" && rowTaskId && restoresUpheldFeedback(effectiveEvents(priorEvents), rowTaskId)
|
|
2012
2049
|
? {
|
|
2013
2050
|
...inputData,
|
|
2014
2051
|
upheldFeedbackRestoredFor: rowTaskId,
|
|
@@ -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
|
-
/**
|
|
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
|
-
/**
|
|
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
|
package/dist/run/stall.d.ts
CHANGED
|
@@ -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 (
|
|
678
|
-
//
|
|
679
|
-
//
|
|
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
|
@@ -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 · <
|
|
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 · <
|
|
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 · <
|
|
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 · <
|
|
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
|
|
88
|
+
Before `tickmarkr compile` or `tickmarkr run`:
|
|
87
89
|
|
|
88
|
-
1. Run `tickmarkr version`
|
|
89
|
-
2.
|
|
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 · <
|
|
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 · <
|
|
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 · <
|
|
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 · <
|
|
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
|
|
74
|
+
Before `tickmarkr compile` or `tickmarkr run`:
|
|
73
75
|
|
|
74
|
-
1. Run `tickmarkr version`
|
|
75
|
-
2.
|
|
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 ·
|
|
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 · <
|
|
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 · <
|
|
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 · <
|
|
214
|
-
For Claude Code: `orca terminal create --worktree path:<repo> --title "ORCH · <
|
|
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 · <
|
|
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 · <
|
|
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
|