tickmarkr 1.40.0 → 1.42.0
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/README.md +6 -1
- package/dist/brand.d.ts +2 -0
- package/dist/brand.js +2 -0
- package/dist/cli/commands/doctor.d.ts +4 -1
- package/dist/cli/commands/doctor.js +4 -2
- package/dist/cli/commands/init.js +15 -2
- package/dist/drivers/herdr.js +6 -2
- package/dist/gates/baseline.js +8 -6
- package/dist/run/daemon.js +2 -1
- package/dist/run/git.d.ts +1 -0
- package/dist/run/git.js +1 -1
- package/dist/run/merge.d.ts +2 -1
- package/dist/run/merge.js +18 -13
- package/package.json +13 -1
- package/skills/tickmarkr-auto/SKILL.md +6 -2
- package/skills/tickmarkr-loop/SKILL.md +8 -3
package/README.md
CHANGED
package/dist/brand.d.ts
CHANGED
package/dist/brand.js
CHANGED
|
@@ -9,6 +9,8 @@ export const BANNER = [
|
|
|
9
9
|
` ${g(35, "▀▀████▀▀")} spec in, verified work out.`,
|
|
10
10
|
"",
|
|
11
11
|
].join("\n");
|
|
12
|
+
/** ANSI-stripped, trailing-space-trimmed twin of BANNER — README hero and other plain surfaces. */
|
|
13
|
+
export const PLAIN_BANNER = BANNER.replace(/\x1b\[[0-9;]*m/g, "").replace(/[ \t]+$/gm, "");
|
|
12
14
|
// Shell one-liner that prints the banner inside a pane before a gate command runs. ESC bytes are
|
|
13
15
|
// carried as printf %b escapes (never raw control bytes in a command string crossing the herdr socket).
|
|
14
16
|
export function bannerShell() {
|
|
@@ -1,2 +1,5 @@
|
|
|
1
1
|
import type { WorkerAdapter } from "../../adapters/types.js";
|
|
2
|
-
export
|
|
2
|
+
export type DoctorOpts = {
|
|
3
|
+
banner?: boolean;
|
|
4
|
+
};
|
|
5
|
+
export declare function doctor(_argv: string[], cwd?: string, adapters?: WorkerAdapter[], opts?: DoctorOpts): Promise<string>;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { writeFileSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { allAdapters, probeAll, probeModels, readAutoPrefer, servableExclusions, servabilityLine, writeDoctor } from "../../adapters/registry.js";
|
|
4
|
+
import { BANNER } from "../../brand.js";
|
|
4
5
|
import { tickmarkrDir, stateDirName } from "../../graph/graph.js";
|
|
5
6
|
import { modelLints, suggestOverlay, ttyVisual } from "../../adapters/model-lints.js";
|
|
6
7
|
import { DEFAULT_CONFIG, loadConfig, overlayPreferShapes } from "../../config/config.js";
|
|
@@ -30,7 +31,7 @@ function stylize(out) {
|
|
|
30
31
|
.replace(/\bunauthed:/, color("unauthed:", 33));
|
|
31
32
|
}).join("\n");
|
|
32
33
|
}
|
|
33
|
-
export async function doctor(_argv, cwd = process.cwd(), adapters = allAdapters()) {
|
|
34
|
+
export async function doctor(_argv, cwd = process.cwd(), adapters = allAdapters(), opts = {}) {
|
|
34
35
|
const cfg = loadConfig(cwd);
|
|
35
36
|
// stderr, live: auth probes are real LLM calls (up to 30s per configured model) and the CLI
|
|
36
37
|
// otherwise prints nothing until the end — silence here reads as a hang (v1.33.1)
|
|
@@ -158,5 +159,6 @@ export async function doctor(_argv, cwd = process.cwd(), adapters = allAdapters(
|
|
|
158
159
|
const modelSummary = modelStatus.length || preferStatus.length
|
|
159
160
|
? `\nmodel status:\n${[...modelStatus, ...preferStatus].join("\n")}`
|
|
160
161
|
: "";
|
|
161
|
-
|
|
162
|
+
const body = stylize(`tickmarkr doctor — capability matrix:\n${rows.join("\n")}${modelSummary}${drift}\nwrote ${stateDirName(cwd)}/doctor.json`);
|
|
163
|
+
return opts.banner !== false && visual() ? BANNER + body : body;
|
|
162
164
|
}
|
|
@@ -5,8 +5,10 @@ import { parseArgs } from "node:util";
|
|
|
5
5
|
import { allAdapters, formatDoctorAgeForInit, formatDoctorReport, initDoctorReuse } from "../../adapters/registry.js";
|
|
6
6
|
import { configTemplate, DEFAULT_CONFIG, globalConfigDir, loadConfig } from "../../config/config.js";
|
|
7
7
|
import { LEGACY_PREFIX, specTemplate } from "../../compile/native.js";
|
|
8
|
+
import { BANNER } from "../../brand.js";
|
|
8
9
|
import { tickmarkrDir } from "../../graph/graph.js";
|
|
9
10
|
import { doctor } from "./doctor.js";
|
|
11
|
+
const visual = () => process.stdout.isTTY === true && process.env.NO_COLOR === undefined;
|
|
10
12
|
const AGENT_SKILLS = ["tickmarkr-loop", "tickmarkr-auto"];
|
|
11
13
|
const DOCS_BEGIN = "<!-- tickmarkr:agent-docs begin -->";
|
|
12
14
|
const DOCS_END = "<!-- tickmarkr:agent-docs end -->";
|
|
@@ -138,6 +140,13 @@ async function runInitWizard(cwd) {
|
|
|
138
140
|
}
|
|
139
141
|
}
|
|
140
142
|
export async function init(argv, cwd = process.cwd()) {
|
|
143
|
+
let bannerEmitted = false;
|
|
144
|
+
const emitBanner = () => {
|
|
145
|
+
if (visual() && !bannerEmitted) {
|
|
146
|
+
bannerEmitted = true;
|
|
147
|
+
process.stdout.write(BANNER);
|
|
148
|
+
}
|
|
149
|
+
};
|
|
141
150
|
const { values } = parseArgs({
|
|
142
151
|
args: argv,
|
|
143
152
|
options: {
|
|
@@ -183,10 +192,11 @@ export async function init(argv, cwd = process.cwd()) {
|
|
|
183
192
|
const { reuse, ageMs, health } = initDoctorReuse(cwd, fresh);
|
|
184
193
|
const doc = reuse && health && ageMs !== null
|
|
185
194
|
? `using probe results from ${formatDoctorAgeForInit(ageMs)} ago — run tickmarkr doctor to refresh (or init --fresh)\n${formatDoctorReport(cwd, loadConfig(cwd), health, allAdapters(), { wrote: false })}`
|
|
186
|
-
: await doctor([], cwd);
|
|
195
|
+
: await doctor([], cwd, undefined, { banner: false });
|
|
187
196
|
const interactive = process.stdin.isTTY === true && process.stdout.isTTY === true && !(values.yes ?? false);
|
|
188
197
|
if (!repoConfigExists) {
|
|
189
198
|
if (interactive) {
|
|
199
|
+
emitBanner();
|
|
190
200
|
const wizard = await runInitWizard(cwd);
|
|
191
201
|
writeFileSync(repoConfigPath, configTemplate(wizard.overlay));
|
|
192
202
|
notes.push(`wrote ${repoConfigPath}`);
|
|
@@ -200,5 +210,8 @@ export async function init(argv, cwd = process.cwd()) {
|
|
|
200
210
|
}
|
|
201
211
|
if (values.agent)
|
|
202
212
|
await installAgentFiles(cwd, values.force ?? false, values.docs ?? false, notes);
|
|
203
|
-
|
|
213
|
+
const body = `${notes.join("\n")}\n${doc}\nnext: edit tickmarkr.spec.md, then tickmarkr compile tickmarkr.spec.md && tickmarkr plan && tickmarkr run`;
|
|
214
|
+
if (visual() && !bannerEmitted)
|
|
215
|
+
return BANNER + body;
|
|
216
|
+
return body;
|
|
204
217
|
}
|
package/dist/drivers/herdr.js
CHANGED
|
@@ -202,11 +202,15 @@ export class HerdrDriver {
|
|
|
202
202
|
const token = newest ? canonicalizeLegacyName(newest.name, "").taskId : undefined;
|
|
203
203
|
const glyph = newest ? await this.glyphFor(newest) : "";
|
|
204
204
|
const label = token ? `${entry.label} · ${token}${glyph}` : entry.label;
|
|
205
|
+
const cmd = `tab rename ${shq(entry.tabId)} ${shq(label)}`;
|
|
206
|
+
const ok = async () => (await this.herdr(cmd)).code === 0;
|
|
207
|
+
if (await ok() || await ok())
|
|
208
|
+
return;
|
|
205
209
|
try {
|
|
206
|
-
await this.
|
|
210
|
+
await this.notify(`tickmarkr tab relabel failed: ${entry.tabId} → ${label}`);
|
|
207
211
|
}
|
|
208
212
|
catch {
|
|
209
|
-
/* cosmetic only — a relabel failure never blocks membership or teardown (v1.18 invariant) */
|
|
213
|
+
/* OBS-45: cosmetic only — a relabel failure never blocks membership or teardown (v1.18 invariant) */
|
|
210
214
|
}
|
|
211
215
|
}
|
|
212
216
|
// VIS-13: at most one glyph on the hot token. ✋ wins — the driver observes the member blocked live
|
package/dist/gates/baseline.js
CHANGED
|
@@ -6,20 +6,21 @@ import { sh } from "../run/git.js";
|
|
|
6
6
|
// a pass-marker line is never a failure. [\d;#] covers raw ANSI and digit-normalized ANSI ("\x1b[#m") from
|
|
7
7
|
// baselines stored by pre-hardening code.
|
|
8
8
|
const ANSI_RE = /\x1b\[[\d;#]*[A-Za-z]/g;
|
|
9
|
-
// ponytail: only leading ✓/✔ after optional "label:" prefixes (turbo/vitest)
|
|
10
|
-
// runners' pass markers (PASS, ok) stay fingerprintable
|
|
11
|
-
const PASS_LINE_RE = /^\s*(?:[\w@./-]+:\s*)*[✓✔]/;
|
|
9
|
+
// ponytail: only leading ✓/✔ after optional "label:" prefixes (turbo/vitest), or tickmarkr's own run
|
|
10
|
+
// summary, counts as a pass line — other runners' pass markers (PASS, ok) stay fingerprintable
|
|
11
|
+
const PASS_LINE_RE = /^\s*(?:(?:[\w@./-]+:\s*)*[✓✔]|(?:\[tickmarkr\]\s+)?(?:tickmarkr\s+[\w.-]+:\s+)?(?:\d+|#)\s+done,\s+(?:\d+|#)\s+failed(?:,\s+(?:\d+|#)\s+awaiting human)?\b)/;
|
|
12
12
|
// HYG-08 (D-01, incident run-20260711-154920): a failing test went unnamed for 3 attempts because details
|
|
13
13
|
// headlined benign fingerprint-diff noise. These anchors harvest the runner's OWN failure naming from fresh
|
|
14
14
|
// output to headline it. \s is fine in a TS regex — the BSD [[:space:]] rule binds shell grep only.
|
|
15
|
-
|
|
15
|
+
// OBS-42: vitest's diagnostic failure headings are shared anchors for baseline and tip verification.
|
|
16
|
+
const FAIL_ANCHOR_RE = /^\s*(?:FAIL\s+|[^\w]*(?:Unhandled Errors|Uncaught Exception)\b)/;
|
|
16
17
|
const SUMMARY_FAIL_RE = /^\s*Tests?\s+(?:Files?\s+)?\d+\s+failed/; // " Tests N failed | M passed (T)"
|
|
17
18
|
const normalizeLine = (l) => l.replace(/\d+/g, "#").replace(/\s+/g, " ").trim();
|
|
18
19
|
export function fingerprint(output) {
|
|
19
20
|
const lines = output
|
|
20
21
|
.split("\n")
|
|
21
22
|
.map((l) => l.replace(ANSI_RE, ""))
|
|
22
|
-
.filter((l) => !PASS_LINE_RE.test(l) && /\b(error|fail(ed|ure|ing)?)\b/i.test(l))
|
|
23
|
+
.filter((l) => !PASS_LINE_RE.test(l) && (FAIL_ANCHOR_RE.test(l) || /\b(error|fail(ed|ure|ing)?)\b/i.test(l)))
|
|
23
24
|
.map(normalizeLine);
|
|
24
25
|
return [...new Set(lines)];
|
|
25
26
|
}
|
|
@@ -84,7 +85,8 @@ export async function compareToBaseline(cwd, commands, baseline, enabled) {
|
|
|
84
85
|
}
|
|
85
86
|
const raw = (r.stdout + "\n" + r.stderr).split(cwd).join("");
|
|
86
87
|
const known = new Set((baseline.commands[name]?.fingerprints ?? []).map(renormalize));
|
|
87
|
-
|
|
88
|
+
// OBS-42: diagnostic headings enrich fingerprints but cannot invalidate legacy baselines.
|
|
89
|
+
const fresh = fingerprint(raw).filter((f) => !known.has(f) && (!FAIL_ANCHOR_RE.test(f) || f.startsWith("FAIL ")));
|
|
88
90
|
if (!fresh.length && (baseline.commands[name]?.exitCode ?? 1) === 0) {
|
|
89
91
|
results.push({
|
|
90
92
|
gate: name,
|
package/dist/run/daemon.js
CHANGED
|
@@ -723,7 +723,7 @@ export async function runDaemon(repoRoot, opts = {}) {
|
|
|
723
723
|
// OBS-34: post-merge integration-tip verify — strict exit codes, no baseline forgiveness.
|
|
724
724
|
const lastMergedTask = [...journal.read()].reverse().find((e) => e.event === "merge" && e.taskId)?.taskId;
|
|
725
725
|
if (summary.done.length > 0 && Object.keys(commands).length > 0) {
|
|
726
|
-
const tipResults = await verifyIntegrationTip(intWt, commands);
|
|
726
|
+
const tipResults = await verifyIntegrationTip(intWt, commands, journal.dir);
|
|
727
727
|
let tipFailed = false;
|
|
728
728
|
for (const r of tipResults) {
|
|
729
729
|
if (r.pass) {
|
|
@@ -735,6 +735,7 @@ export async function runDaemon(repoRoot, opts = {}) {
|
|
|
735
735
|
cmd: r.cmd,
|
|
736
736
|
exitCode: r.exitCode,
|
|
737
737
|
fingerprints: r.fingerprints,
|
|
738
|
+
artifact: r.artifact,
|
|
738
739
|
lastMergedTask,
|
|
739
740
|
});
|
|
740
741
|
tipFailed = true;
|
package/dist/run/git.d.ts
CHANGED
|
@@ -16,4 +16,5 @@ export declare function cleanupRunWorktrees(repo: string, branch: string, opts:
|
|
|
16
16
|
}): Promise<void>;
|
|
17
17
|
export declare function resolveIntegrationBranch(_repo: string, branch: string): Promise<string>;
|
|
18
18
|
export declare function createWorktree(repo: string, branch: string, baseRef: string): Promise<string>;
|
|
19
|
+
export declare function linkNodeModules(repo: string, dir: string): void;
|
|
19
20
|
export declare function removeWorktree(repo: string, dir: string): Promise<void>;
|
package/dist/run/git.js
CHANGED
|
@@ -78,7 +78,7 @@ export async function createWorktree(repo, branch, baseRef) {
|
|
|
78
78
|
}
|
|
79
79
|
// best-effort: devDep-based gates (tsx, vitest) shell out root-anchored and ENOENT in a bare
|
|
80
80
|
// fresh worktree (OBS-27); failure here must never fail worktree creation
|
|
81
|
-
function linkNodeModules(repo, dir) {
|
|
81
|
+
export function linkNodeModules(repo, dir) {
|
|
82
82
|
const src = join(repo, "node_modules");
|
|
83
83
|
const dest = join(dir, "node_modules");
|
|
84
84
|
if (!existsSync(src) || existsSync(dest))
|
package/dist/run/merge.d.ts
CHANGED
|
@@ -6,6 +6,7 @@ export interface TipVerifyResult {
|
|
|
6
6
|
exitCode: number;
|
|
7
7
|
fingerprints: string[];
|
|
8
8
|
details: string;
|
|
9
|
+
artifact?: string;
|
|
9
10
|
}
|
|
10
11
|
export declare function integrationBranch(cfg: TickmarkrConfig, runId: string): string;
|
|
11
12
|
export declare function ensureIntegration(repo: string, branch: string, baseRef: string): Promise<string>;
|
|
@@ -18,4 +19,4 @@ export declare function mergeTask(intWt: string, taskBranch: string, message: st
|
|
|
18
19
|
branchTip: string;
|
|
19
20
|
};
|
|
20
21
|
}>;
|
|
21
|
-
export declare function verifyIntegrationTip(intWt: string, commands: Record<string, string
|
|
22
|
+
export declare function verifyIntegrationTip(intWt: string, commands: Record<string, string>, runDir: string): Promise<TipVerifyResult[]>;
|
package/dist/run/merge.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { existsSync } from "node:fs";
|
|
1
|
+
import { existsSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { shq } from "../adapters/types.js";
|
|
4
4
|
import { fingerprint } from "../gates/baseline.js";
|
|
5
5
|
import { tickmarkrDir } from "../graph/graph.js";
|
|
6
|
-
import { gitHead, resolveIntegrationBranch, sh, shOk } from "./git.js";
|
|
6
|
+
import { gitHead, linkNodeModules, resolveIntegrationBranch, sh, shOk } from "./git.js";
|
|
7
7
|
export function integrationBranch(cfg, runId) {
|
|
8
8
|
return `${cfg.integrationBranchPrefix}${runId}`;
|
|
9
9
|
}
|
|
@@ -11,15 +11,16 @@ const sanitize = (branch) => branch.replace(/[^\w.-]+/g, "-");
|
|
|
11
11
|
export async function ensureIntegration(repo, branch, baseRef) {
|
|
12
12
|
branch = await resolveIntegrationBranch(repo, branch);
|
|
13
13
|
const dir = join(tickmarkrDir(repo), "worktrees", sanitize(branch));
|
|
14
|
-
if (existsSync(join(dir, ".git")))
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
14
|
+
if (!existsSync(join(dir, ".git"))) {
|
|
15
|
+
const exists = (await sh(`git rev-parse --verify refs/heads/${shq(branch)}`, repo)).code === 0;
|
|
16
|
+
if (exists) {
|
|
17
|
+
await shOk(`git worktree add ${shq(dir)} ${shq(branch)}`, repo);
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
await shOk(`git worktree add -b ${shq(branch)} ${shq(dir)} ${shq(baseRef)}`, repo);
|
|
21
|
+
}
|
|
22
22
|
}
|
|
23
|
+
linkNodeModules(repo, dir);
|
|
23
24
|
return dir;
|
|
24
25
|
}
|
|
25
26
|
export function integrationHead(intWt) {
|
|
@@ -44,18 +45,22 @@ export async function mergeTask(intWt, taskBranch, message, gatedCommit) {
|
|
|
44
45
|
return { ok: false, conflict };
|
|
45
46
|
}
|
|
46
47
|
// OBS-34: strict exit-code verify on the integration tip — no baseline forgiveness.
|
|
47
|
-
export async function verifyIntegrationTip(intWt, commands) {
|
|
48
|
+
export async function verifyIntegrationTip(intWt, commands, runDir) {
|
|
48
49
|
const results = [];
|
|
49
50
|
for (const [gate, cmd] of Object.entries(commands)) {
|
|
50
51
|
const r = await sh(cmd, intWt);
|
|
51
|
-
const raw =
|
|
52
|
+
const raw = r.stdout + "\n" + r.stderr;
|
|
53
|
+
const artifact = r.code !== 0 ? join(runDir, `tip-verify-${gate}.log`) : undefined;
|
|
54
|
+
if (artifact)
|
|
55
|
+
writeFileSync(artifact, raw);
|
|
52
56
|
results.push({
|
|
53
57
|
gate,
|
|
54
58
|
cmd,
|
|
55
59
|
pass: r.code === 0,
|
|
56
60
|
exitCode: r.code,
|
|
57
|
-
fingerprints: r.code !== 0 ? fingerprint(raw) : [],
|
|
61
|
+
fingerprints: r.code !== 0 ? fingerprint(raw.split(intWt).join("")) : [],
|
|
58
62
|
details: r.code === 0 ? "exit 0" : `exit ${r.code}`,
|
|
63
|
+
...(artifact ? { artifact } : {}),
|
|
59
64
|
});
|
|
60
65
|
}
|
|
61
66
|
return results;
|
package/package.json
CHANGED
|
@@ -1,9 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tickmarkr",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.42.0",
|
|
4
4
|
"description": "Spec in, verified work out.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
|
+
"keywords": [
|
|
8
|
+
"ai",
|
|
9
|
+
"agents",
|
|
10
|
+
"orchestration",
|
|
11
|
+
"automation",
|
|
12
|
+
"cli"
|
|
13
|
+
],
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "https://github.com/alzahrani-khalid/tickmarkr.git"
|
|
17
|
+
},
|
|
18
|
+
"homepage": "https://github.com/alzahrani-khalid/tickmarkr",
|
|
7
19
|
"engines": {
|
|
8
20
|
"node": ">=20"
|
|
9
21
|
},
|
|
@@ -12,12 +12,16 @@ Use this to execute a requested sequence of repository specs. It is SDD-agnostic
|
|
|
12
12
|
|
|
13
13
|
When working in a multi-agent terminal environment, decide your role before starting:
|
|
14
14
|
|
|
15
|
-
- **Orchestrator:** your session was started to execute the mission. Rename your own tab/pane
|
|
15
|
+
- **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
16
|
- **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.
|
|
17
|
-
- **Primary session without an orchestrator:** rename your own tab OVERSEER
|
|
17
|
+
- **Primary session without an orchestrator:** rename your own tab `OVERSEER · <version>` and your agent `overseer`, spawn one child orchestration session with `--permission-mode bypassPermissions`, 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) and close its tab — the journal, records, and ledger hold the story; scrollback is disposable.
|
|
18
18
|
|
|
19
19
|
Outside a multi-agent terminal environment, run the loop directly.
|
|
20
20
|
|
|
21
|
+
## Stand-down (mission end and retirement)
|
|
22
|
+
|
|
23
|
+
On each mission's terminal state, after the record commit and operator notification: the orchestrator stops every monitor and background task it started, prints one final stand-down line, and leaves nothing queued in its input box. A finished session with an armed watcher or pre-filled input is a loaded gun.
|
|
24
|
+
|
|
21
25
|
## Invariants
|
|
22
26
|
|
|
23
27
|
- Never run two tickmarkr runs in the same repository concurrently.
|
|
@@ -11,9 +11,9 @@ Use this for any spec that `tickmarkr compile` accepts. It is SDD-agnostic: use
|
|
|
11
11
|
|
|
12
12
|
When working in a multi-agent terminal environment, decide your role before starting:
|
|
13
13
|
|
|
14
|
-
- **Orchestrator:** your session was started to execute the mission. Rename your own tab/pane
|
|
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.
|
|
15
15
|
- **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
|
|
16
|
+
- **Primary session without an orchestrator:** rename your own tab `OVERSEER · <version>` and your agent `overseer`, spawn one child orchestration session with `--permission-mode bypassPermissions`, 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
17
|
|
|
18
18
|
Outside a multi-agent terminal environment, run the loop directly.
|
|
19
19
|
|
|
@@ -50,6 +50,11 @@ Use one of:
|
|
|
50
50
|
|
|
51
51
|
After sending, **confirm delivery** by reading the target pane and verifying the message landed (input empty, agent status `working`, or notification acknowledged). Never report "briefed" or "relayed" without read-back confirmation.
|
|
52
52
|
|
|
53
|
+
## Stand-down (mission end and retirement)
|
|
54
|
+
|
|
55
|
+
- **Orchestrator, on terminal state** (green, failed, or parked), after the record commit and operator notification: stop every monitor and background task you started, print one final stand-down line, and leave NOTHING queued in your input box. A finished session with an armed watcher or pre-filled input is a loaded gun — a retired v1.40 orchestrator sat idle with "merge … tag, publish" unsent in its input; one stray Enter would have shipped a duplicate release.
|
|
56
|
+
- **Supervisor, when a mission completes** (and always before spawning the next orchestrator): verify the orchestrator stood down, then close its tab. The journal, execution record, OBS ledger, and memory hold the story; pane scrollback is disposable. Never leave a retired agent idle with watchers armed.
|
|
57
|
+
|
|
53
58
|
## The loop
|
|
54
59
|
|
|
55
60
|
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.
|
|
@@ -57,4 +62,4 @@ After sending, **confirm delivery** by reading the target pane and verifying the
|
|
|
57
62
|
3. **Plan** — run `tickmarkr plan`. Review the routing table, capability-floor warnings, and every human gate, including work that each gate blocks.
|
|
58
63
|
4. **Run** — run `tickmarkr run`. Watch the run journal for its terminal event rather than repeatedly polling agents. Resolve blocked interactions in the agent session; do not turn them into proxy questions.
|
|
59
64
|
5. **Verify and consolidate** — accept only a green run. A run is green when the run-end event exists in the journal AND the tip verify is not "failed". 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.
|
|
60
|
-
6. **Record** — write `tickmarkr report <runId> --md` beside the source spec and commit the execution record when the repository tracks those records.
|
|
65
|
+
6. **Record** — write `tickmarkr report <runId> --md` beside the source spec and commit the execution record when the repository tracks those records. Then [stand down](#stand-down-mission-end-and-retirement).
|