tickmarkr 1.84.0 → 1.86.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 +4 -2
- package/dist/adapters/catalog-remote.d.ts +64 -0
- package/dist/adapters/catalog-remote.js +287 -0
- package/dist/adapters/catalog.d.ts +96 -0
- package/dist/adapters/catalog.js +176 -0
- package/dist/adapters/claude-code.d.ts +1 -0
- package/dist/adapters/claude-code.js +59 -1
- package/dist/adapters/fake.js +42 -4
- package/dist/adapters/model-lints.d.ts +25 -5
- package/dist/adapters/model-lints.js +184 -50
- package/dist/adapters/model-windows.d.ts +31 -0
- package/dist/adapters/model-windows.js +69 -0
- package/dist/adapters/prompt.d.ts +5 -1
- package/dist/adapters/prompt.js +13 -4
- package/dist/adapters/registry.d.ts +25 -26
- package/dist/adapters/registry.js +173 -110
- package/dist/adapters/types.d.ts +3 -0
- package/dist/adapters/types.js +36 -3
- package/dist/brand.d.ts +5 -1
- package/dist/brand.js +18 -2
- package/dist/cli/commands/doctor.d.ts +3 -0
- package/dist/cli/commands/doctor.js +43 -21
- package/dist/cli/commands/fleet.d.ts +7 -0
- package/dist/cli/commands/fleet.js +94 -74
- package/dist/cli/commands/init.js +118 -5
- package/dist/cli/commands/status.js +202 -46
- package/dist/compile/collateral.d.ts +86 -2
- package/dist/compile/collateral.js +294 -3
- package/dist/compile/gsd.d.ts +2 -1
- package/dist/compile/gsd.js +68 -2
- package/dist/compile/native.d.ts +14 -0
- package/dist/compile/native.js +161 -12
- package/dist/config/config.d.ts +82 -5
- package/dist/config/config.js +253 -66
- package/dist/config/fleet-overlay.d.ts +25 -20
- package/dist/config/fleet-overlay.js +195 -77
- package/dist/config/fleet-why.d.ts +23 -0
- package/dist/config/fleet-why.js +42 -0
- package/dist/drivers/herdr.d.ts +21 -3
- package/dist/drivers/herdr.js +344 -110
- package/dist/gates/acceptance.js +7 -2
- package/dist/gates/baseline.d.ts +1 -0
- package/dist/gates/baseline.js +91 -13
- package/dist/gates/llm.d.ts +0 -1
- package/dist/gates/llm.js +5 -30
- package/dist/gates/review.d.ts +9 -1
- package/dist/gates/review.js +105 -10
- package/dist/gates/run-gates.d.ts +9 -0
- package/dist/gates/run-gates.js +285 -41
- package/dist/gates/verdict-cause.d.ts +4 -0
- package/dist/gates/verdict-cause.js +63 -0
- package/dist/graph/schema.d.ts +6 -0
- package/dist/graph/schema.js +8 -5
- package/dist/route/router.d.ts +0 -5
- package/dist/route/router.js +16 -20
- package/dist/run/consult.d.ts +6 -0
- package/dist/run/consult.js +35 -25
- package/dist/run/daemon.d.ts +48 -2
- package/dist/run/daemon.js +1488 -330
- package/dist/run/journal.d.ts +56 -3
- package/dist/run/journal.js +358 -4
- package/dist/run/stall.d.ts +35 -1
- package/dist/run/stall.js +118 -8
- package/dist/tui/cockpit/capture.d.ts +12 -0
- package/dist/tui/cockpit/capture.js +37 -1
- package/dist/tui/cockpit/components.d.ts +2 -0
- package/dist/tui/cockpit/components.js +8 -8
- package/dist/tui/cockpit/derive.d.ts +29 -2
- package/dist/tui/cockpit/derive.js +219 -23
- package/dist/tui/cockpit/run-cockpit.js +128 -27
- package/dist/tui/cockpit/theme.d.ts +32 -26
- package/dist/tui/cockpit/theme.js +11 -5
- package/dist/tui/ink/components.d.ts +0 -15
- package/dist/tui/ink/components.js +0 -17
- package/dist/tui/ink/fleet-app.d.ts +4 -1
- package/dist/tui/ink/fleet-app.js +134 -13
- package/fixtures/sample.native.md +1 -1
- package/package.json +1 -1
- package/skills/tickmarkr-overseer/SKILL.md +354 -34
- package/skills/tickmarkr-overseer/scripts/watch-artifacts.sh +70 -0
- package/skills/tickmarkr-overseer/scripts/watch-panes.sh +1 -1
- package/dist/tui/ink/studio-app.d.ts +0 -59
- package/dist/tui/ink/studio-app.js +0 -320
- package/dist/tui/save.d.ts +0 -38
- package/dist/tui/save.js +0 -96
- package/dist/tui/staging.d.ts +0 -29
- package/dist/tui/staging.js +0 -78
package/dist/compile/native.js
CHANGED
|
@@ -1,7 +1,61 @@
|
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
|
1
2
|
import { existsSync, readFileSync } from "node:fs";
|
|
3
|
+
import { basename, dirname } from "node:path";
|
|
2
4
|
import picomatch from "picomatch";
|
|
3
5
|
import { GATE_NAMES, GRAPH_ROUTING_MODES, ORACLES, SHAPES, TIERS, validateGraph } from "../graph/schema.js";
|
|
4
6
|
import { CompileError, inferShape, sha256 } from "./common.js";
|
|
7
|
+
// OBS-170/OBS-184: `context:` is a promise to the worker, and nothing ever checked it could be kept.
|
|
8
|
+
// Workers run in `git worktree add <baseRef>` (run/git.ts:169-176), which materialises the base
|
|
9
|
+
// TREE and copies nothing in — so the oracle is that tree, and NEVER two things that look like it:
|
|
10
|
+
// · existsSync reads the AUTHOR's checkout. Measured on v1.85: 10 of 16 unreachable entries are
|
|
11
|
+
// plainly present there (.overseer/ is 21 tracked files out of 480 on disk).
|
|
12
|
+
// · `git ls-files` reads the INDEX. `git add -f` writes the index, so a staged-uncommitted file
|
|
13
|
+
// reports present while still being invisible to every worker — the lint would then certify
|
|
14
|
+
// exactly the failure it exists to catch (RULING 2026-08-03 rider; the absent-vs-empty shape
|
|
15
|
+
// again, cf. fileBytes). Hence the opt-in is "add AND commit", and the resolver reads HEAD.
|
|
16
|
+
// `ls-tree -r HEAD` enumerates that tree once; per-path `cat-file -e HEAD:<path>` is the same oracle
|
|
17
|
+
// one path at a time, and one spawn beats N.
|
|
18
|
+
function trackedAtHead(dir) {
|
|
19
|
+
const git = (...args) => spawnSync("git", ["-C", dir, ...args], { encoding: "utf8", maxBuffer: 1 << 28 });
|
|
20
|
+
const top = git("rev-parse", "--show-toplevel");
|
|
21
|
+
// --full-tree is load-bearing: without it `ls-tree` scopes to the CWD's subtree and prints paths
|
|
22
|
+
// relative to it, so a spec in specs/ would see only specs/-relative names and misjudge every
|
|
23
|
+
// path outside it. Caught only by a fixture whose spec is NOT at the repo root.
|
|
24
|
+
const r = git("ls-tree", "--full-tree", "-r", "--name-only", "HEAD");
|
|
25
|
+
// not a repo, or no commits yet — fail open, so non-repo fixtures and fresh inits stay silent
|
|
26
|
+
if (top.status !== 0 || r.status !== 0 || typeof r.stdout !== "string")
|
|
27
|
+
return undefined;
|
|
28
|
+
return { root: top.stdout.trim(), tracked: new Set(r.stdout.split("\n").filter(Boolean)) };
|
|
29
|
+
}
|
|
30
|
+
const GLOB_CHARS = /[*?{[]/;
|
|
31
|
+
/**
|
|
32
|
+
* Classify one `context:` entry against the tree a worker's worktree is built from.
|
|
33
|
+
* ok — reachable
|
|
34
|
+
* untracked — in the author's checkout, invisible to every worker. WARNS, permanently: per-file
|
|
35
|
+
* force-add is the deliberate opt-in, so this is a standing prompt, not a migration.
|
|
36
|
+
* missing — absent from both. FAILS compile; carries a repair when exactly one tracked file
|
|
37
|
+
* shares the basename (ambiguous matches suggest nothing rather than guess).
|
|
38
|
+
* RULING 2026-08-03 (.overseer/OVERSEER-RULING-20260803-context-visibility.md): OBS-170's
|
|
39
|
+
* fail-closed-on-untracked is retired — oracle upheld, policy overruled (it blocked 11 of 13).
|
|
40
|
+
*/
|
|
41
|
+
export function classifyContextPath(entry, tracked, repoDir) {
|
|
42
|
+
if (GLOB_CHARS.test(entry))
|
|
43
|
+
return { kind: "ok" }; // a glob is not a promise about one file
|
|
44
|
+
const q = entry.replace(/\/+$/, "");
|
|
45
|
+
if (tracked.has(q))
|
|
46
|
+
return { kind: "ok" };
|
|
47
|
+
for (const t of tracked)
|
|
48
|
+
if (t.startsWith(`${q}/`))
|
|
49
|
+
return { kind: "ok" }; // directory with tracked children
|
|
50
|
+
if (existsSync(`${repoDir}/${q}`))
|
|
51
|
+
return { kind: "untracked" };
|
|
52
|
+
// Measured: 5 of v1.85's 6 absent entries are a tracked file written without its directory
|
|
53
|
+
// (`V185-SEEDS.md` for `.overseer/V185-SEEDS.md`) — the opt-in had worked, only the check was
|
|
54
|
+
// missing. Suggest ONLY on an unambiguous basename: two candidates is a guess, not a repair.
|
|
55
|
+
const base = basename(q);
|
|
56
|
+
const hits = [...tracked].filter((t) => basename(t) === base);
|
|
57
|
+
return hits.length === 1 ? { kind: "missing", suggestion: hits[0] } : { kind: "missing" };
|
|
58
|
+
}
|
|
5
59
|
// OBS-97: mirror of the vitest.config.ts suite include — the only path class vitest collects.
|
|
6
60
|
export const COLLECTABLE_TESTS = "tests/**/*.test.ts";
|
|
7
61
|
export const LEGACY_PREFIX = ["dro", "vr"].join("");
|
|
@@ -31,7 +85,7 @@ export function compileNative(file) {
|
|
|
31
85
|
for (const line of content.split("\n")) {
|
|
32
86
|
const heading = line.match(HEAD_RE);
|
|
33
87
|
if (heading) {
|
|
34
|
-
drafts.push({ id: heading[1], title: heading[2].trim(), fields: {}, acceptance: [], gates: [], hasGates: false, list: null,
|
|
88
|
+
drafts.push({ id: heading[1], title: heading[2].trim(), fields: {}, acceptance: [], gates: [], hasGates: false, list: null, continuationField: null });
|
|
35
89
|
continue;
|
|
36
90
|
}
|
|
37
91
|
const draft = drafts.at(-1);
|
|
@@ -59,29 +113,42 @@ export function compileNative(file) {
|
|
|
59
113
|
draft.list = name;
|
|
60
114
|
if (name === "gates")
|
|
61
115
|
draft.hasGates = true;
|
|
62
|
-
draft.
|
|
116
|
+
draft.continuationField = null;
|
|
63
117
|
}
|
|
64
118
|
else {
|
|
65
119
|
if (name === "goal" && !value)
|
|
66
120
|
invalid(draft.id, field[1], "must not be empty");
|
|
67
121
|
draft.fields[name] = value;
|
|
68
|
-
// OBS-60: carry
|
|
69
|
-
|
|
122
|
+
// OBS-60/OBS-308: carry wrapped physical lines for prose and comma-separated fields. A
|
|
123
|
+
// worker must receive the whole logical field, regardless of the author's editor width.
|
|
124
|
+
draft.continuationField = name === "goal" || name === "deps" || name === "files" || name === "context"
|
|
125
|
+
? name
|
|
126
|
+
: null;
|
|
70
127
|
draft.list = null;
|
|
71
128
|
}
|
|
72
129
|
continue;
|
|
73
130
|
}
|
|
74
|
-
// OBS-60:
|
|
75
|
-
|
|
131
|
+
// OBS-60/OBS-308: indented physical lines continue the preceding logical field until the next
|
|
132
|
+
// field/list/heading. List continuations join with a space so the existing comma parser sees the
|
|
133
|
+
// exact same value it would have received on one line.
|
|
134
|
+
if (draft.continuationField) {
|
|
135
|
+
const continuation = draft.continuationField;
|
|
76
136
|
if ((line.startsWith(" ") || line.startsWith("\t")) && !NESTED_RE.test(line)) {
|
|
77
|
-
|
|
78
|
-
|
|
137
|
+
const value = line.trim();
|
|
138
|
+
if (continuation === "goal") {
|
|
139
|
+
draft.fields.goal += `\n${value}`;
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
if (value) {
|
|
143
|
+
draft.fields[continuation] += ` ${value}`;
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
79
146
|
}
|
|
80
|
-
if (!line.trim()) {
|
|
147
|
+
if (continuation === "goal" && !line.trim()) {
|
|
81
148
|
draft.fields.goal += "\n";
|
|
82
149
|
continue;
|
|
83
150
|
}
|
|
84
|
-
draft.
|
|
151
|
+
draft.continuationField = null;
|
|
85
152
|
}
|
|
86
153
|
const nested = line.match(NESTED_RE);
|
|
87
154
|
if (nested && draft.list) {
|
|
@@ -124,12 +191,16 @@ export function compileNative(file) {
|
|
|
124
191
|
}
|
|
125
192
|
// v1.62 (OBS-97): commas inside {a,b} alternatives are part of one glob entry, not separators —
|
|
126
193
|
// split only at brace depth 0 so a brace glob reaches the lint (and the scope gate) intact.
|
|
194
|
+
// OBS-170/OBS-184: the same is true of a citation annotation. `.planning/OBSERVATIONS.md (OBS-262,
|
|
195
|
+
// OBS-263)` split at the inner comma into two bullets, and workers were dispatched a context line
|
|
196
|
+
// reading `OBS-263)`. Track paren depth alongside brace depth for exactly the OBS-97 reason.
|
|
127
197
|
const splitTop = (value) => {
|
|
128
198
|
const parts = [];
|
|
129
199
|
let depth = 0;
|
|
200
|
+
let paren = 0;
|
|
130
201
|
let current = "";
|
|
131
202
|
for (const ch of value) {
|
|
132
|
-
if (ch === "," && depth === 0) {
|
|
203
|
+
if (ch === "," && depth === 0 && paren === 0) {
|
|
133
204
|
parts.push(current);
|
|
134
205
|
current = "";
|
|
135
206
|
continue;
|
|
@@ -138,11 +209,34 @@ export function compileNative(file) {
|
|
|
138
209
|
depth++;
|
|
139
210
|
else if (ch === "}" && depth > 0)
|
|
140
211
|
depth--;
|
|
212
|
+
else if (ch === "(")
|
|
213
|
+
paren++;
|
|
214
|
+
else if (ch === ")" && paren > 0)
|
|
215
|
+
paren--;
|
|
141
216
|
current += ch;
|
|
142
217
|
}
|
|
143
218
|
return [...parts, current];
|
|
144
219
|
};
|
|
145
|
-
|
|
220
|
+
// Keeping the comma is only half of it: the merged entry still carries the annotation, so it
|
|
221
|
+
// resolves no better than the fragments did (measured: 24 → 19 unresolvable, zero rescued).
|
|
222
|
+
// Strip one trailing parenthetical so the entry is the path its author meant.
|
|
223
|
+
const stripAnnotation = (item) => {
|
|
224
|
+
const trimmed = item.trim();
|
|
225
|
+
if (!trimmed.endsWith(")"))
|
|
226
|
+
return trimmed;
|
|
227
|
+
let paren = 0;
|
|
228
|
+
for (let i = trimmed.length - 1; i >= 0; i--) {
|
|
229
|
+
if (trimmed[i] === ")")
|
|
230
|
+
paren++;
|
|
231
|
+
else if (trimmed[i] === "(") {
|
|
232
|
+
paren--;
|
|
233
|
+
if (paren === 0)
|
|
234
|
+
return trimmed.slice(0, i).trim();
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
return trimmed;
|
|
238
|
+
};
|
|
239
|
+
const csv = (value) => value && value.toLowerCase() !== "none" ? splitTop(value).map((item) => stripAnnotation(item.trim())).filter(Boolean) : [];
|
|
146
240
|
// OBS-97: a typed test: oracle needs a collectable home. vitest only collects COLLECTABLE_TESTS
|
|
147
241
|
// paths, so a task whose non-empty files[] cannot host one makes scope-green and acceptance-green
|
|
148
242
|
// mutually exclusive by construction — run-20260719-210434 burned two dispatch attempts before a
|
|
@@ -281,6 +375,31 @@ export function compileNative(file) {
|
|
|
281
375
|
}
|
|
282
376
|
}
|
|
283
377
|
}
|
|
378
|
+
// OBS-170/OBS-184: warn per unreachable context: entry, classified by the action its author must
|
|
379
|
+
// take. Warn-only by operator ruling (2026-08-03) — fail-closed would have refused to compile the
|
|
380
|
+
// spec that shipped green. Fails open when git cannot answer, so non-repo fixtures stay silent.
|
|
381
|
+
const repo = trackedAtHead(dirname(file));
|
|
382
|
+
if (repo) {
|
|
383
|
+
const unreachable = [];
|
|
384
|
+
for (const t of tasks) {
|
|
385
|
+
for (const entry of t.context) {
|
|
386
|
+
const { kind, suggestion } = classifyContextPath(entry, repo.tracked, repo.root);
|
|
387
|
+
if (kind === "untracked") {
|
|
388
|
+
// Permanent, by ruling: the per-file force-add IS the opt-in, so this stays a standing
|
|
389
|
+
// prompt. "and commit" is load-bearing — staging alone leaves it out of the base tree.
|
|
390
|
+
console.warn(`tickmarkr: OBS-170: task ${t.id} context ${JSON.stringify(entry)} exists in your checkout but is NOT in a worker's worktree. To make it worker context: git add -f ${entry} && git commit. Staging alone is not enough.`);
|
|
391
|
+
}
|
|
392
|
+
else if (kind === "missing") {
|
|
393
|
+
unreachable.push(` ${t.id}: ${JSON.stringify(entry)}${suggestion ? `\n → did you mean ${suggestion} ?` : "\n → not found in the repository, and not a path."}`);
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
if (unreachable.length) {
|
|
398
|
+
throw new CompileError(`context: paths that do not exist in ${file}:\n${unreachable.join("\n")}\n\n` +
|
|
399
|
+
`A context: entry is a promise the worker can read it. These resolve to nothing in the repository,\n` +
|
|
400
|
+
`so the worker would be told to read a file that is not there. Fix the paths and recompile.`);
|
|
401
|
+
}
|
|
402
|
+
}
|
|
284
403
|
return result;
|
|
285
404
|
}
|
|
286
405
|
// Commented native spec written to tickmarkr.spec.md by `tickmarkr init`. Documented via HTML comments (which the
|
|
@@ -319,6 +438,36 @@ acceptance is required on every task (a nested list of observable outcomes).
|
|
|
319
438
|
- test: <name> (oracle: test — named test)
|
|
320
439
|
- judge: <rubric> (oracle: judge — LLM-judged, free text)
|
|
321
440
|
- <plain text> (compat: compiles as judge oracle, warns)
|
|
441
|
+
|
|
442
|
+
HARD BOUNDS — these FAIL the compile, they do not warn:
|
|
443
|
+
- at most 6 acceptance items per task (no exception path)
|
|
444
|
+
- at most 8 files[] patterns; a {a,b} brace group counts as ONE pattern
|
|
445
|
+
- acceptance items x files[] patterns must be <= 24 ("surface")
|
|
446
|
+
- at most 60 goal words per acceptance criterion ("density")
|
|
447
|
+
Density is goal words DIVIDED BY items, so REMOVING a criterion RAISES it. Compress the goal in the
|
|
448
|
+
same edit, or a task that was comfortably inside the bound breaches it while you are tidying up.
|
|
449
|
+
|
|
450
|
+
WHAT MAKES A CRITERION REAL:
|
|
451
|
+
- "test:" must name a real test asserting on recorded state, journal lines, or drawn frames, and its
|
|
452
|
+
title must match the criterion string verbatim. It also needs a collectable test path in files[].
|
|
453
|
+
- NO criterion may be satisfiable by an absence, a rename, a source-text grep, or an empty collection.
|
|
454
|
+
"no file references X" is not a criterion — it passes in a repo where the feature was never built.
|
|
455
|
+
- "goal:" is NEVER verification. Prose in the goal enforces nothing: every obligation needs an
|
|
456
|
+
acceptance item, or a NAMED independent gate that actually applies. A gate is not independent of a
|
|
457
|
+
task that owns both the thing it checks and the fixture it checks against.
|
|
458
|
+
- A source-only obligation (a comment or doc that a change makes false) has no lawful "test:" — verify
|
|
459
|
+
it with "judge:", which reads the DIFF and must cite a changed line in a file the task owns.
|
|
460
|
+
|
|
461
|
+
ORDERING AND OWNERSHIP:
|
|
462
|
+
- Every path has exactly ONE owning task. Two tasks writing one file must be ORDERED by deps, or the
|
|
463
|
+
loser's work is silently dropped when the integration tip advances.
|
|
464
|
+
- A file one task CREATES cannot be "context:" for another — only deps: carries it, and that extends to
|
|
465
|
+
the task that PRODUCES a value, not just the file's existence.
|
|
466
|
+
- Deleting or renaming a symbol is a cross-task contract. Sweep for consumers by symbol AND by what the
|
|
467
|
+
mechanism DOES for them; the most dangerous consumer is a file no task owns, because nothing fixes it
|
|
468
|
+
and no edge can order it.
|
|
469
|
+
|
|
470
|
+
"timeout:" IS A KILL CEILING, NOT AN ESTIMATE. Never read a sum of timeouts as a predicted duration.
|
|
322
471
|
-->
|
|
323
472
|
|
|
324
473
|
## T1: Scaffold the feature
|
package/dist/config/config.d.ts
CHANGED
|
@@ -7,6 +7,7 @@ declare const TierEnum: z.ZodEnum<{
|
|
|
7
7
|
export type Tier = z.infer<typeof TierEnum>;
|
|
8
8
|
export declare const TIER_RANK: Record<Tier, number>;
|
|
9
9
|
export declare const DEFAULT_DIFF_CAP = 60000;
|
|
10
|
+
export declare const MODEL_WINDOW_PLAUSIBILITY_FLOOR = 1024;
|
|
10
11
|
export declare const ROUTING_MODES: readonly ["partner-led", "risk-based", "staff-led"];
|
|
11
12
|
declare const ModeEnum: z.ZodEnum<{
|
|
12
13
|
"partner-led": "partner-led";
|
|
@@ -26,7 +27,7 @@ export declare const MapEntrySchema: z.ZodObject<{
|
|
|
26
27
|
}, z.core.$strip>;
|
|
27
28
|
export type MapEntry = z.infer<typeof MapEntrySchema>;
|
|
28
29
|
export declare const TierEntrySchema: z.ZodObject<{
|
|
29
|
-
vendor: z.ZodString
|
|
30
|
+
vendor: z.ZodNullable<z.ZodString>;
|
|
30
31
|
channel: z.ZodEnum<{
|
|
31
32
|
sub: "sub";
|
|
32
33
|
api: "api";
|
|
@@ -36,6 +37,13 @@ export declare const TierEntrySchema: z.ZodObject<{
|
|
|
36
37
|
mid: "mid";
|
|
37
38
|
frontier: "frontier";
|
|
38
39
|
}>>;
|
|
40
|
+
modelOverrides: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
41
|
+
vendor: z.ZodOptional<z.ZodString>;
|
|
42
|
+
channel: z.ZodOptional<z.ZodEnum<{
|
|
43
|
+
sub: "sub";
|
|
44
|
+
api: "api";
|
|
45
|
+
}>>;
|
|
46
|
+
}, z.core.$strip>>>;
|
|
39
47
|
windows: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodNumber>>;
|
|
40
48
|
}, z.core.$strip>;
|
|
41
49
|
export type TierEntry = z.infer<typeof TierEntrySchema>;
|
|
@@ -52,6 +60,63 @@ export declare const SubPricingSchema: z.ZodObject<{
|
|
|
52
60
|
windowsPerMonthHigh: z.ZodNumber;
|
|
53
61
|
}, z.core.$strip>;
|
|
54
62
|
export type SubPricing = z.infer<typeof SubPricingSchema>;
|
|
63
|
+
export declare const REVIEW_POLICIES: readonly ["full", "judge-only"];
|
|
64
|
+
export type ReviewPolicy = (typeof REVIEW_POLICIES)[number];
|
|
65
|
+
/**
|
|
66
|
+
* The leaf class: work a judge alone can carry because there is no behaviour to review. It is CLOSED
|
|
67
|
+
* and enumerated here — documentation pages, the changelog, the release runbook, and the version
|
|
68
|
+
* mirrors a release bump rewrites. Everything else, including tests and scripts, is full-review work.
|
|
69
|
+
*/
|
|
70
|
+
export declare const REVIEW_LEAF_GLOBS: readonly ["docs/**", "CHANGELOG.md", "RELEASING.md", "package.json", "package-lock.json"];
|
|
71
|
+
/**
|
|
72
|
+
* The version mirrors a release bump rewrites — and ONLY at the repo root: `docs/package.json` is a
|
|
73
|
+
* real manifest for something, not a mirror. Membership here is still a claim about paths, and a
|
|
74
|
+
* manifest diff that moves more than the version field leaves the class; the gate re-tests that
|
|
75
|
+
* against the actual diff content (src/gates/review.ts) because a path cannot say it by itself.
|
|
76
|
+
*/
|
|
77
|
+
export declare const REVIEW_VERSION_MIRRORS: ReadonlySet<string>;
|
|
78
|
+
/**
|
|
79
|
+
* Paths whose review may never be skipped (R3): the gate/run/driver/adapter surface — lifecycle,
|
|
80
|
+
* demux, and everything that reaches a shell. Shipped as the default so the compile lint that
|
|
81
|
+
* enforces them is live in a repo that never configures anything.
|
|
82
|
+
*/
|
|
83
|
+
export declare const DEFAULT_REVIEW_CRITICAL_PATHS: readonly ["src/gates/**", "src/run/**", "src/drivers/**", "src/adapters/**"];
|
|
84
|
+
/**
|
|
85
|
+
* True when `path` — an ACTUAL path a diff touched — is provably leaf-class work. Enumerated, closed,
|
|
86
|
+
* and positive: the two named root files, the two root version mirrors, and documentation formats
|
|
87
|
+
* under `docs/`. Everything else, including a `.vue`/`.svelte`/`Makefile`/`package.json` parked under
|
|
88
|
+
* `docs/`, is behaviour and draws a full review.
|
|
89
|
+
*/
|
|
90
|
+
export declare function isReviewLeafPath(path: string): boolean;
|
|
91
|
+
/**
|
|
92
|
+
* True when a DECLARED pattern may claim leaf work. A declaration is a claim about a directory, not
|
|
93
|
+
* a file: `docs/**` is how a documentation task declares itself and has to stay claimable, so a glob
|
|
94
|
+
* rooted at `docs/` reads leaf here while `docs/tool.ts` does not read leaf above. The claim is never
|
|
95
|
+
* the last word — reviewGate re-tests it against what the diff actually touched and promotes when the
|
|
96
|
+
* diff leaves the class. A glob rooted anywhere else can claim nothing.
|
|
97
|
+
*/
|
|
98
|
+
export declare function isReviewLeafDeclaration(pattern: string): boolean;
|
|
99
|
+
/** full beats judge-only. The join is monotone by construction: nothing here can LOWER a policy. */
|
|
100
|
+
export declare function raiseReviewPolicy(...policies: ReadonlyArray<ReviewPolicy | undefined>): ReviewPolicy;
|
|
101
|
+
/**
|
|
102
|
+
* The compiler's assignment, from the task's DECLARED files[]. judge-only only when EVERY declared
|
|
103
|
+
* path is provably leaf work — one non-leaf pattern (or an empty files[]) makes it full.
|
|
104
|
+
*/
|
|
105
|
+
export declare function declaredReviewPolicy(files: ReadonlyArray<string>): ReviewPolicy;
|
|
106
|
+
/** The declared assignment after the operator's floor is applied — config may raise it, never lower it. */
|
|
107
|
+
export declare function effectiveReviewPolicy(files: ReadonlyArray<string>, review: {
|
|
108
|
+
policy?: ReviewPolicy;
|
|
109
|
+
}): ReviewPolicy;
|
|
110
|
+
/**
|
|
111
|
+
* Declared patterns that intersect a critical path. Directional containment is the certain half — a
|
|
112
|
+
* critical `src/gates/**` contains a declared `src/gates/review.ts`, and a declared `docs/**` contains
|
|
113
|
+
* a critical `docs/security/**`. It is NOT intersection: two globs can overlap without either matching
|
|
114
|
+
* the other as a literal string, which is how a judge-only task declaring `docs/**\/README.md` slipped
|
|
115
|
+
* past a critical `docs/security/*.md`. When BOTH sides are globs the conservative overlap test above
|
|
116
|
+
* decides, and it decides toward flagging — an over-flagged task is narrowed by its author, an
|
|
117
|
+
* under-flagged one skips the only gate that exists for it.
|
|
118
|
+
*/
|
|
119
|
+
export declare function criticalPathHits(files: ReadonlyArray<string>, criticalPaths?: ReadonlyArray<string>): string[];
|
|
55
120
|
export declare const TickmarkrConfigSchema: z.ZodObject<{
|
|
56
121
|
concurrency: z.ZodNumber;
|
|
57
122
|
driver: z.ZodEnum<{
|
|
@@ -116,7 +181,7 @@ export declare const TickmarkrConfigSchema: z.ZodObject<{
|
|
|
116
181
|
}, z.core.$strip>>;
|
|
117
182
|
}, z.core.$strip>;
|
|
118
183
|
tiers: z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
119
|
-
vendor: z.ZodString
|
|
184
|
+
vendor: z.ZodNullable<z.ZodString>;
|
|
120
185
|
channel: z.ZodEnum<{
|
|
121
186
|
sub: "sub";
|
|
122
187
|
api: "api";
|
|
@@ -126,6 +191,13 @@ export declare const TickmarkrConfigSchema: z.ZodObject<{
|
|
|
126
191
|
mid: "mid";
|
|
127
192
|
frontier: "frontier";
|
|
128
193
|
}>>;
|
|
194
|
+
modelOverrides: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
195
|
+
vendor: z.ZodOptional<z.ZodString>;
|
|
196
|
+
channel: z.ZodOptional<z.ZodEnum<{
|
|
197
|
+
sub: "sub";
|
|
198
|
+
api: "api";
|
|
199
|
+
}>>;
|
|
200
|
+
}, z.core.$strip>>>;
|
|
129
201
|
windows: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodNumber>>;
|
|
130
202
|
}, z.core.$strip>>;
|
|
131
203
|
pricing: z.ZodRecord<z.ZodString, z.ZodNumber>;
|
|
@@ -173,6 +245,11 @@ export declare const TickmarkrConfigSchema: z.ZodObject<{
|
|
|
173
245
|
complexityThreshold: z.ZodNumber;
|
|
174
246
|
required: z.ZodBoolean;
|
|
175
247
|
prefer: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
248
|
+
policy: z.ZodOptional<z.ZodEnum<{
|
|
249
|
+
full: "full";
|
|
250
|
+
"judge-only": "judge-only";
|
|
251
|
+
}>>;
|
|
252
|
+
criticalPaths: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
176
253
|
}, z.core.$strip>;
|
|
177
254
|
consult: z.ZodObject<{
|
|
178
255
|
adapter: z.ZodString;
|
|
@@ -239,8 +316,8 @@ export type InitConfigOverlay = {
|
|
|
239
316
|
};
|
|
240
317
|
};
|
|
241
318
|
export declare function configTemplate(overlay?: InitConfigOverlay): string;
|
|
242
|
-
export { FLEET_OVERLAY_KEYS, fleetEditableEquals, fleetRepoOverlayFromDelta,
|
|
243
|
-
export type {
|
|
319
|
+
export { FLEET_OVERLAY_KEYS, fleetEditableEquals, fleetRepoOverlayFromDelta, renderFleetOverlayWrite, repoOverlayYaml, serializeFleetOverlay, unifiedYamlDiff, } from "./fleet-overlay.js";
|
|
320
|
+
export type { FleetOverlayWrite } from "./fleet-overlay.js";
|
|
244
321
|
export type FleetTierAssignment = {
|
|
245
322
|
tier: Tier;
|
|
246
323
|
provenance?: string;
|
|
@@ -254,7 +331,7 @@ export type FleetEditable = {
|
|
|
254
331
|
};
|
|
255
332
|
export declare function repoOverlayPath(repoRoot: string): string;
|
|
256
333
|
export declare function readOverlayFile(path: string): Record<string, unknown>;
|
|
257
|
-
export declare function fleetEditableFromConfig(cfg: TickmarkrConfig
|
|
334
|
+
export declare function fleetEditableFromConfig(cfg: TickmarkrConfig): FleetEditable;
|
|
258
335
|
/** Resolve which layer last set a dotted fleet path (defaults < global < repo). */
|
|
259
336
|
export declare function fleetKeyLayer(repoRoot: string, dotted: string, opts?: {
|
|
260
337
|
globalDir?: string;
|