tickmarkr 2.3.0 → 2.4.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/dist/adapters/catalog-remote.d.ts +1 -4
- package/dist/adapters/catalog-remote.js +52 -42
- package/dist/adapters/catalog.js +5 -3
- package/dist/adapters/claude-code.d.ts +1 -1
- package/dist/adapters/claude-code.js +8 -5
- package/dist/adapters/model-lints.d.ts +9 -5
- package/dist/adapters/model-lints.js +56 -15
- package/dist/adapters/model-windows.js +11 -0
- package/dist/adapters/prompt.js +1 -0
- package/dist/adapters/qwen.d.ts +5 -0
- package/dist/adapters/qwen.js +153 -0
- package/dist/adapters/types.d.ts +1 -0
- package/dist/adapters/types.js +1 -0
- package/dist/cli/commands/compile.js +32 -6
- package/dist/cli/commands/doctor.d.ts +4 -3
- package/dist/cli/commands/doctor.js +20 -6
- package/dist/cli/commands/fleet.d.ts +4 -0
- package/dist/cli/commands/fleet.js +53 -14
- package/dist/cli/commands/plan.js +29 -5
- package/dist/cli/commands/status.d.ts +1 -0
- package/dist/cli/commands/status.js +45 -1
- package/dist/cli/commands/verify.d.ts +1 -0
- package/dist/cli/commands/verify.js +5 -0
- package/dist/cli/index.d.ts +1 -1
- package/dist/cli/index.js +2 -2
- package/dist/compile/collateral.d.ts +2 -9
- package/dist/compile/collateral.js +2 -9
- package/dist/compile/index.d.ts +4 -1
- package/dist/compile/index.js +41 -7
- package/dist/compile/native.d.ts +4 -2
- package/dist/compile/native.js +55 -6
- package/dist/compile/ownership.js +34 -9
- package/dist/config/config.d.ts +1 -0
- package/dist/config/config.js +51 -5
- package/dist/drivers/herdr.d.ts +1 -0
- package/dist/drivers/herdr.js +11 -1
- package/dist/drivers/orca.d.ts +14 -1
- package/dist/drivers/orca.js +114 -15
- package/dist/drivers/types.d.ts +10 -0
- package/dist/gates/baseline.js +26 -7
- package/dist/gates/review.d.ts +4 -2
- package/dist/gates/review.js +24 -13
- package/dist/gates/run-gates.d.ts +5 -2
- package/dist/gates/run-gates.js +34 -17
- package/dist/route/preference.d.ts +4 -0
- package/dist/route/preference.js +40 -0
- package/dist/route/router.js +15 -2
- package/dist/run/consult.d.ts +1 -0
- package/dist/run/consult.js +34 -7
- package/dist/run/daemon.d.ts +4 -0
- package/dist/run/daemon.js +128 -50
- package/dist/run/git.d.ts +2 -0
- package/dist/run/git.js +36 -5
- package/dist/run/journal.js +4 -1
- package/dist/tui/ink/fleet-app.d.ts +4 -0
- package/dist/tui/ink/fleet-app.js +45 -16
- package/package.json +59 -1
|
@@ -1,12 +1,32 @@
|
|
|
1
1
|
import { isAbsolute, join } from "node:path";
|
|
2
2
|
import { parseArgs } from "node:util";
|
|
3
3
|
import { collateralLints, sourceScopeLints } from "../../compile/collateral.js";
|
|
4
|
+
import { CompileError } from "../../compile/common.js";
|
|
4
5
|
import { compileSource } from "../../compile/index.js";
|
|
5
6
|
import { saveGraph, stateDirName } from "../../graph/graph.js";
|
|
6
7
|
import { formatPriorFindingEvidence, readPriorRunEvidence } from "../../run/journal.js";
|
|
7
8
|
import { shGit } from "../../run/git.js";
|
|
8
9
|
import { acquireRunLock, releaseRunLock } from "../../run/lock.js";
|
|
9
10
|
import { harnessLine, resolveHarness } from "../harness.js";
|
|
11
|
+
function nativeSourceScopeErrors(graph, lints) {
|
|
12
|
+
const byId = new Map(graph.tasks.map((task) => [task.id, task]));
|
|
13
|
+
const errors = [];
|
|
14
|
+
for (const lint of lints) {
|
|
15
|
+
const match = lint.match(/^(\S+): criteria implicate out-of-scope source not in files\[\]: (.*)$/);
|
|
16
|
+
if (!match)
|
|
17
|
+
continue;
|
|
18
|
+
const task = byId.get(match[1]);
|
|
19
|
+
if (!task)
|
|
20
|
+
continue;
|
|
21
|
+
const paths = match[2].replace(/ \(capped\)$/, "").split(", ").filter(Boolean);
|
|
22
|
+
for (const path of paths) {
|
|
23
|
+
if (task.goal.includes(`scope-waiver: ${path}`))
|
|
24
|
+
continue;
|
|
25
|
+
errors.push(`${task.id}: ${path} requires scope-waiver: ${path} in the task goal`);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return errors;
|
|
29
|
+
}
|
|
10
30
|
async function mergedPendingDiagnostics(cwd, pending, merges) {
|
|
11
31
|
const head = await shGit("git rev-parse HEAD", cwd);
|
|
12
32
|
const base = head.code === 0 ? head.stdout.trim() : "";
|
|
@@ -33,15 +53,25 @@ export async function compile(argv, cwd = process.cwd(), harnessFrom = process.a
|
|
|
33
53
|
options: {
|
|
34
54
|
type: { type: "string" },
|
|
35
55
|
"dry-run": { type: "boolean" },
|
|
56
|
+
strict: { type: "boolean" },
|
|
36
57
|
},
|
|
37
58
|
allowPositionals: true,
|
|
38
59
|
});
|
|
39
60
|
const src = positionals[0];
|
|
40
61
|
if (!src)
|
|
41
|
-
throw new Error("usage: tickmarkr compile <spec-dir-or-md> [--type speckit|prd|gsd|native] [--dry-run]");
|
|
62
|
+
throw new Error("usage: tickmarkr compile <spec-dir-or-md> [--type speckit|prd|gsd|native] [--dry-run] [--strict]");
|
|
42
63
|
// resolve against the target repo, not the process cwd (the CLI test passes a tmp repo)
|
|
43
64
|
// Both modes reach the same pure compiler; --dry-run only removes the lock/write side effect below.
|
|
44
|
-
const g = compileSource(isAbsolute(src) ? src : join(cwd, src), values.type, cwd
|
|
65
|
+
const g = compileSource(isAbsolute(src) ? src : join(cwd, src), values.type, cwd, // repo root: gsd stores context[0] repo-relative so workers resolve it inside their worktree
|
|
66
|
+
(plan) => plan, { strict: values.strict });
|
|
67
|
+
const scopeLints = [...collateralLints(g.tasks, cwd), ...sourceScopeLints(g.tasks, cwd)];
|
|
68
|
+
const diagnostics = scopeLints.length
|
|
69
|
+
? `\nscope lints:\n${scopeLints.map((lint) => ` ! ${lint}`).join("\n")}`
|
|
70
|
+
: "";
|
|
71
|
+
const unwaived = g.spec.source === "native" ? nativeSourceScopeErrors(g, scopeLints) : [];
|
|
72
|
+
if (unwaived.length > 0) {
|
|
73
|
+
throw new CompileError(`${src} has unwaived native source-scope authoring errors:\n${unwaived.map((line) => ` - ${line}`).join("\n")}${diagnostics}`);
|
|
74
|
+
}
|
|
45
75
|
// One bounded read supplies both cross-run surfaces: unresolved findings below and merge facts for
|
|
46
76
|
// the ancestry check. Neither fact mutates the compiled graph; status and every readiness predicate
|
|
47
77
|
// remain the source compiler's answer.
|
|
@@ -62,10 +92,6 @@ export async function compile(argv, cwd = process.cwd(), harnessFrom = process.a
|
|
|
62
92
|
const summary = values["dry-run"]
|
|
63
93
|
? `validated ${src} (${g.tasks.length} tasks, source ${g.spec.source}, hash ${g.spec.hash.slice(0, 12)}) — dry run; no graph written`
|
|
64
94
|
: `compiled ${src} → ${stateDir}/graph.json (${g.tasks.length} tasks, source ${g.spec.source}, hash ${g.spec.hash.slice(0, 12)})`;
|
|
65
|
-
const scopeLints = [...collateralLints(g.tasks, cwd), ...sourceScopeLints(g.tasks, cwd)];
|
|
66
|
-
const diagnostics = scopeLints.length
|
|
67
|
-
? `\nscope lints:\n${scopeLints.map((lint) => ` ! ${lint}`).join("\n")}`
|
|
68
|
-
: "";
|
|
69
95
|
const priorFindings = prior.findings.length
|
|
70
96
|
? `\nprior-run evidence:\n${prior.findings.map((finding) => ` ${formatPriorFindingEvidence(finding)}`).join("\n")}`
|
|
71
97
|
: "";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type ClaudeAlias } from "../../adapters/claude-code.js";
|
|
2
2
|
import type { WorkerAdapter } from "../../adapters/types.js";
|
|
3
3
|
import { type KimiDoctorTurnResult } from "../../adapters/kimi.js";
|
|
4
|
-
import { type CatalogReadResult } from "../../adapters/catalog-remote.js";
|
|
4
|
+
import { type CatalogFetcher, type CatalogReadResult } from "../../adapters/catalog-remote.js";
|
|
5
5
|
import { type ShResult } from "../../run/git.js";
|
|
6
6
|
import { type VitestListResult } from "../../gates/acceptance.js";
|
|
7
7
|
/** Where a newer `table_<date>.csv` is discovered — the deployed site builds filenames by
|
|
@@ -14,6 +14,7 @@ export type DoctorOpts = {
|
|
|
14
14
|
resolveClaudeAliasIdentity?: (cwd: string, alias: ClaudeAlias) => string | undefined;
|
|
15
15
|
catalog?: CatalogReadResult;
|
|
16
16
|
catalogNow?: () => Date;
|
|
17
|
+
catalogFetcher?: CatalogFetcher;
|
|
17
18
|
/** init's between-acts surface: status rows only — the model matrix and inline drift stay
|
|
18
19
|
* behind `tickmarkr doctor` (files are still written; only the RETURNED string shrinks). */
|
|
19
20
|
compact?: boolean;
|
|
@@ -91,8 +92,8 @@ export declare function selfShadowFinding(ownVersion: string, cwd?: string, reso
|
|
|
91
92
|
* §4.4: `LIVEBENCH_TABLE_DATE` is a hand-bumped constant, and a constant nobody lints is a stale
|
|
92
93
|
* constant — the pinned table ages silently while every tier suggestion keeps citing it. Warns past
|
|
93
94
|
* 90 days, naming the pinned date and the listing where a newer table is discovered.
|
|
94
|
-
* ponytail:
|
|
95
|
-
*
|
|
95
|
+
* ponytail: the listing itself is never fetched; the operator (or RELEASING.md's release-time
|
|
96
|
+
* check) reads it and bumps the constant.
|
|
96
97
|
*/
|
|
97
98
|
export declare function liveBenchStalenessFinding(now: Date): string | undefined;
|
|
98
99
|
export declare function doctor(_argv: string[], cwd?: string, adapters?: WorkerAdapter[], opts?: DoctorOpts): Promise<string>;
|
|
@@ -15,7 +15,7 @@ import { HerdrDriver } from "../../drivers/herdr.js";
|
|
|
15
15
|
import { ORCA_FIXTURE_VERSION, parseEnvelope, resolveOrcaCliBinary } from "../../drivers/orca.js";
|
|
16
16
|
import { kimi, probeKimiDoctorTurn } from "../../adapters/kimi.js";
|
|
17
17
|
import { denyPreferCollisionLine, denyPreferCollisions, disallowedBy, excludedChannels, exclusionLine, preferRanks } from "../../route/preference.js";
|
|
18
|
-
import { LIVEBENCH_TABLE_DATE, readCachedCatalog, refreshCatalogCommand } from "../../adapters/catalog-remote.js";
|
|
18
|
+
import { CATALOG_REFRESH_TIMEOUT_MS, LIVEBENCH_TABLE_DATE, readCachedCatalog, refreshCatalogCommand } from "../../adapters/catalog-remote.js";
|
|
19
19
|
import { sh } from "../../run/git.js";
|
|
20
20
|
import { auditNamedTestOracles, listVitestTests } from "../../gates/acceptance.js";
|
|
21
21
|
/** Where a newer `table_<date>.csv` is discovered — the deployed site builds filenames by
|
|
@@ -336,8 +336,8 @@ export function selfShadowFinding(ownVersion, cwd = process.cwd(), resolve = res
|
|
|
336
336
|
* §4.4: `LIVEBENCH_TABLE_DATE` is a hand-bumped constant, and a constant nobody lints is a stale
|
|
337
337
|
* constant — the pinned table ages silently while every tier suggestion keeps citing it. Warns past
|
|
338
338
|
* 90 days, naming the pinned date and the listing where a newer table is discovered.
|
|
339
|
-
* ponytail:
|
|
340
|
-
*
|
|
339
|
+
* ponytail: the listing itself is never fetched; the operator (or RELEASING.md's release-time
|
|
340
|
+
* check) reads it and bumps the constant.
|
|
341
341
|
*/
|
|
342
342
|
export function liveBenchStalenessFinding(now) {
|
|
343
343
|
const pinnedMs = Date.parse(`${LIVEBENCH_TABLE_DATE.replace(/_/g, "-")}T00:00:00Z`);
|
|
@@ -356,9 +356,20 @@ export async function doctor(_argv, cwd = process.cwd(), adapters = allAdapters(
|
|
|
356
356
|
// Operator directive 2026-08-12 (declutter): long per-model lists render only when asked for.
|
|
357
357
|
const listAllModels = _argv.includes("--models");
|
|
358
358
|
const cfg = loadConfig(cwd);
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
359
|
+
let catalog = opts.catalog ?? readCachedCatalog(cwd, { now: opts.catalogNow });
|
|
360
|
+
let catalogRefreshWarning;
|
|
361
|
+
// RULING-222-17 reverses cache-only for these two operator-facing commands only. The refresh
|
|
362
|
+
// remains age-guarded; compile, plan, and run never import this path.
|
|
363
|
+
if (!opts.catalog && catalog.source === "cache" && catalog.stale) {
|
|
364
|
+
const refreshed = await refreshCatalogCommand({
|
|
365
|
+
repoRoot: cwd,
|
|
366
|
+
fetcher: opts.catalogFetcher,
|
|
367
|
+
timeoutMs: CATALOG_REFRESH_TIMEOUT_MS,
|
|
368
|
+
now: opts.catalogNow,
|
|
369
|
+
});
|
|
370
|
+
catalog = refreshed.catalog;
|
|
371
|
+
catalogRefreshWarning = refreshed.warning;
|
|
372
|
+
}
|
|
362
373
|
// banner at START — the logo greets the operator before the ~60s probe wait, never trailing it (operator report 2026-07-17)
|
|
363
374
|
if (opts.banner !== false && visual())
|
|
364
375
|
process.stdout.write(BANNER);
|
|
@@ -455,6 +466,9 @@ export async function doctor(_argv, cwd = process.cwd(), adapters = allAdapters(
|
|
|
455
466
|
if (catalog.warning) {
|
|
456
467
|
rows.push(attentionRow(`model catalog cache unreadable — ${catalog.warning}; using vendored fallback (advisory — routing unchanged)`));
|
|
457
468
|
}
|
|
469
|
+
if (catalogRefreshWarning) {
|
|
470
|
+
rows.push(attentionRow(`model catalog auto-refresh failed open — ${catalogRefreshWarning}; retained ${catalog.source} catalog`));
|
|
471
|
+
}
|
|
458
472
|
// v1.48 T1 / v1.86 T12: advisory sweep for known agent CLIs with no drive contract. Presence is
|
|
459
473
|
// resolved through the worker's login shell; advisory targets are never executed or written to health.
|
|
460
474
|
rows.push(...detectCandidateClis({ cwd }).map(({ binary, path }) => alignedStatusRow("warn", binary, `detected at ${path} (no drive contract — not routable)`)));
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type CatalogFetcher, type CatalogReadResult } from "../../adapters/catalog-remote.js";
|
|
1
2
|
import type { WorkerAdapter } from "../../adapters/types.js";
|
|
2
3
|
import type { FleetEditorResult } from "../../tui/ink/fleet-app.js";
|
|
3
4
|
type FleetEditorProps = Parameters<typeof import("../../tui/ink/fleet-app.js").runFleetInkEditor>[0];
|
|
@@ -16,6 +17,8 @@ export type FleetIO = {
|
|
|
16
17
|
output?: FleetOutput;
|
|
17
18
|
debug?: boolean;
|
|
18
19
|
reloadGuard?: (bytes: string) => string | null;
|
|
20
|
+
catalogFetcher?: CatalogFetcher;
|
|
21
|
+
catalogNow?: () => Date;
|
|
19
22
|
};
|
|
20
23
|
export type FleetWriteHooks = {
|
|
21
24
|
readPrior?: (path: string) => string;
|
|
@@ -36,6 +39,7 @@ export declare function fleet(argv: string[], cwd?: string, adapters?: WorkerAda
|
|
|
36
39
|
export declare function assembleFleetEditor(cwd: string, adapters: WorkerAdapter[], io: FleetIO, opts: {
|
|
37
40
|
globalDir: string;
|
|
38
41
|
entry?: "presets" | "probe";
|
|
42
|
+
catalog?: CatalogReadResult;
|
|
39
43
|
}): Promise<{
|
|
40
44
|
props: FleetEditorProps;
|
|
41
45
|
commit: (result: FleetEditorResult) => string;
|
|
@@ -3,7 +3,7 @@ import { dirname } from "node:path";
|
|
|
3
3
|
import { parseArgs } from "node:util";
|
|
4
4
|
import { allAdapters, discoverChannels, doctorAgeMs, initDoctorReuse, modelAuthExclusions } from "../../adapters/registry.js";
|
|
5
5
|
import { catalogModelAdvisory, catalogTierRanking, declaredModelWindow, fleetUnclassifiedModels } from "../../adapters/model-lints.js";
|
|
6
|
-
import { readCachedCatalog } from "../../adapters/catalog-remote.js";
|
|
6
|
+
import { CATALOG_REFRESH_TIMEOUT_MS, readCachedCatalog, refreshCatalogCommand } from "../../adapters/catalog-remote.js";
|
|
7
7
|
import { CLAUDE_ALIAS_IDENTITY_STAMPS, readClaudeAliasIdentity } from "../../adapters/claude-code.js";
|
|
8
8
|
import { fleetEditableFromConfig, fleetEditableEquals, formatFleetPrint, globalConfigDir, overlayBytesLoadError, renderFleetOverlayWrite, repoOverlayPath, ROUTING_MODES, unifiedYamlDiff, } from "../../config/config.js";
|
|
9
9
|
import { projectFleetWhy, renderFleetWhy } from "../../config/fleet-why.js";
|
|
@@ -96,6 +96,19 @@ export async function fleet(argv, cwd = process.cwd(), adapters = allAdapters(),
|
|
|
96
96
|
const input = io.input ?? process.stdin;
|
|
97
97
|
const output = io.output ?? process.stdout;
|
|
98
98
|
const interactive = input.isTTY === true && output.isTTY === true;
|
|
99
|
+
let catalog = readCachedCatalog(cwd, { now: io.catalogNow });
|
|
100
|
+
let refreshReason = "";
|
|
101
|
+
if (catalog.source === "cache" && catalog.stale) {
|
|
102
|
+
const refreshed = await refreshCatalogCommand({
|
|
103
|
+
repoRoot: cwd,
|
|
104
|
+
fetcher: io.catalogFetcher,
|
|
105
|
+
timeoutMs: CATALOG_REFRESH_TIMEOUT_MS,
|
|
106
|
+
now: io.catalogNow,
|
|
107
|
+
});
|
|
108
|
+
catalog = refreshed.catalog;
|
|
109
|
+
if (refreshed.warning)
|
|
110
|
+
refreshReason = `fleet: catalog auto-refresh failed open — ${refreshed.warning}; retained ${catalog.source} catalog`;
|
|
111
|
+
}
|
|
99
112
|
if (print) {
|
|
100
113
|
// v1.51 T4: the print surface names the mode and its source layer right under the header —
|
|
101
114
|
// comment-prefixed so the YAML body stays machine-parseable and regex-stable.
|
|
@@ -104,10 +117,10 @@ export async function fleet(argv, cwd = process.cwd(), adapters = allAdapters(),
|
|
|
104
117
|
const nl = body.indexOf("\n");
|
|
105
118
|
// Steering comes from the same resolved config snapshot the editor consumes below,
|
|
106
119
|
// not from another parse of either raw overlay.
|
|
107
|
-
return `${body.slice(0, nl)}\n# mode: ${rm.mode.mode} (${rm.source})${body.slice(nl)}${formatFleetSteering(rm.cfg)}`;
|
|
120
|
+
return `${body.slice(0, nl)}\n# mode: ${rm.mode.mode} (${rm.source})${refreshReason ? `\n# ${refreshReason}` : ""}${body.slice(nl)}${formatFleetSteering(rm.cfg)}`;
|
|
108
121
|
}
|
|
109
122
|
if (!why && !interactive)
|
|
110
|
-
return { out: NON_TTY_MSG
|
|
123
|
+
return { out: `${refreshReason ? `${refreshReason}\n` : ""}${NON_TTY_MSG}`, code: 1 };
|
|
111
124
|
// OBS-528: `--fresh` parsed since v1.92 but nothing ever RAN the probe — it forced the reuse
|
|
112
125
|
// gate false and guaranteed the "probe data missing or stale" refusal. The law stands — the
|
|
113
126
|
// EDITOR never probes (previews stay cache-only, `r` still exits to the operator) — but the
|
|
@@ -119,11 +132,11 @@ export async function fleet(argv, cwd = process.cwd(), adapters = allAdapters(),
|
|
|
119
132
|
output.write(`${await doctor([], cwd, adapters, { banner: false, compact: true })}\n`);
|
|
120
133
|
}
|
|
121
134
|
}
|
|
122
|
-
const assembled = await assembleFleetEditor(cwd, adapters, io, { globalDir });
|
|
135
|
+
const assembled = await assembleFleetEditor(cwd, adapters, io, { globalDir, catalog });
|
|
123
136
|
if ("unavailable" in assembled)
|
|
124
|
-
return { out: assembled.unavailable
|
|
137
|
+
return { out: `${refreshReason ? `${refreshReason}\n` : ""}${assembled.unavailable}`, code: 1 };
|
|
125
138
|
if (why)
|
|
126
|
-
return assembled.renderWhy()
|
|
139
|
+
return `${refreshReason ? `${refreshReason}\n` : ""}${assembled.renderWhy()}`;
|
|
127
140
|
// Keep Ink out of print, non-TTY, and missing-probe paths: the component runtime belongs
|
|
128
141
|
// exclusively to the interactive editor. The capture window brackets only the dynamic Ink
|
|
129
142
|
// import — keys typed while the module loads land in props.initialInput, never get dropped.
|
|
@@ -155,7 +168,7 @@ export async function fleet(argv, cwd = process.cwd(), adapters = allAdapters(),
|
|
|
155
168
|
}
|
|
156
169
|
assembled.props.initialInput = initialInput;
|
|
157
170
|
const result = await runFleetInkEditor(assembled.props);
|
|
158
|
-
return assembled.commit(result)
|
|
171
|
+
return `${refreshReason ? `${refreshReason}\n` : ""}${assembled.commit(result)}`;
|
|
159
172
|
}
|
|
160
173
|
/** Everything between the doctor-reuse gate and the Ink render, packaged for reuse: `tickmarkr init`
|
|
161
174
|
* runs the same editor (entry="presets": Esc is HOME to the preset overlay, never a bare quit)
|
|
@@ -192,7 +205,7 @@ export async function assembleFleetEditor(cwd, adapters, io, opts) {
|
|
|
192
205
|
// OBS-508: the same catalog evidence doctor's drift overlay prints now rides each unclassified
|
|
193
206
|
// row — it prefills the classify flow and feeds the bulk `s` stage. Suggestions stay advisory:
|
|
194
207
|
// only the review-diff confirm writes, so "tickmarkr never applies" holds with less typing.
|
|
195
|
-
const catalog = readCachedCatalog(cwd);
|
|
208
|
+
const catalog = opts.catalog ?? readCachedCatalog(cwd);
|
|
196
209
|
// The same alias→identity resolution doctor hands its advisory rows: models.dev has never heard of
|
|
197
210
|
// `opus`, so without it the fleet's own frontier models drop out of the universe they are supposed
|
|
198
211
|
// to anchor and fleet bands a different set than doctor for one fleet. The stored identity wins;
|
|
@@ -203,9 +216,32 @@ export async function assembleFleetEditor(cwd, adapters, io, opts) {
|
|
|
203
216
|
: readClaudeAliasIdentity(cwd, model) ?? CLAUDE_ALIAS_IDENTITY_STAMPS[model];
|
|
204
217
|
// One ranking universe for the whole screen: every unclassified row bands fleet-relatively
|
|
205
218
|
// against the same set, so a suggestion never depends on which adapter group renders first.
|
|
206
|
-
const
|
|
219
|
+
const detectedRows = fleetUnclassifiedModels(cfg, health, adapters)
|
|
207
220
|
.map((row) => ({ ...row, resolvedModel: resolvedCatalogModel(row.adapter, row.model) }));
|
|
208
|
-
const catalogRanking = catalogTierRanking(cfg, catalog,
|
|
221
|
+
const catalogRanking = catalogTierRanking(cfg, catalog, detectedRows, resolvedCatalogModel);
|
|
222
|
+
const foldedRows = [];
|
|
223
|
+
const folds = new Map();
|
|
224
|
+
for (const row of detectedRows) {
|
|
225
|
+
const advisory = catalogModelAdvisory(cfg, catalog, row.adapter, row.model, row.resolvedModel, catalogRanking);
|
|
226
|
+
const evidence = advisory.coverage === "covered" ? advisory.evidence : undefined;
|
|
227
|
+
const score = evidence?.agenticCodingScore ?? evidence?.intelligenceIndex ?? evidence?.codingScore;
|
|
228
|
+
const next = { ...row, advisory, ...(score !== undefined ? { score } : {}) };
|
|
229
|
+
const foldKey = evidence ? `${row.adapter}:${evidence.catalogId}` : undefined;
|
|
230
|
+
const first = foldKey ? folds.get(foldKey) : undefined;
|
|
231
|
+
if (first) {
|
|
232
|
+
first.foldedModels ??= [first.model];
|
|
233
|
+
first.foldedModels.push(row.model);
|
|
234
|
+
}
|
|
235
|
+
else {
|
|
236
|
+
foldedRows.push(next);
|
|
237
|
+
if (foldKey)
|
|
238
|
+
folds.set(foldKey, next);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
const unclassifiedRows = foldedRows.sort((a, b) => Number(!!b.advisory.suggestion) - Number(!!a.advisory.suggestion)
|
|
242
|
+
|| (b.detectedAt ?? "").localeCompare(a.detectedAt ?? "")
|
|
243
|
+
|| (b.score ?? Number.NEGATIVE_INFINITY) - (a.score ?? Number.NEGATIVE_INFINITY)
|
|
244
|
+
|| a.model.localeCompare(b.model));
|
|
209
245
|
// OBS-508 follow-through: the browser renders the metadata the assembler always had — catalog
|
|
210
246
|
// ctx/price evidence and doctor's model-probe wall clock — as columns instead of dropping them.
|
|
211
247
|
const rowEvidence = (adapter, model, evidence) => {
|
|
@@ -239,14 +275,17 @@ export async function assembleFleetEditor(cwd, adapters, io, opts) {
|
|
|
239
275
|
...unclassified
|
|
240
276
|
.filter((row) => !editable.tiers[adapter.id]?.[row.model])
|
|
241
277
|
.map((row) => {
|
|
242
|
-
const
|
|
243
|
-
const evidence = rowEvidence(adapter.id, row.model, advisory.coverage === "covered" ? advisory.evidence : undefined);
|
|
278
|
+
const evidence = rowEvidence(adapter.id, row.classifyModel ?? row.model, row.advisory.coverage === "covered" ? row.advisory.evidence : undefined);
|
|
244
279
|
return {
|
|
245
280
|
model: row.model,
|
|
246
281
|
detectedAt: row.detectedAt,
|
|
282
|
+
...(row.classifyModel ? { classifyModel: row.classifyModel } : {}),
|
|
283
|
+
...(row.variants ? { variants: row.variants } : {}),
|
|
284
|
+
...(row.foldedModels ? { foldedModels: row.foldedModels } : {}),
|
|
285
|
+
...(row.score !== undefined ? { score: row.score } : {}),
|
|
247
286
|
...(evidence ? { evidence } : {}),
|
|
248
|
-
...(advisory.suggestion
|
|
249
|
-
? { suggestion: { tier: advisory.suggestion.tier, note: advisory.suggestion.provenanceNote } }
|
|
287
|
+
...(row.advisory.suggestion
|
|
288
|
+
? { suggestion: { tier: row.advisory.suggestion.tier, note: row.advisory.suggestion.provenanceNote } }
|
|
250
289
|
: {}),
|
|
251
290
|
};
|
|
252
291
|
}),
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { existsSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { parseArgs } from "node:util";
|
|
4
|
-
import { allAdapters,
|
|
4
|
+
import { allAdapters, doctorAgeMs, modelAuthExclusions, probeAll, readDoctor, rolePools, servableExclusions, servabilityLine } from "../../adapters/registry.js";
|
|
5
5
|
import { formatModelAuthLine, contextWindowLints, modelLints, preferEntryLints, ttyVisual } from "../../adapters/model-lints.js";
|
|
6
6
|
import { GLYPHS, dim, rule, title, warn } from "../../brand.js";
|
|
7
7
|
import { collateralLints, sourceScopeLints } from "../../compile/collateral.js";
|
|
@@ -10,11 +10,11 @@ import { DEFAULT_CONFIG, overlayPreferShapes, ROUTING_MODES, TIER_RANK } from ".
|
|
|
10
10
|
import { loadGraph } from "../../graph/graph.js";
|
|
11
11
|
import { renderAcceptanceItem } from "../../graph/schema.js";
|
|
12
12
|
import { resolveRunMode } from "../../run/daemon.js";
|
|
13
|
-
import { excludedChannels, exclusionLine } from "../../route/preference.js";
|
|
13
|
+
import { disallowedBy, excludedChannels, exclusionLine, routingEntrySeatLines } from "../../route/preference.js";
|
|
14
14
|
import { staffLedEvidence } from "../../route/profile.js";
|
|
15
15
|
import { route, RoutingError } from "../../route/router.js";
|
|
16
16
|
import { auditNamedTestOracles, listVitestTests } from "../../gates/acceptance.js";
|
|
17
|
-
import { modelId } from "../../gates/review.js";
|
|
17
|
+
import { modelId, modelProvider } from "../../gates/review.js";
|
|
18
18
|
import { loadRoutingProfile } from "../../run/journal.js";
|
|
19
19
|
import { harnessLine, resolveHarness } from "../harness.js";
|
|
20
20
|
import { shq } from "../../adapters/types.js";
|
|
@@ -153,7 +153,8 @@ export async function plan(argv, cwd = process.cwd(), adapters = allAdapters(),
|
|
|
153
153
|
// readDoctor cache path: staleness line only fires here (probeAll fallback is fresh by construction).
|
|
154
154
|
const cached = readDoctor(cwd);
|
|
155
155
|
const health = cached ?? (await probeAll(adapters));
|
|
156
|
-
const
|
|
156
|
+
const pools = rolePools(cfg, adapters, health);
|
|
157
|
+
const channels = pools.worker;
|
|
157
158
|
// VIS-04 trust ramp (VALIDATION 13-01-11): preview:true bypasses the routing.learned:off short-circuit so
|
|
158
159
|
// the static-vs-learned comparison renders even when the daemon's learned routing is off. Cold ⇒ undefined ⇒
|
|
159
160
|
// output byte-identical to today.
|
|
@@ -175,6 +176,13 @@ export async function plan(argv, cwd = process.cwd(), adapters = allAdapters(),
|
|
|
175
176
|
harnessLine(resolveHarness(harnessFrom)),
|
|
176
177
|
"",
|
|
177
178
|
];
|
|
179
|
+
const judgeDeny = disallowedBy(cfg.judge, cfg.routing, "judge");
|
|
180
|
+
if (judgeDeny?.by === "deny") {
|
|
181
|
+
lines.push(`REFUSAL OBS-576: judge seat ${cfg.judge.adapter}:${cfg.judge.model} is removed by flat routing.deny (${judgeDeny.entry}) — move worker-only policy to routing.deny.workers`, "");
|
|
182
|
+
}
|
|
183
|
+
const entrySeats = routingEntrySeatLines(cfg);
|
|
184
|
+
if (entrySeats.length)
|
|
185
|
+
lines.push(...entrySeats, "");
|
|
178
186
|
const derivation = (shape) => {
|
|
179
187
|
const floor = cfg.routing.floors[shape];
|
|
180
188
|
return floor ? ` floor ${floor} ← ${mode.provenance[shape] ?? "config floors"}` : null;
|
|
@@ -235,6 +243,22 @@ export async function plan(argv, cwd = process.cwd(), adapters = allAdapters(),
|
|
|
235
243
|
lints.push(...modelLints(cfg, health, adapters, { tty: ttyVisual() })); // health may be pre-v1.5/probeAll-fallback — no-detection branch covers both
|
|
236
244
|
// v1.54 T3: dead-steering sweep — advisory only, renders with the routing lints, never alters routing.
|
|
237
245
|
lints.push(...preferEntryLints(cfg, health, overlayPreferShapes(cwd)));
|
|
246
|
+
const preferMatches = (entry, pool) => pool.some((c) => entry === c.adapter || entry === `${c.adapter}:${c.model}`);
|
|
247
|
+
const lintInertPrefer = (surface, entries, poolName, pool) => {
|
|
248
|
+
for (const entry of entries ?? []) {
|
|
249
|
+
if (!preferMatches(entry, pool))
|
|
250
|
+
lints.push(`${surface} '${entry}' resolves to no live channel of ${poolName} pool`);
|
|
251
|
+
}
|
|
252
|
+
};
|
|
253
|
+
for (const [shape, entry] of Object.entries(cfg.routing.map)) {
|
|
254
|
+
lintInertPrefer(`routing.map.${shape}.prefer`, entry.prefer, "worker", pools.worker);
|
|
255
|
+
}
|
|
256
|
+
lintInertPrefer("review.prefer", cfg.review.prefer, "review", pools.review);
|
|
257
|
+
lintInertPrefer("consult.prefer", cfg.consult.prefer, "consult", pools.consult);
|
|
258
|
+
const reviewProviders = new Set(pools.review.map((c) => modelProvider(c.model, c.vendor)));
|
|
259
|
+
if (pools.review.length > 0 && reviewProviders.size < 2) {
|
|
260
|
+
lints.push(`review pool concentration: ${pools.review.length} channel(s), ${reviewProviders.size} provider(s)`);
|
|
261
|
+
}
|
|
238
262
|
// The guard here was `channels.length && !fleetCanCrossVendorReview(channels)`, so the review lint went
|
|
239
263
|
// SILENT at zero channels — the first-run state, where review.required is set and nothing can route at all.
|
|
240
264
|
// Deleting that clause outright is worse than the silence: it fires "no cross-vendor reviewer pair in fleet"
|
|
@@ -245,7 +269,7 @@ export async function plan(argv, cwd = process.cwd(), adapters = allAdapters(),
|
|
|
245
269
|
if (cfg.review.required && channels.length === 0) {
|
|
246
270
|
lints.push("review: review.required is set but no channel can route — the fleet is empty; install or authenticate an adapter");
|
|
247
271
|
}
|
|
248
|
-
else if (cfg.review.required && !fleetCanCrossVendorReview(
|
|
272
|
+
else if (cfg.review.required && !fleetCanCrossVendorReview(pools.review)) {
|
|
249
273
|
lints.push("review: no cross-vendor reviewer pair in fleet — set review.required: false to waive");
|
|
250
274
|
}
|
|
251
275
|
let cost = 0;
|
|
@@ -21,6 +21,7 @@ export declare const GATE_KEYS: {
|
|
|
21
21
|
readonly acceptance: "A";
|
|
22
22
|
readonly review: "R";
|
|
23
23
|
};
|
|
24
|
+
export declare const STATUS_HELP = "usage: tickmarkr status [<runId>] [--watch [--events]] [--oneline]\nWith --watch --events, stdout is one JSON document per decision event and stderr carries keepalive lines.\nDo not merge stdout and stderr (for example with 2>&1): doing so corrupts the JSON document stream.";
|
|
24
25
|
export declare const decisionEventsFromJournal: (events: JournalEvent[], runId: string, stateDir?: string) => DecisionEvent[];
|
|
25
26
|
export declare const taskBox: (status: TaskStatus) => string;
|
|
26
27
|
export declare const gateBox: (state: "open" | "pass" | "fail" | "skip", unicode: boolean) => string;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import * as os from "node:os";
|
|
2
3
|
import { join } from "node:path";
|
|
3
4
|
import { GLYPHS, LIVE } from "../../brand.js";
|
|
4
5
|
import { DEFAULT_CONFIG, loadConfig } from "../../config/config.js";
|
|
@@ -108,6 +109,11 @@ const SPINNER = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇",
|
|
|
108
109
|
const ASCII_SPINNER = ["|", "/", "-", "\\"];
|
|
109
110
|
const SAVE_TERMINAL_TITLE = "\x1b[22;0t";
|
|
110
111
|
const RESTORE_TERMINAL_TITLE = "\x1b[23;0t";
|
|
112
|
+
const ENTER_ALTERNATE_SCREEN = "\x1b[?1049h";
|
|
113
|
+
const LEAVE_ALTERNATE_SCREEN = "\x1b[?1049l";
|
|
114
|
+
export const STATUS_HELP = `usage: tickmarkr status [<runId>] [--watch [--events]] [--oneline]
|
|
115
|
+
With --watch --events, stdout is one JSON document per decision event and stderr carries keepalive lines.
|
|
116
|
+
Do not merge stdout and stderr (for example with 2>&1): doing so corrupts the JSON document stream.`;
|
|
111
117
|
const decisionEvidence = (stateDir, runId, sequence) => `${stateDir}/runs/${runId}/journal.jsonl#L${sequence}`;
|
|
112
118
|
const DECISION_GATE_VERDICTS = {
|
|
113
119
|
passed: "passed",
|
|
@@ -1109,7 +1115,7 @@ binaryVersion, now = Date.now(), animationFrame = 0, workerLiveness = new Map(),
|
|
|
1109
1115
|
const tally = tipPhase
|
|
1110
1116
|
? `${progressTone(`${progressGlyph} ${done}/${total} tasks done`)}${dot}${progressTone("run not verified")}`
|
|
1111
1117
|
: done === total && total > 0 ? ok(`${done}/${total} done`) : information(`${done}/${total} done`);
|
|
1112
|
-
const live = liveness(events, daemon, now)
|
|
1118
|
+
const live = `${liveness(events, daemon, now)} · load1 ${os.loadavg()[0].toFixed(2)}`
|
|
1113
1119
|
.replace(/\bdead\b/u, fail("dead"))
|
|
1114
1120
|
.replace(/\bfinished\b/u, dim("finished"))
|
|
1115
1121
|
.replace(/\balive\b/u, running("alive"))
|
|
@@ -1324,6 +1330,8 @@ const oneLine = (cwd, namedRunId) => {
|
|
|
1324
1330
|
return sanitizeTaskText([record.runId, ...claims].join(" · "));
|
|
1325
1331
|
};
|
|
1326
1332
|
export async function status(argv, cwd = process.cwd(), opts = {}) {
|
|
1333
|
+
if (argv.some((arg) => arg === "--help" || arg === "-h"))
|
|
1334
|
+
return STATUS_HELP;
|
|
1327
1335
|
const namedRunId = positionalRunId(argv);
|
|
1328
1336
|
if (argv.includes("--oneline"))
|
|
1329
1337
|
return oneLine(cwd, namedRunId);
|
|
@@ -1456,6 +1464,13 @@ export async function status(argv, cwd = process.cwd(), opts = {}) {
|
|
|
1456
1464
|
// a dead board keeps writing and reads ARMED. That is the over-claiming direction, the one an operator
|
|
1457
1465
|
// acts on, and the one this instrument exists to close.
|
|
1458
1466
|
const armed = bounded ? undefined : armWatchSupervision(cwd, opts.supervisionBeatMs);
|
|
1467
|
+
let alternateScreen = false;
|
|
1468
|
+
const leaveAlternateScreen = () => {
|
|
1469
|
+
if (!alternateScreen)
|
|
1470
|
+
return;
|
|
1471
|
+
alternateScreen = false;
|
|
1472
|
+
process.stdout.write(LEAVE_ALTERNATE_SCREEN);
|
|
1473
|
+
};
|
|
1459
1474
|
let titleSaved = false;
|
|
1460
1475
|
const restoreTitle = () => {
|
|
1461
1476
|
if (!titleSaved)
|
|
@@ -1478,6 +1493,28 @@ export async function status(argv, cwd = process.cwd(), opts = {}) {
|
|
|
1478
1493
|
}
|
|
1479
1494
|
process.stdout.write(`\x1b]0;⏳ ${hotPhase.taskId} ${phaseLabel(hotPhase)} ${fmtElapsed(nowMs - hotPhase.startedAt)}\x07`);
|
|
1480
1495
|
};
|
|
1496
|
+
const signalHandlers = [];
|
|
1497
|
+
const exitFromSignal = (code) => () => {
|
|
1498
|
+
if (titleSaved) {
|
|
1499
|
+
process.removeListener("exit", restoreTitle);
|
|
1500
|
+
restoreTitle();
|
|
1501
|
+
}
|
|
1502
|
+
if (alternateScreen) {
|
|
1503
|
+
process.removeListener("exit", leaveAlternateScreen);
|
|
1504
|
+
leaveAlternateScreen();
|
|
1505
|
+
}
|
|
1506
|
+
process.exit(code);
|
|
1507
|
+
};
|
|
1508
|
+
if (!bounded && !eventStream && tty) {
|
|
1509
|
+
process.stdout.write(ENTER_ALTERNATE_SCREEN);
|
|
1510
|
+
alternateScreen = true;
|
|
1511
|
+
process.once("exit", leaveAlternateScreen);
|
|
1512
|
+
for (const [signal, code] of [["SIGINT", 130], ["SIGTERM", 143]]) {
|
|
1513
|
+
const handler = exitFromSignal(code);
|
|
1514
|
+
signalHandlers.push([signal, handler]);
|
|
1515
|
+
process.once(signal, handler);
|
|
1516
|
+
}
|
|
1517
|
+
}
|
|
1481
1518
|
try {
|
|
1482
1519
|
for (let i = 0; i < iterations; i++) {
|
|
1483
1520
|
const nowMs = now();
|
|
@@ -1490,6 +1527,7 @@ export async function status(argv, cwd = process.cwd(), opts = {}) {
|
|
|
1490
1527
|
if (bounded)
|
|
1491
1528
|
eventLines.push(line);
|
|
1492
1529
|
}
|
|
1530
|
+
process.stderr.write("status: keepalive\n");
|
|
1493
1531
|
}
|
|
1494
1532
|
else {
|
|
1495
1533
|
frame = renderFrame(cwd, binaryVersion, nowMs, i, workerLiveness, true, namedRunId);
|
|
@@ -1525,6 +1563,12 @@ export async function status(argv, cwd = process.cwd(), opts = {}) {
|
|
|
1525
1563
|
process.removeListener("exit", restoreTitle);
|
|
1526
1564
|
restoreTitle();
|
|
1527
1565
|
}
|
|
1566
|
+
for (const [signal, handler] of signalHandlers)
|
|
1567
|
+
process.removeListener(signal, handler);
|
|
1568
|
+
if (alternateScreen) {
|
|
1569
|
+
process.removeListener("exit", leaveAlternateScreen);
|
|
1570
|
+
leaveAlternateScreen();
|
|
1571
|
+
}
|
|
1528
1572
|
}
|
|
1529
1573
|
return bounded ? eventStream ? eventLines.join("\n") : frames.join(sep) : "";
|
|
1530
1574
|
}
|
|
@@ -9,6 +9,7 @@ export declare function verifyStateRoot(cwd: string): Promise<string>;
|
|
|
9
9
|
/** Cache identity is repository content, never a checkout path. */
|
|
10
10
|
export declare function baselineCachePath(cwd: string, baseSha: string, commands: Record<string, string>): string;
|
|
11
11
|
export declare function excludeAuthorProvider(channels: BillingChannel[], author: BillingChannel): BillingChannel[];
|
|
12
|
+
export declare const VERIFY_HELP = "usage: tickmarkr verify [--base <ref>] [--criteria <file> | --task <id>] [--json]\nThe final verdict and JSON result are written to stdout; progress and diagnostics are written to stderr.\nDo not merge stdout and stderr (for example with 2>&1): doing so corrupts the verdict stream.";
|
|
12
13
|
export declare function verify(argv: string[], cwd?: string): Promise<{
|
|
13
14
|
out: string;
|
|
14
15
|
code: number;
|
|
@@ -125,7 +125,12 @@ async function warnWideTaskRange(cwd, stateRoot, taskId, mergeBase) {
|
|
|
125
125
|
console.error(`verify: WARNING --task ${taskId} range also carries merge commit(s) for ${others.map((row) => `${row.taskId} ${row.commit.slice(0, 12)}`).join(", ")}; ${taskId}'s own merge ${own.commit} is the intended HEAD`);
|
|
126
126
|
}
|
|
127
127
|
}
|
|
128
|
+
export const VERIFY_HELP = `usage: tickmarkr verify [--base <ref>] [--criteria <file> | --task <id>] [--json]
|
|
129
|
+
The final verdict and JSON result are written to stdout; progress and diagnostics are written to stderr.
|
|
130
|
+
Do not merge stdout and stderr (for example with 2>&1): doing so corrupts the verdict stream.`;
|
|
128
131
|
export async function verify(argv, cwd = process.cwd()) {
|
|
132
|
+
if (argv.some((arg) => arg === "--help" || arg === "-h"))
|
|
133
|
+
return { out: VERIFY_HELP, code: 0 };
|
|
129
134
|
const { values } = parseArgs({
|
|
130
135
|
args: argv,
|
|
131
136
|
options: {
|
package/dist/cli/index.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ export type CommandResult = string | {
|
|
|
5
5
|
};
|
|
6
6
|
export type CommandMap = Record<string, (argv: string[]) => Promise<CommandResult>>;
|
|
7
7
|
export declare const COMMANDS: CommandMap;
|
|
8
|
-
export declare const USAGE = "tickmarkr \u2014 spec-driven orchestration harness for AI coding agents\nusage: tickmarkr <command>\n init guided setup + doctor; init --agent [--force] [--docs] adds agent skills/docs\n doctor re-probe adapters, herdr, auth; print capability matrix (--fix writes the test-runner ignore when a safe edit exists)\n fleet interactive fleet editor (fleet --print for CI drift checks)\n compile <src> spec \u2192 .tickmarkr/graph.json (fails without acceptance criteria)\n scope <intent> draft a compiled native spec beside an answered intent (--force to overwrite)\n plan dry-run routing table + cost estimate + floor lints\n eval run checked-in fixtures against every channel in isolated temp repos\n run execute the graph (--concurrency N --driver auto|herdr|subprocess|orca --route-strict; orca runs only when named)\n status live run state\n stats all-run channel delivery, red, rescue, author and reviewer statistics\n verify run the gate battery standalone against merge-base(--base, HEAD)..HEAD \u2014
|
|
8
|
+
export declare const USAGE = "tickmarkr \u2014 spec-driven orchestration harness for AI coding agents\nusage: tickmarkr <command>\n init guided setup + doctor; init --agent [--force] [--docs] adds agent skills/docs\n doctor re-probe adapters, herdr, auth; print capability matrix (--fix writes the test-runner ignore when a safe edit exists)\n fleet interactive fleet editor (fleet --print for CI drift checks)\n compile <src> spec \u2192 .tickmarkr/graph.json (fails without acceptance criteria)\n scope <intent> draft a compiled native spec beside an answered intent (--force to overwrite)\n plan dry-run routing table + cost estimate + floor lints\n eval run checked-in fixtures against every channel in isolated temp repos\n run execute the graph (--concurrency N --driver auto|herdr|subprocess|orca --route-strict; orca runs only when named)\n status live run state (--watch --events: JSON documents on stdout, keepalives on stderr; 2>&1 corrupts the stream)\n stats all-run channel delivery, red, rescue, author and reviewer statistics\n verify run the gate battery standalone against merge-base(--base, HEAD)..HEAD \u2014 verdict/JSON on stdout, progress on stderr; 2>&1 corrupts the verdict stream (--base main --criteria <file> | --task <id> [--files <glob>] [--author adapter:model] [--no-review] [--json])\n resume <id> continue a run from its journal\n report <id> cost/quality report (--md for committable execution record)\n profile show learned routing profile (profile reset = forget history via cursor, keeps telemetry)\n ui open the Fleet Studio TUI (full-screen tabbed cockpit)\n unlock remove a stale/garbage run lock (refuses if the holder is alive)\n beat <tier> record one supervision beat for orchestrator|orchestrator-context|overseer|overseer-context|watch, --seat <identity> required (--stand-down to hand off); a supervising seat's own watcher loop calls it, and status reads the tier STALE once the beats stop\n approve <id> <task> release a park (--uphold sides with the reviewer and funds a fixed attempt; --by <name> --reason <text>); takes effect on resume";
|
|
9
9
|
export declare function dispatch(cmd: string | undefined, argv: string[], commands?: CommandMap): Promise<{
|
|
10
10
|
out: string;
|
|
11
11
|
code: number;
|
package/dist/cli/index.js
CHANGED
|
@@ -37,9 +37,9 @@ usage: tickmarkr <command>
|
|
|
37
37
|
plan dry-run routing table + cost estimate + floor lints
|
|
38
38
|
eval run checked-in fixtures against every channel in isolated temp repos
|
|
39
39
|
run execute the graph (--concurrency N --driver auto|herdr|subprocess|orca --route-strict; orca runs only when named)
|
|
40
|
-
status live run state
|
|
40
|
+
status live run state (--watch --events: JSON documents on stdout, keepalives on stderr; 2>&1 corrupts the stream)
|
|
41
41
|
stats all-run channel delivery, red, rescue, author and reviewer statistics
|
|
42
|
-
verify run the gate battery standalone against merge-base(--base, HEAD)..HEAD —
|
|
42
|
+
verify run the gate battery standalone against merge-base(--base, HEAD)..HEAD — verdict/JSON on stdout, progress on stderr; 2>&1 corrupts the verdict stream (--base main --criteria <file> | --task <id> [--files <glob>] [--author adapter:model] [--no-review] [--json])
|
|
43
43
|
resume <id> continue a run from its journal
|
|
44
44
|
report <id> cost/quality report (--md for committable execution record)
|
|
45
45
|
profile show learned routing profile (profile reset = forget history via cursor, keeps telemetry)
|
|
@@ -149,14 +149,7 @@ export declare function reviewParticipationErrors(tasks: ReadonlyArray<Pick<Task
|
|
|
149
149
|
* whose task objects carry no `context` gets byte-identical verdicts to the ones it got before the
|
|
150
150
|
* field existed, because an absent declaration grants no authority at all.
|
|
151
151
|
*
|
|
152
|
-
*
|
|
153
|
-
*
|
|
154
|
-
* PROGRAMMATIC compile whose target repo differs from process.cwd() resolves the wrong root and can
|
|
155
|
-
* miss that repo's own `review.criticalPaths`. Threading it is one line in src/compile/index.ts,
|
|
156
|
-
* outside this task's file scope. Two things bound the exposure meanwhile, both live: the critical
|
|
157
|
-
* set here is the UNION with the shipped defaults, so a wrong root can never lower enforcement below
|
|
158
|
-
* the shipped floor; and the review GATE — which is handed the run's real config — refuses to skip a
|
|
159
|
-
* critical path itself (src/gates/review.ts), so a lint this seam misses costs a late verdict rather
|
|
160
|
-
* than an unreviewed one. The compile lint is the early warning; the gate is the fail-closed backstop.
|
|
152
|
+
* The compile seam in src/compile/index.ts threads `compileSource`'s repo root here, so CLI tests
|
|
153
|
+
* and programmatic compiles check the same repository overlay the later run will use.
|
|
161
154
|
*/
|
|
162
155
|
export declare function taskUnitContractErrors(tasks: ReadonlyArray<Pick<Task, "id" | "goal" | "files" | "deps" | "acceptance" | "shape"> & Partial<Pick<Task, "context">>>, repoRoot?: string, review?: ReviewParticipation): string[];
|
|
@@ -896,15 +896,8 @@ function activeReviewParticipation(repoRoot) {
|
|
|
896
896
|
* whose task objects carry no `context` gets byte-identical verdicts to the ones it got before the
|
|
897
897
|
* field existed, because an absent declaration grants no authority at all.
|
|
898
898
|
*
|
|
899
|
-
*
|
|
900
|
-
*
|
|
901
|
-
* PROGRAMMATIC compile whose target repo differs from process.cwd() resolves the wrong root and can
|
|
902
|
-
* miss that repo's own `review.criticalPaths`. Threading it is one line in src/compile/index.ts,
|
|
903
|
-
* outside this task's file scope. Two things bound the exposure meanwhile, both live: the critical
|
|
904
|
-
* set here is the UNION with the shipped defaults, so a wrong root can never lower enforcement below
|
|
905
|
-
* the shipped floor; and the review GATE — which is handed the run's real config — refuses to skip a
|
|
906
|
-
* critical path itself (src/gates/review.ts), so a lint this seam misses costs a late verdict rather
|
|
907
|
-
* than an unreviewed one. The compile lint is the early warning; the gate is the fail-closed backstop.
|
|
899
|
+
* The compile seam in src/compile/index.ts threads `compileSource`'s repo root here, so CLI tests
|
|
900
|
+
* and programmatic compiles check the same repository overlay the later run will use.
|
|
908
901
|
*/
|
|
909
902
|
export function taskUnitContractErrors(tasks, repoRoot = process.cwd(), review = activeReviewParticipation(repoRoot)) {
|
|
910
903
|
return [
|
package/dist/compile/index.d.ts
CHANGED
|
@@ -10,6 +10,9 @@ export type PlanIR = {
|
|
|
10
10
|
tasks: RunGraph["tasks"];
|
|
11
11
|
};
|
|
12
12
|
export type PlanFinalizationHook = (plan: PlanIR) => PlanIR;
|
|
13
|
+
export type CompileOptions = {
|
|
14
|
+
strict?: boolean;
|
|
15
|
+
};
|
|
13
16
|
export declare function finalizePlan(plan: PlanIR, src: string, repoRoot?: string): RunGraph;
|
|
14
|
-
export declare function compileSource(src: string, type?: SourceType, root?: string, beforeFinalize?: PlanFinalizationHook): RunGraph;
|
|
17
|
+
export declare function compileSource(src: string, type?: SourceType, root?: string, beforeFinalize?: PlanFinalizationHook, options?: CompileOptions): RunGraph;
|
|
15
18
|
export {};
|