tickmarkr 2.2.1 → 2.3.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 +10 -9
- package/dist/adapters/types.d.ts +20 -1
- package/dist/adapters/types.js +42 -2
- package/dist/cli/commands/approve.js +5 -4
- package/dist/cli/commands/beat.js +7 -4
- package/dist/cli/commands/doctor.d.ts +5 -1
- package/dist/cli/commands/doctor.js +67 -7
- package/dist/cli/commands/init.js +36 -21
- package/dist/cli/commands/plan.js +16 -2
- package/dist/cli/commands/report.js +37 -1
- package/dist/cli/commands/verify.d.ts +5 -0
- package/dist/cli/commands/verify.js +140 -25
- package/dist/compile/collateral.js +15 -9
- package/dist/compile/native.js +3 -3
- package/dist/config/config.js +1 -1
- package/dist/drivers/index.d.ts +6 -0
- package/dist/drivers/index.js +19 -4
- package/dist/drivers/orca.d.ts +22 -1
- package/dist/drivers/orca.js +147 -6
- package/dist/drivers/subprocess.d.ts +3 -3
- package/dist/drivers/subprocess.js +16 -9
- package/dist/drivers/types.d.ts +2 -0
- package/dist/gates/baseline.d.ts +2 -0
- package/dist/gates/baseline.js +21 -4
- package/dist/gates/llm.d.ts +6 -0
- package/dist/gates/llm.js +25 -9
- package/dist/gates/review.d.ts +3 -1
- package/dist/gates/review.js +40 -12
- package/dist/gates/run-gates.js +17 -10
- package/dist/gates/verdict-cause.d.ts +6 -2
- package/dist/gates/verdict-cause.js +8 -4
- package/dist/run/consult.js +5 -1
- package/dist/run/daemon.d.ts +12 -0
- package/dist/run/daemon.js +218 -25
- package/dist/run/git.d.ts +1 -0
- package/dist/run/git.js +4 -0
- package/dist/run/journal.d.ts +15 -2
- package/dist/run/journal.js +70 -12
- package/dist/run/supervision.d.ts +6 -0
- package/dist/run/supervision.js +29 -1
- package/dist/tui/ink/init-app.js +4 -4
- package/package.json +1 -1
- package/skills/tickmarkr-overseer/SKILL.md +77 -18
- package/skills/tickmarkr-overseer/scripts/seat-send.sh +88 -18
- package/skills/tickmarkr-overseer/scripts/watch-artifacts.sh +36 -2
- package/skills/tickmarkr-overseer/scripts/watch-contamination.sh +36 -15
- package/skills/tickmarkr-overseer/scripts/watch-context.sh +33 -7
- package/skills/tickmarkr-overseer/scripts/watch-pending-input.sh +32 -8
package/dist/gates/review.js
CHANGED
|
@@ -2,12 +2,13 @@ import { writeFileSync } from "node:fs";
|
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { channelKey, shq } from "../adapters/types.js";
|
|
4
4
|
import { criticalPathHits, DEFAULT_DIFF_CAP, DEFAULT_REVIEW_CRITICAL_PATHS, declaredReviewPolicy, isReviewLeafPath, raiseReviewPolicy, REVIEW_VERSION_MIRRORS, TIER_RANK, } from "../config/config.js";
|
|
5
|
+
import { filesGlob } from "../graph/files-glob.js";
|
|
5
6
|
import { renderAcceptanceItem } from "../graph/schema.js";
|
|
6
7
|
import { getAdapter } from "../adapters/registry.js";
|
|
7
8
|
import { shOk } from "../run/git.js";
|
|
8
9
|
import { redactSecrets } from "../run/redact.js";
|
|
9
10
|
import { marginalCostRank } from "../route/router.js";
|
|
10
|
-
import { appendAnchoredReview, COMPLETION_FAKING_CHECKLIST, extractVerdictJson, generateVerdictNonce,
|
|
11
|
+
import { appendAnchoredReview, COMPLETION_FAKING_CHECKLIST, extractVerdictJson, generateVerdictNonce, runLlmDetailed, verdictNonceLine } from "./llm.js";
|
|
11
12
|
import { classifyVerdictCause } from "./verdict-cause.js";
|
|
12
13
|
import { captureDiffCapFor, measureArtifactDiff, reviewableLogicDiff, } from "./artifact-manifest.js";
|
|
13
14
|
export { isProtectedEvidence, PROTECTED_EVIDENCE_PREFIXES, REGENERABLE_CAPTURE_PATHS, setAsideReceiptPath, setAsideRegenerableCaptures, } from "./artifact-manifest.js";
|
|
@@ -99,12 +100,14 @@ export async function mirrorsVersionOnly(worktree, baseRef, path) {
|
|
|
99
100
|
const changed = diff.split("\n").filter((l) => /^[+-]/.test(l) && !/^(?:\+\+\+|---)/.test(l));
|
|
100
101
|
return changed.length > 0 && changed.every((l) => VERSION_FIELD_LINE_RE.test(l));
|
|
101
102
|
}
|
|
102
|
-
export async function fetchTaskDiff(worktree, baseRef) {
|
|
103
|
+
export async function fetchTaskDiff(worktree, baseRef, files = []) {
|
|
103
104
|
// --full-index: abbreviated index lines vary with object-store density, so two measurements of
|
|
104
105
|
// the same diff could disagree by a few bytes between invocations (CI-only red, release 1.89.0).
|
|
105
|
-
const
|
|
106
|
-
|
|
107
|
-
|
|
106
|
+
const matched = files.length ? (await changedPaths(worktree, baseRef)).filter(filesGlob([...files])) : [];
|
|
107
|
+
const pathspec = matched.length ? ` -- ${matched.map(shq).join(" ")}` : "";
|
|
108
|
+
const [rawFull, rawForCap] = files.length && matched.length === 0 ? ["", ""] : await Promise.all([
|
|
109
|
+
shOk(`git diff --full-index '${baseRef}..HEAD'${pathspec}`, worktree),
|
|
110
|
+
shOk(`git diff --full-index -U0 '${baseRef}..HEAD'${pathspec}`, worktree),
|
|
108
111
|
]);
|
|
109
112
|
const fullMeasurement = measureArtifactDiff(rawFull);
|
|
110
113
|
const capMeasurement = measureArtifactDiff(rawForCap);
|
|
@@ -162,6 +165,24 @@ export function diffCapParkReason(results) {
|
|
|
162
165
|
export function modelId(model) {
|
|
163
166
|
return model.slice(model.lastIndexOf("/") + 1);
|
|
164
167
|
}
|
|
168
|
+
/** Provider identity comes from the served model, not a gateway adapter's stamped vendor. */
|
|
169
|
+
export function modelProvider(model, fallback = "unknown") {
|
|
170
|
+
const id = model.toLowerCase();
|
|
171
|
+
const prefix = id.includes("/") ? id.slice(0, id.indexOf("/")) : "";
|
|
172
|
+
if (prefix === "openai" || prefix === "openai-codex" || /^(?:gpt|o\d)/.test(id))
|
|
173
|
+
return "openai";
|
|
174
|
+
if (prefix === "anthropic" || /^(?:claude|opus|sonnet|haiku|fable)(?:-|$)/.test(id))
|
|
175
|
+
return "anthropic";
|
|
176
|
+
if (prefix === "google" || /^gemini(?:-|$)/.test(id))
|
|
177
|
+
return "google";
|
|
178
|
+
if (prefix === "xai" || /^grok(?:-|$)/.test(id))
|
|
179
|
+
return "xai";
|
|
180
|
+
if (["zai", "zhipu", "zai-coding-plan"].includes(prefix) || /^glm(?:-|$)/.test(id))
|
|
181
|
+
return "zhipu";
|
|
182
|
+
if (["kimi-code", "moonshot"].includes(prefix) || /^kimi(?:-|$)/.test(id))
|
|
183
|
+
return "moonshot";
|
|
184
|
+
return fallback;
|
|
185
|
+
}
|
|
165
186
|
// v1.53 T2: same entry grammar as routing.map.prefer (router.ts preferIndex — router is out of this
|
|
166
187
|
// module's dependency direction for a private fn, so the 3 lines live here too): `adapter` matches
|
|
167
188
|
// every channel of that adapter, `adapter:model` exactly one; unmatched channels sort after all entries.
|
|
@@ -179,11 +200,14 @@ floor) {
|
|
|
179
200
|
const authorChannel = channels.find((c) => c.adapter === author.adapter && c.model === author.model);
|
|
180
201
|
if (!authorChannel)
|
|
181
202
|
return null;
|
|
203
|
+
// Failover also guards true provider; the initial pick keeps the established stamped-vendor contract.
|
|
204
|
+
const authorProvider = modelProvider(author.model, authorChannel.vendor);
|
|
182
205
|
return (channels
|
|
183
206
|
// two independent axes: different vendor AND different base-model identity (ADDED TO the vendor
|
|
184
207
|
// rule, never replacing it — a future edit can't silently drop either). The diversity filter runs
|
|
185
208
|
// BEFORE preference ranking: prefer sorts survivors only, so no entry can resurrect an excluded channel.
|
|
186
209
|
.filter((c) => c.vendor !== authorChannel.vendor
|
|
210
|
+
&& (exclude.length === 0 || modelProvider(c.model, c.vendor) !== authorProvider)
|
|
187
211
|
&& modelId(c.model) !== modelId(author.model)
|
|
188
212
|
&& !exclude.includes(channelKey(c))
|
|
189
213
|
&& (floor === undefined || TIER_RANK[c.tier] >= TIER_RANK[floor]))
|
|
@@ -285,10 +309,10 @@ artifactDir) {
|
|
|
285
309
|
? `no cross-vendor reviewer available at or above task-declared ${reviewerFloor} floor (diversity rule)`
|
|
286
310
|
: "no cross-vendor reviewer available (diversity rule)";
|
|
287
311
|
return cfg.review.required
|
|
288
|
-
? { gate: "review", pass: false, details:
|
|
312
|
+
? { gate: "review", pass: false, details: `unreadable — ${reason}; set review.required:false to waive`, meta: { noEligibleReviewer: true, unreadable: true, ...(reviewerFloor ? { reviewerFloor } : {}) } }
|
|
289
313
|
: { gate: "review", pass: true, details: `WARNING: ${reason} — review waived by config`, meta: { noEligibleReviewer: true, ...(reviewerFloor ? { reviewerFloor } : {}) } };
|
|
290
314
|
}
|
|
291
|
-
const measuredDiff = await fetchTaskDiff(worktree, baseRef);
|
|
315
|
+
const measuredDiff = await fetchTaskDiff(worktree, baseRef, task.files);
|
|
292
316
|
// Keep the reader payload identical to the text charged to the strict cap:
|
|
293
317
|
// whole-file source deletions are represented by their citable operation fact.
|
|
294
318
|
const diff = reviewableLogicDiff(measuredDiff.full);
|
|
@@ -327,7 +351,7 @@ Approve iff no material finding remains; an empty findings list is a clean appro
|
|
|
327
351
|
The top-level comments array is optional. Use it only for actionable line-anchored feedback.
|
|
328
352
|
`;
|
|
329
353
|
let concludedOnInactivity = false;
|
|
330
|
-
const
|
|
354
|
+
const llm = await runLlmDetailed(getAdapter(reviewer.adapter, adapters), reviewer.model, prompt, worktree, via ? {
|
|
331
355
|
driver: via.driver,
|
|
332
356
|
keep: via.keep,
|
|
333
357
|
onSlot: via.onSlot,
|
|
@@ -341,13 +365,15 @@ The top-level comments array is optional. Use it only for actionable line-anchor
|
|
|
341
365
|
// (run-20260709-104447 P87-09). ponytail: literal 15min; make it cfg.review.timeoutMs if a
|
|
342
366
|
// second knob-turner appears.
|
|
343
367
|
900_000);
|
|
368
|
+
const raw = llm.output;
|
|
369
|
+
const provider = modelProvider(reviewer.model, reviewer.vendor);
|
|
344
370
|
const v = extractVerdictJson(raw, nonce);
|
|
345
371
|
const findings = v && Array.isArray(v.findings) ? v.findings : null;
|
|
346
372
|
// findings decides the verdict on its own; the legacy path still needs approve + issues to parse.
|
|
347
373
|
if (!v || (findings === null && (typeof v.approve !== "boolean" || !Array.isArray(v.issues)))) {
|
|
348
374
|
// OBS-196: name the cause and persist the raw bytes — a ruled-on "unparseable" without its
|
|
349
375
|
// evidence cannot be audited, and a cutoff must never be indistinguishable from a parse defect.
|
|
350
|
-
const cause = classifyVerdictCause(raw, nonce, "approve");
|
|
376
|
+
const cause = classifyVerdictCause(raw, nonce, "approve", llm);
|
|
351
377
|
const bytes = Buffer.byteLength(raw, "utf8");
|
|
352
378
|
let saved;
|
|
353
379
|
if (artifactDir) {
|
|
@@ -367,10 +393,12 @@ The top-level comments array is optional. Use it only for actionable line-anchor
|
|
|
367
393
|
return {
|
|
368
394
|
gate: "review",
|
|
369
395
|
pass: false,
|
|
370
|
-
details: `${failure} (reviewer ${reviewer.adapter}:${reviewer.model}; cause: ${cause}${saved ? `; raw saved: ${saved}` : ""}) — failing closed`,
|
|
396
|
+
details: `${failure} (reviewer ${reviewer.adapter}:${reviewer.model}; vendor: ${reviewer.vendor}; provider: ${provider}; cause: ${cause}${saved ? `; raw saved: ${saved}` : ""}) — failing closed`,
|
|
371
397
|
meta: {
|
|
372
398
|
...policyMeta,
|
|
373
399
|
reviewer: channelKey(reviewer),
|
|
400
|
+
vendor: reviewer.vendor,
|
|
401
|
+
provider,
|
|
374
402
|
unparseable: true,
|
|
375
403
|
cause,
|
|
376
404
|
...(cause === "empty-output" ? { bytes } : {}),
|
|
@@ -381,11 +409,11 @@ The top-level comments array is optional. Use it only for actionable line-anchor
|
|
|
381
409
|
const decided = findings !== null
|
|
382
410
|
? classifyReviewFindings(findings)
|
|
383
411
|
: classifyReviewIssues(v.approve, v.issues);
|
|
384
|
-
const prose = `reviewer ${reviewer.adapter}:${reviewer.model} (${reviewer.vendor}): ${decided.headline}${decided.lines.length ? "\n" + decided.lines.join("\n") : ""}`;
|
|
412
|
+
const prose = `reviewer ${reviewer.adapter}:${reviewer.model} (vendor: ${reviewer.vendor}; provider: ${provider}): ${decided.headline}${decided.lines.length ? "\n" + decided.lines.join("\n") : ""}`;
|
|
385
413
|
return {
|
|
386
414
|
gate: "review",
|
|
387
415
|
pass: decided.pass,
|
|
388
416
|
details: appendAnchoredReview(prose, v),
|
|
389
|
-
meta: { ...policyMeta, reviewer: channelKey(reviewer) },
|
|
417
|
+
meta: { ...policyMeta, reviewer: channelKey(reviewer), vendor: reviewer.vendor, provider },
|
|
390
418
|
};
|
|
391
419
|
}
|
package/dist/gates/run-gates.js
CHANGED
|
@@ -11,7 +11,7 @@ import { evidenceGate } from "./evidence.js";
|
|
|
11
11
|
import { captureLlmOutput } from "./llm.js";
|
|
12
12
|
import { disallowedBy } from "../route/preference.js";
|
|
13
13
|
import { marginalCostRank } from "../route/router.js";
|
|
14
|
-
import { reviewGate } from "./review.js";
|
|
14
|
+
import { pickReviewer, reviewGate } from "./review.js";
|
|
15
15
|
import { scopeGate } from "./scope.js";
|
|
16
16
|
import { shGit } from "../run/git.js";
|
|
17
17
|
import { withJudgeInvocationEvidence } from "../run/journal.js";
|
|
@@ -540,7 +540,7 @@ export async function runGates(task, ctx) {
|
|
|
540
540
|
.sort((x, y) => TIER_RANK[y.tier] - TIER_RANK[x.tier] || marginalCostRank(x) - marginalCostRank(y))[0];
|
|
541
541
|
// v1.87 T2: the failover seat obeys the same policy the primary judge just passed — a denied
|
|
542
542
|
// channel is refused here too, never reached by falling through the exclusion arms below.
|
|
543
|
-
const judgePool = (ctx.judgeChannels ??
|
|
543
|
+
const judgePool = (ctx.judgeChannels ?? []).filter((c) => disallowedBy(c, ctx.cfg.routing, "judge") === null);
|
|
544
544
|
const crossAdapter = pick(judgePool.filter((c) => c.adapter !== flakedAdapter));
|
|
545
545
|
const sameAdapter = pick(judgePool.filter((c) => c.adapter === flakedAdapter && channelKey(c) !== flakedKey));
|
|
546
546
|
// Prefer a different adapter; if the fleet only has one adapter, retry on a different channel of
|
|
@@ -576,11 +576,9 @@ export async function runGates(task, ctx) {
|
|
|
576
576
|
return captured.value;
|
|
577
577
|
};
|
|
578
578
|
let rv = await dispatch((adapters) => reviewGate(task, ctx.worktree, ctx.baseRef, ctx.author, ctx.channels, adapters, ctx.cfg, ctx.via, ctx.excludeReviewers, ctx.artifactDir));
|
|
579
|
-
// OBS-193: an unparseable review verdict retries the REVIEW exactly once
|
|
580
|
-
//
|
|
581
|
-
//
|
|
582
|
-
// parameter, so pickReviewer's diversity rules still govern the retry seat; a fleet with no second
|
|
583
|
-
// eligible seat keeps the ORIGINAL result so the recorded cause stays truthful (OBS-196).
|
|
579
|
+
// OBS-193/574: an unparseable review verdict retries the REVIEW exactly once, preferring a
|
|
580
|
+
// different adapter. Only a single-adapter eligible pool may fall back to another channel on the
|
|
581
|
+
// flaked adapter. The flaked verdict never enters results; an exhausted pool preserves its cause.
|
|
584
582
|
if (rv.meta?.unparseable === true && typeof rv.meta.reviewer === "string") {
|
|
585
583
|
const flaked = rv.meta.reviewer;
|
|
586
584
|
const emptyOutput = rv.meta.cause === "empty-output";
|
|
@@ -594,15 +592,24 @@ export async function runGates(task, ctx) {
|
|
|
594
592
|
const retryVia = ctx.via
|
|
595
593
|
? { ...ctx.via, nameFor: (role, adapter) => ctx.via.nameFor(role, adapter) + "-r1" }
|
|
596
594
|
: undefined;
|
|
597
|
-
const
|
|
595
|
+
const priorExclusions = ctx.excludeReviewers ?? [];
|
|
596
|
+
const flakedAdapter = flaked.slice(0, flaked.indexOf(":"));
|
|
597
|
+
const adapterExclusions = ctx.channels.filter((c) => c.adapter === flakedAdapter).map(channelKey);
|
|
598
|
+
const crossAdapter = pickReviewer(ctx.author, ctx.channels, [...priorExclusions, ...adapterExclusions], ctx.cfg.review.prefer ?? [], task.routingHints?.floor);
|
|
599
|
+
const exclusion = crossAdapter ? "adapter" : "channel";
|
|
600
|
+
const retryExclusions = [...priorExclusions, ...(crossAdapter ? adapterExclusions : [flaked])];
|
|
601
|
+
const second = await dispatch((adapters) => reviewGate(task, ctx.worktree, ctx.baseRef, ctx.author, ctx.channels, adapters, ctx.cfg, retryVia, retryExclusions, ctx.artifactDir));
|
|
598
602
|
if (second.meta?.noEligibleReviewer !== true) {
|
|
599
603
|
const retried = typeof second.meta?.reviewer === "string" ? second.meta.reviewer : "none";
|
|
604
|
+
const route = exclusion === "adapter"
|
|
605
|
+
? `different-adapter retry; excluded flaked adapter ${flakedAdapter}`
|
|
606
|
+
: `same-adapter fallback; excluded flaked channel ${flaked}`;
|
|
600
607
|
rv = {
|
|
601
608
|
...second,
|
|
602
609
|
// `details` is lifted onto the journal's gate-result row; meta.reviewRetry is not. Keep the
|
|
603
610
|
// re-route visible in the result text a reader actually opens, including on a red retry.
|
|
604
|
-
details: `review re-route: ${flaked} produced ${emptyOutput ? "EMPTY output" : "no parseable verdict"}; replaced by ${retried}\n${second.details}`,
|
|
605
|
-
meta: { ...second.meta, reviewRetry: { flaked, retried } },
|
|
611
|
+
details: `review re-route (${route}): ${flaked} produced ${emptyOutput ? "EMPTY output" : "no parseable verdict"}; replaced by ${retried}\n${second.details}`,
|
|
612
|
+
meta: { ...second.meta, reviewRetry: { flaked, retried, exclusion } },
|
|
606
613
|
};
|
|
607
614
|
}
|
|
608
615
|
else if (task.routingHints?.floor) {
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
export type VerdictDiscriminator = "approve" | "pass" | "ok" | "action";
|
|
2
|
-
export type VerdictUnparseableCause = "empty-output" | "no-verdict" | "malformed-verdict";
|
|
2
|
+
export type VerdictUnparseableCause = "empty-output" | "no-verdict" | "malformed-verdict" | "timeout" | "startup-failure";
|
|
3
|
+
export interface VerdictProcessFacts {
|
|
4
|
+
timedOut?: boolean;
|
|
5
|
+
exitCode?: number;
|
|
6
|
+
}
|
|
3
7
|
export declare function hasVerdictParticipationWitness(raw: string, nonce: string, discriminator: VerdictDiscriminator): boolean;
|
|
4
|
-
export declare function classifyVerdictCause(raw: string, nonce: string, discriminator: VerdictDiscriminator): VerdictUnparseableCause;
|
|
8
|
+
export declare function classifyVerdictCause(raw: string, nonce: string, discriminator: VerdictDiscriminator, process?: VerdictProcessFacts): VerdictUnparseableCause;
|
|
@@ -54,10 +54,14 @@ export function hasVerdictParticipationWitness(raw, nonce, discriminator) {
|
|
|
54
54
|
const discriminatorPattern = new RegExp(String.raw `"${discriminator}"\s*:\s*${valuePattern}\s*[,}]`);
|
|
55
55
|
return objectPrefixes(raw).some((prefix) => noncePattern.test(prefix) && discriminatorPattern.test(prefix));
|
|
56
56
|
}
|
|
57
|
-
export function classifyVerdictCause(raw, nonce, discriminator) {
|
|
57
|
+
export function classifyVerdictCause(raw, nonce, discriminator, process = {}) {
|
|
58
|
+
if (process.timedOut)
|
|
59
|
+
return "timeout";
|
|
58
60
|
if (raw.trim().length === 0)
|
|
59
61
|
return "empty-output";
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
62
|
+
if (hasVerdictParticipationWitness(raw, nonce, discriminator))
|
|
63
|
+
return "malformed-verdict";
|
|
64
|
+
if (process.exitCode !== undefined && process.exitCode !== 0)
|
|
65
|
+
return "startup-failure";
|
|
66
|
+
return "no-verdict";
|
|
63
67
|
}
|
package/dist/run/consult.js
CHANGED
|
@@ -147,6 +147,10 @@ opts = {}) {
|
|
|
147
147
|
out = r.stdout + r.stderr;
|
|
148
148
|
}
|
|
149
149
|
else {
|
|
150
|
+
// Preserve the adapter contract for CLIs that cannot seed their TUI: supported adapters use
|
|
151
|
+
// the interactive form, while a declared null keeps the existing visible print fallback.
|
|
152
|
+
const command = adapter.interactiveCommand(promptFile, seatModel)
|
|
153
|
+
?? adapter.headlessCommand(promptFile, seatModel);
|
|
150
154
|
// T8: role-first pane name for fleet visibility (consult · T2); consultSeq stays on the dossier artifact only
|
|
151
155
|
const slot = await driver.slot(cwd, gatePaneName("consult", d.taskId), {
|
|
152
156
|
label: `CONSULT ${d.taskId}`,
|
|
@@ -158,7 +162,7 @@ opts = {}) {
|
|
|
158
162
|
writeFileSync(scriptPath, [
|
|
159
163
|
"export BASH_SILENCE_DEPRECATION_WARNING=1",
|
|
160
164
|
bannerShell(),
|
|
161
|
-
|
|
165
|
+
command,
|
|
162
166
|
gateExitTrailer(nonce),
|
|
163
167
|
].join("\n"));
|
|
164
168
|
try {
|
package/dist/run/daemon.d.ts
CHANGED
|
@@ -102,6 +102,8 @@ export declare const gateSatisfied: (g: GateResult) => boolean;
|
|
|
102
102
|
* A round is the gate-result span opened by each `gates` phase-start, per task.
|
|
103
103
|
*/
|
|
104
104
|
export declare function decisiveReviewRounds(events: JournalEvent[]): JournalEvent[];
|
|
105
|
+
export declare const SUITE_POLL_MS = 250;
|
|
106
|
+
export declare const APPROVAL_POLL_MS = 250;
|
|
105
107
|
export declare const EARLY_LAUNCH_LIVENESS_MS = 60000;
|
|
106
108
|
/** Test seam — lowers the empty-pane liveness window without sleeping 60s per case. */
|
|
107
109
|
export declare function setEarlyLaunchLivenessMsForTests(ms: number): void;
|
|
@@ -141,6 +143,16 @@ export declare function verifyIntegrationTipCached(intWt: string, commands: Reco
|
|
|
141
143
|
lastMergedTask?: string;
|
|
142
144
|
baseline?: Baseline;
|
|
143
145
|
}): Promise<boolean>;
|
|
146
|
+
type SuitePidProbe = (pid: number) => number | undefined;
|
|
147
|
+
/** Count full-suite roots in one process-table snapshot. The probes are arguments so the ownership
|
|
148
|
+
* rules remain testable on hosts that forbid process inspection; production supplies cwd and the
|
|
149
|
+
* inherited TICKMARKR_SUITE_PARENT marker from the process itself. */
|
|
150
|
+
export declare function countLiveSuites(snapshot: string, repoRoot: string, daemonPid?: number, cwdForPid?: (pid: number) => string | undefined, suiteParentForPid?: SuitePidProbe): number;
|
|
151
|
+
export declare const setLiveSuiteCountForTests: (probe: (repoRoot: string) => Promise<number>) => void;
|
|
152
|
+
export declare const resetLiveSuiteCountForTests: () => void;
|
|
153
|
+
/** Live full-suite roots attributable to this repository or this daemon. Ancestors are excluded so
|
|
154
|
+
* a daemon invoked by vitest does not wait on its own test harness forever. */
|
|
155
|
+
export declare function liveSuiteCount(repoRoot: string): Promise<number>;
|
|
144
156
|
/** Test seam — exercise the production observer's total read bound with a small real tree. */
|
|
145
157
|
export declare function setObserveBudgetBytesForTests(bytes: number): void;
|
|
146
158
|
export declare function resetObserveBudgetBytesForTests(): void;
|