tickmarkr 2.6.1 → 2.6.2
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 +14 -3
- package/dist/adapters/catalog-remote.js +89 -47
- package/dist/adapters/claude-code.js +9 -6
- package/dist/adapters/codex.js +7 -4
- package/dist/adapters/prompt.d.ts +1 -0
- package/dist/adapters/prompt.js +14 -6
- package/dist/adapters/registry.js +3 -3
- package/dist/adapters/types.d.ts +12 -4
- package/dist/adapters/types.js +6 -0
- package/dist/cli/commands/approve.d.ts +5 -1
- package/dist/cli/commands/approve.js +66 -23
- package/dist/cli/commands/compile.js +13 -3
- package/dist/cli/commands/doctor.d.ts +2 -0
- package/dist/cli/commands/doctor.js +11 -3
- package/dist/cli/commands/fleet.js +45 -7
- package/dist/cli/commands/report.js +18 -2
- package/dist/cli/commands/resume.js +4 -2
- package/dist/cli/commands/status.js +24 -19
- package/dist/cli/help.d.ts +2 -0
- package/dist/cli/help.js +9 -2
- package/dist/config/config.d.ts +20 -0
- package/dist/config/config.js +47 -8
- package/dist/config/fleet-overlay.d.ts +1 -0
- package/dist/config/fleet-overlay.js +56 -0
- package/dist/drivers/orca.d.ts +26 -1
- package/dist/drivers/orca.js +193 -60
- package/dist/eval/canary.d.ts +2 -1
- package/dist/eval/canary.js +2 -2
- package/dist/eval/dispatch.js +1 -0
- package/dist/gates/acceptance.d.ts +2 -1
- package/dist/gates/acceptance.js +7 -2
- package/dist/gates/baseline.d.ts +12 -1
- package/dist/gates/baseline.js +11 -4
- package/dist/gates/llm.d.ts +5 -4
- package/dist/gates/llm.js +13 -13
- package/dist/gates/review.d.ts +8 -0
- package/dist/gates/review.js +40 -4
- package/dist/gates/run-gates.d.ts +2 -1
- package/dist/gates/run-gates.js +27 -13
- package/dist/gates/test-manifest.d.ts +3 -1
- package/dist/gates/test-manifest.js +9 -2
- package/dist/graph/schema.d.ts +2 -0
- package/dist/graph/schema.js +2 -0
- package/dist/plan/scope.js +2 -2
- package/dist/route/preference.d.ts +20 -2
- package/dist/route/preference.js +48 -13
- package/dist/route/router.js +30 -15
- package/dist/run/consult.d.ts +13 -1
- package/dist/run/consult.js +14 -5
- package/dist/run/daemon.d.ts +37 -2
- package/dist/run/daemon.js +579 -141
- package/dist/run/git.d.ts +8 -0
- package/dist/run/git.js +14 -0
- package/dist/run/journal.d.ts +126 -3
- package/dist/run/journal.js +410 -37
- package/dist/run/merge.d.ts +3 -1
- package/dist/run/merge.js +3 -2
- package/dist/run/operator-summary.d.ts +3 -0
- package/dist/run/operator-summary.js +3 -1
- package/dist/run/protocol.d.ts +31 -1
- package/dist/run/protocol.js +3 -1
- package/dist/run/supervision.d.ts +7 -1
- package/dist/run/supervision.js +5 -2
- package/dist/tui/cockpit/board.js +3 -3
- package/dist/tui/cockpit/decision-actions.d.ts +8 -5
- package/dist/tui/cockpit/decision-actions.js +55 -32
- package/dist/tui/cockpit/derive.js +13 -2
- package/dist/tui/cockpit/live-runtime.d.ts +10 -0
- package/dist/tui/cockpit/live-runtime.js +50 -3
- package/dist/tui/cockpit/run-cockpit.d.ts +3 -0
- package/dist/tui/cockpit/run-cockpit.js +26 -1
- package/dist/tui/cockpit/run-view.d.ts +9 -3
- package/dist/tui/cockpit/run-view.js +60 -7
- package/dist/tui/cockpit/setup-cockpit.d.ts +4 -0
- package/dist/tui/cockpit/setup-cockpit.js +6 -3
- package/dist/tui/ink/fleet-app.d.ts +15 -3
- package/dist/tui/ink/fleet-app.js +91 -22
- package/package.json +2 -1
- package/schema/config.schema.json +818 -0
- package/skills/tickmarkr-loop/SKILL.md +8 -2
- package/skills/tickmarkr-overseer/SKILL.md +42 -0
- package/skills/tickmarkr-overseer/scripts/classify-vitest-log.sh +88 -0
- package/skills/tickmarkr-overseer/scripts/context-statusline.sh +81 -0
- package/skills/tickmarkr-overseer/scripts/grade-ci.sh +36 -34
- package/skills/tickmarkr-overseer/scripts/watch-journal.sh +6 -4
package/dist/route/router.js
CHANGED
|
@@ -44,6 +44,7 @@ export function marginalCostRank(c) {
|
|
|
44
44
|
}
|
|
45
45
|
const toAssignment = (c) => ({
|
|
46
46
|
adapter: c.adapter, model: c.model, channel: c.channel, tier: c.tier,
|
|
47
|
+
...(c.effort ? { effort: c.effort } : {}),
|
|
47
48
|
});
|
|
48
49
|
function resolvePin(pin, channels) {
|
|
49
50
|
const c = channels.find((c) => c.adapter === pin.via && c.model === pin.model);
|
|
@@ -150,6 +151,13 @@ const maybeSlaLint = (lints, task, profile, slaMinutes, c) => {
|
|
|
150
151
|
lints.push(`${task.id} (${task.shape}): median ${medianMin}m exceeds sla ${slaMinutes}m — learned perf term references sla ${slaMinutes}m ref`);
|
|
151
152
|
};
|
|
152
153
|
export function route(task, cfg, channels, profile, preferCtx, exclude, exploreCtx) {
|
|
154
|
+
// OBS-1143: deny checks below read the probed identity the offered channels carry — the identity
|
|
155
|
+
// discovery admitted them under. A seat no channel vouches for stays unknown (conservative).
|
|
156
|
+
const observed = new Map(channels.flatMap((c) => (c.identity ? [[channelKey(c), c.identity]] : [])));
|
|
157
|
+
const seat = (adapter, model) => {
|
|
158
|
+
const identity = observed.get(channelKey({ adapter, model }));
|
|
159
|
+
return identity ? { adapter, model, identity } : { adapter, model };
|
|
160
|
+
};
|
|
153
161
|
channels = withoutExcluded(channels, exclude);
|
|
154
162
|
const lints = [];
|
|
155
163
|
const advisoryFloor = cfg.routing.floors[task.shape];
|
|
@@ -161,7 +169,7 @@ export function route(task, cfg, channels, profile, preferCtx, exclude, exploreC
|
|
|
161
169
|
const prefer = effectivePrefer(entry);
|
|
162
170
|
const prefActive = !!(cfg.routing.allow || cfg.routing.deny);
|
|
163
171
|
const disallowedPin = (via, model, kind) => {
|
|
164
|
-
const d = disallowedBy(
|
|
172
|
+
const d = disallowedBy(seat(via, model), cfg.routing);
|
|
165
173
|
if (d) {
|
|
166
174
|
throw new RoutingError(`${task.id}: ${kind} ${via}:${model} is disallowed by routing.${d.by} (${d.entry}) — remove the ${d.by} entry or re-pin to an allowed channel`);
|
|
167
175
|
}
|
|
@@ -174,8 +182,8 @@ export function route(task, cfg, channels, profile, preferCtx, exclude, exploreC
|
|
|
174
182
|
}
|
|
175
183
|
if (cfg.tiers[p]) {
|
|
176
184
|
const expanded = channelsFromConfig(p, cfg);
|
|
177
|
-
if (expanded.length && expanded.every((c) => disallowedBy(c, cfg.routing) !== null)) {
|
|
178
|
-
const d = disallowedBy(expanded[0], cfg.routing);
|
|
185
|
+
if (expanded.length && expanded.every((c) => disallowedBy(seat(c.adapter, c.model), cfg.routing) !== null)) {
|
|
186
|
+
const d = disallowedBy(seat(expanded[0].adapter, expanded[0].model), cfg.routing);
|
|
179
187
|
throw new RoutingError(`${task.id}: prefer entry ${p} is disallowed by routing.${d.by} (${d.entry}) — remove the ${d.by} entry or re-pin to an allowed channel`);
|
|
180
188
|
}
|
|
181
189
|
return;
|
|
@@ -228,21 +236,28 @@ export function route(task, cfg, channels, profile, preferCtx, exclude, exploreC
|
|
|
228
236
|
// single-channel pool) and the open floor/auto path. Exhaustion stays fail-loud (pin precedent).
|
|
229
237
|
if (entry?.pool) {
|
|
230
238
|
const pool = entry.pool;
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
+
// OBS-1144: a disallowed entry is skipped, named in provenance, never fatal for the pool; only an
|
|
240
|
+
// empty admitted remainder refuses. Filtering keeps declaration order, so ordered stays ordered.
|
|
241
|
+
const skipped = [];
|
|
242
|
+
const admitted = pool.channels.filter((p) => {
|
|
243
|
+
if (!prefActive)
|
|
244
|
+
return true;
|
|
245
|
+
const i = p.indexOf(":");
|
|
246
|
+
const d = disallowedBy(seat(p.slice(0, i), p.slice(i + 1)), cfg.routing);
|
|
247
|
+
if (d)
|
|
248
|
+
skipped.push(`${p} (disallowed by routing.${d.by} (${d.entry}))`);
|
|
249
|
+
return d === null;
|
|
250
|
+
});
|
|
251
|
+
const skippedNote = skipped.length ? `; skipped ${skipped.join(", ")}` : "";
|
|
252
|
+
if (!admitted.length) {
|
|
253
|
+
throw new RoutingError(`${task.id}: routing.map.${task.shape}.pool is exhausted — no admitted entry remains: ${skipped.join(", ")} — remove the deny/allow entry or add an admitted channel to the pool`);
|
|
239
254
|
}
|
|
240
255
|
// live preserves pool declaration order — ordered mode and any-mode stable-sort ties depend on it
|
|
241
|
-
const live =
|
|
256
|
+
const live = admitted
|
|
242
257
|
.map((id) => channels.find((c) => channelKey(c) === id))
|
|
243
258
|
.filter((c) => c !== undefined && (!taskFloor || TIER_RANK[c.tier] >= TIER_RANK[taskFloor]));
|
|
244
259
|
if (!live.length) {
|
|
245
|
-
throw new RoutingError(`${task.id}: routing.map.${task.shape}.pool has no live channel${taskFloor ? ` at task floor ${taskFloor}` : ""} — declared: ${pool.channels.join(", ")}; doctor found: ${channels.map(channelKey).join(", ") || "(nothing)"}`);
|
|
260
|
+
throw new RoutingError(`${task.id}: routing.map.${task.shape}.pool has no live channel${taskFloor ? ` at task floor ${taskFloor}` : ""} — declared: ${pool.channels.join(", ")}${skippedNote}; doctor found: ${channels.map(channelKey).join(", ") || "(nothing)"}`);
|
|
246
261
|
}
|
|
247
262
|
// No learned/explore inside pools: any spreads deterministically; ordered keeps declaration order.
|
|
248
263
|
const chosen = pool.mode === "ordered"
|
|
@@ -250,7 +265,7 @@ export function route(task, cfg, channels, profile, preferCtx, exclude, exploreC
|
|
|
250
265
|
: rankAnyPool(live, task)[0];
|
|
251
266
|
lintMapPinFloor(chosen.tier, "pool");
|
|
252
267
|
maybeSlaLint(lints, task, profile, slaMinutes, chosen);
|
|
253
|
-
return { assignment: toAssignment(chosen), ladder: ladderFor(task, entry), lints, provenance: `${degraded}pool ${pool.mode} ${channelKey(chosen)} (config routing.map)` };
|
|
268
|
+
return { assignment: toAssignment(chosen), ladder: ladderFor(task, entry), lints, provenance: `${degraded}pool ${pool.mode} ${channelKey(chosen)} (config routing.map${skippedNote})` };
|
|
254
269
|
}
|
|
255
270
|
const baseTier = floor ?? "cheap";
|
|
256
271
|
// A task floor is hard on pool and floor/auto paths; the map-pin branch above stays supreme.
|
|
@@ -268,7 +283,7 @@ export function route(task, cfg, channels, profile, preferCtx, exclude, exploreC
|
|
|
268
283
|
let msg = `${task.id}: no channel at tier>=${minTier}; available: ${channels.map(channelKey).join(", ") || "(none)"}`;
|
|
269
284
|
if (prefActive) {
|
|
270
285
|
const excluded = Object.keys(cfg.tiers).flatMap((id) => channelsFromConfig(id, cfg))
|
|
271
|
-
.filter((c) => disallowedBy(c, cfg.routing) !== null && TIER_RANK[c.tier] >= TIER_RANK[minTier])
|
|
286
|
+
.filter((c) => disallowedBy(seat(c.adapter, c.model), cfg.routing) !== null && TIER_RANK[c.tier] >= TIER_RANK[minTier])
|
|
272
287
|
.map(channelKey);
|
|
273
288
|
if (excluded.length)
|
|
274
289
|
msg += `; ${excluded.length} channel(s) excluded by routing.allow/deny: ${excluded.join(", ")}`;
|
package/dist/run/consult.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type WorkerAdapter } from "../adapters/types.js";
|
|
2
2
|
import type { TickmarkrConfig } from "../config/config.js";
|
|
3
|
+
import type { Effort } from "../graph/schema.js";
|
|
3
4
|
import type { ExecutorDriver, Slot } from "../drivers/types.js";
|
|
4
5
|
import type { GateResult } from "../gates/types.js";
|
|
5
6
|
import { type VerdictUnparseableCause } from "../gates/verdict-cause.js";
|
|
@@ -13,6 +14,16 @@ export interface ConsultVerdict {
|
|
|
13
14
|
adapter?: string;
|
|
14
15
|
model?: string;
|
|
15
16
|
vendor?: string;
|
|
17
|
+
effort?: Effort;
|
|
18
|
+
invocations?: ConsultInvocation[];
|
|
19
|
+
}
|
|
20
|
+
export interface ConsultInvocation {
|
|
21
|
+
adapter: string;
|
|
22
|
+
model: string;
|
|
23
|
+
vendor: string;
|
|
24
|
+
effort?: Effort;
|
|
25
|
+
outcome: "completed" | "failed";
|
|
26
|
+
reason: string;
|
|
16
27
|
}
|
|
17
28
|
export declare function renderRetryGuidance(v: ConsultVerdict): string;
|
|
18
29
|
export declare function augmentRetryBrief(feedback: string, opts: {
|
|
@@ -37,6 +48,7 @@ export declare function buildDossierPrompt(d: Dossier, nonce: string): string;
|
|
|
37
48
|
export declare function consult(d: Dossier, cfg: TickmarkrConfig, adapters: WorkerAdapter[], driver: ExecutorDriver, cwd: string, runDir: string, opts?: {
|
|
38
49
|
keep?: boolean;
|
|
39
50
|
onSlot?: (slot: Slot) => void;
|
|
51
|
+
onInvocation?: (inv: ConsultInvocation) => void;
|
|
40
52
|
runId?: string;
|
|
41
53
|
channels?: Array<{
|
|
42
54
|
adapter: string;
|
package/dist/run/consult.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { mkdirSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { getAdapter } from "../adapters/registry.js";
|
|
4
|
+
import { configuredEffort } from "../adapters/types.js";
|
|
4
5
|
import { bannerShell, paneDispatchCommand } from "../brand.js";
|
|
5
6
|
import { dewrapPaneVerdict, extractVerdictJson, gateExitTrailer, gatePaneName, generateVerdictNonce, verdictNonceLine } from "../gates/llm.js";
|
|
6
7
|
import { classifyVerdictCause } from "../gates/verdict-cause.js";
|
|
@@ -145,6 +146,8 @@ export async function consult(d, cfg, adapters, driver, cwd, runDir,
|
|
|
145
146
|
// via SlotOpts.owned; without it the legacy gatePaneName shape survives (non-daemon callers/tests).
|
|
146
147
|
// v1.54 T1: channels — the daemon's doctor-filtered live channel list; prefer-seat liveness is
|
|
147
148
|
// judged against it only (never rebuilt from config, which would select installed-but-unauthed seats).
|
|
149
|
+
// OBS-1182: onInvocation hears every launched seat — its identity, effort and outcome — failed seats
|
|
150
|
+
// included, so a seat that never produced the verdict still reaches the journal.
|
|
148
151
|
opts = {}) {
|
|
149
152
|
const n = ++consultSeq;
|
|
150
153
|
const nonce = generateVerdictNonce();
|
|
@@ -157,11 +160,11 @@ opts = {}) {
|
|
|
157
160
|
// One seat = the WHOLE invoke-and-parse unit, both visibility branches (OBS-69 class: a headless-only
|
|
158
161
|
// failover would leave the production pane path hard-failing on seat one). A null verdict retains
|
|
159
162
|
// the classifier's cause when extraction failed; parsed content rejections deliberately have none.
|
|
160
|
-
const invokeSeat = async (seatAdapter, seatModel, seatIdx) => {
|
|
163
|
+
const invokeSeat = async (seatAdapter, seatModel, seatIdx, effort) => {
|
|
161
164
|
const adapter = getAdapter(seatAdapter, adapters);
|
|
162
165
|
let out;
|
|
163
166
|
if (cfg.visibility.llm === "headless") {
|
|
164
|
-
const r = await sh(adapter.headlessCommand(promptFile, seatModel), cwd, cfg.consult.stallMinutes * 60_000);
|
|
167
|
+
const r = await sh(adapter.headlessCommand(promptFile, seatModel, effort), cwd, cfg.consult.stallMinutes * 60_000);
|
|
165
168
|
out = r.stdout + r.stderr;
|
|
166
169
|
}
|
|
167
170
|
else {
|
|
@@ -169,7 +172,7 @@ opts = {}) {
|
|
|
169
172
|
// but never exits, so the exit trailer after it never runs, the wait runs to the stall cap and
|
|
170
173
|
// the 300-line read is a TUI frame. The headless command exits, the trailer prints, and the
|
|
171
174
|
// pane is harvested through dewrap exactly like a judge or review seat.
|
|
172
|
-
const command = adapter.headlessCommand(promptFile, seatModel);
|
|
175
|
+
const command = adapter.headlessCommand(promptFile, seatModel, effort);
|
|
173
176
|
// T8: role-first pane name for fleet visibility (consult · T2); consultSeq stays on the dossier artifact only
|
|
174
177
|
const slot = await driver.slot(cwd, gatePaneName("consult", d.taskId), {
|
|
175
178
|
label: `CONSULT ${d.taskId}`,
|
|
@@ -242,8 +245,12 @@ opts = {}) {
|
|
|
242
245
|
};
|
|
243
246
|
}
|
|
244
247
|
for (const [i, seat] of allowedSeats.entries()) {
|
|
248
|
+
// OBS-1182: each seat launches at its own configured effort, never the worker's.
|
|
249
|
+
const effort = configuredEffort(cfg, seat);
|
|
250
|
+
const record = (outcome, reason) => opts.onInvocation?.({ ...seatIdentity(seat), ...(effort ? { effort } : {}), outcome, reason });
|
|
245
251
|
try {
|
|
246
|
-
const parsed = await invokeSeat(seat.adapter, seat.model, i);
|
|
252
|
+
const parsed = await invokeSeat(seat.adapter, seat.model, i, effort);
|
|
253
|
+
record(parsed.verdict ? "completed" : "failed", parsed.verdict ? "verdict" : `verdict unparseable${parsed.cause ? ` (${parsed.cause})` : ""}`);
|
|
247
254
|
if (parsed.verdict) {
|
|
248
255
|
const excludeProvider = parsed.verdict.excludeAdapter
|
|
249
256
|
? excludedProviderFromDossier(d, parsed.verdict.excludeAdapter)
|
|
@@ -255,11 +262,13 @@ opts = {}) {
|
|
|
255
262
|
notes: `${parsed.verdict.notes} — excluded provider ${excludeProvider}`,
|
|
256
263
|
} : {}),
|
|
257
264
|
...seatIdentity(seat),
|
|
265
|
+
...(effort ? { effort } : {}),
|
|
258
266
|
};
|
|
259
267
|
}
|
|
260
268
|
}
|
|
261
|
-
catch {
|
|
269
|
+
catch (e) {
|
|
262
270
|
// failed seat (unknown adapter, dead driver/pane, shell error) — fall to the next entry
|
|
271
|
+
record("failed", `seat error: ${e instanceof Error ? e.message : String(e)}`.slice(0, 200));
|
|
263
272
|
}
|
|
264
273
|
}
|
|
265
274
|
return {
|
package/dist/run/daemon.d.ts
CHANGED
|
@@ -2,12 +2,14 @@ import { type WorkerAdapter } from "../adapters/types.js";
|
|
|
2
2
|
import { type ModeResolution, type RoutingMode, type TickmarkrConfig } from "../config/config.js";
|
|
3
3
|
import { type DriverChoice } from "../drivers/index.js";
|
|
4
4
|
import { type ExecutorDriver, type Slot } from "../drivers/types.js";
|
|
5
|
-
import { type Baseline } from "../gates/baseline.js";
|
|
5
|
+
import { type Baseline, type BaselineProvenance } from "../gates/baseline.js";
|
|
6
6
|
import type { GateResult } from "../gates/types.js";
|
|
7
7
|
import { type RunGraph } from "../graph/schema.js";
|
|
8
8
|
import { type PreserveProducer } from "./git.js";
|
|
9
9
|
import { Journal, type JournalEvent } from "./journal.js";
|
|
10
10
|
export declare function closeLiveSlot(liveSlots: Set<Slot>, driver: Pick<ExecutorDriver, "close">, slot: Slot): Promise<void>;
|
|
11
|
+
export declare function setContactUnreadableDeadlineMsForTests(ms: number): void;
|
|
12
|
+
export declare function resetContactUnreadableDeadlineMsForTests(): void;
|
|
11
13
|
/** Optional authoritative transport receipt. Screen text can prove execution, never nonacceptance. */
|
|
12
14
|
export interface DispatchObservation {
|
|
13
15
|
slotId: string;
|
|
@@ -67,7 +69,34 @@ export interface RunSummary {
|
|
|
67
69
|
approvalDisposition?: "complete" | "outstanding";
|
|
68
70
|
/** the accepted approvals that never reached a dispatch — named, never left to the park buckets */
|
|
69
71
|
outstandingApprovals?: string[];
|
|
72
|
+
/** OBS-1123: every baseline-recorded red a standing green carried, with the capture that recorded it */
|
|
73
|
+
forgiven?: ForgivenFingerprints[];
|
|
70
74
|
}
|
|
75
|
+
/** OBS-1123: one forgiveness — the fingerprints a green carried and the capture that recorded them. */
|
|
76
|
+
export interface ForgivenFingerprints {
|
|
77
|
+
/** the task whose gate forgave them; absent for the integration tip */
|
|
78
|
+
taskId?: string;
|
|
79
|
+
gate: string;
|
|
80
|
+
/** absent: a legacy row that named no fingerprints */
|
|
81
|
+
fingerprints?: string[];
|
|
82
|
+
/** absent: a legacy capture or row that recorded no provenance — rendered unknown, never dated */
|
|
83
|
+
baseline?: BaselineProvenance;
|
|
84
|
+
}
|
|
85
|
+
/** Journal rows are untrusted input: a malformed field reads as not recorded, never as a value. */
|
|
86
|
+
export declare const fingerprintsOf: (v: unknown) => string[] | undefined;
|
|
87
|
+
export declare const baselineProvenanceOf: (v: unknown) => BaselineProvenance | undefined;
|
|
88
|
+
/** A forgiven battery green: the structured field, or the battery's own "(forgiven)" wording on a legacy row. */
|
|
89
|
+
export declare const forgivenGateRow: (data: Record<string, unknown>) => boolean;
|
|
90
|
+
/**
|
|
91
|
+
* OBS-1123: the forgiveness standing at this point in the journal — each task gate's LATEST verdict
|
|
92
|
+
* where it is a forgiven green, then the latest verification cycle's forgiven tip rows. A legacy row
|
|
93
|
+
* keeps what it never recorded absent, so the reader says "unknown" instead of borrowing a date.
|
|
94
|
+
*/
|
|
95
|
+
export declare function forgivenFingerprints(events: readonly JournalEvent[]): ForgivenFingerprints[];
|
|
96
|
+
/** The capture a forgiveness rests on, or an explicit unknown — never a time read off another clock. */
|
|
97
|
+
export declare function formatBaselineProvenance(p: BaselineProvenance | undefined): string;
|
|
98
|
+
export declare function formatFingerprints(fingerprints: string[] | undefined): string;
|
|
99
|
+
export declare function formatForgiven(f: ForgivenFingerprints): string;
|
|
71
100
|
export declare function pendingDaemonApprovalActions(events: JournalEvent[]): Map<string, import("./journal.js").PendingApprovalAction>;
|
|
72
101
|
/**
|
|
73
102
|
* T14, amended by v2.2 T3: approvals the run accepted and never acted on. `approved` above is still
|
|
@@ -131,6 +160,9 @@ export declare const resetApprovalWindowForTests: () => void;
|
|
|
131
160
|
/** Test seam — shrink the capacity backoff without minute-long sleeps. */
|
|
132
161
|
export declare function setCapacityBackoffMsForTests(ms: number): void;
|
|
133
162
|
export declare function resetCapacityBackoffMsForTests(): void;
|
|
163
|
+
/** Test seam — shrink the bootstrap backoff. */
|
|
164
|
+
export declare function setBootstrapBackoffMsForTests(ms: number): void;
|
|
165
|
+
export declare function resetBootstrapBackoffMsForTests(): void;
|
|
134
166
|
export declare const EARLY_LAUNCH_LIVENESS_MS = 60000;
|
|
135
167
|
/** Test seam — lowers the empty-pane liveness window without sleeping 60s per case. */
|
|
136
168
|
export declare function setEarlyLaunchLivenessMsForTests(ms: number): void;
|
|
@@ -154,8 +186,11 @@ export interface StartupFailureEvidence {
|
|
|
154
186
|
/** One seat, one startup prefix. Once execution or its input box is seen, later panes are ineligible. */
|
|
155
187
|
export declare class StartupFailureDetector {
|
|
156
188
|
private readonly adapter;
|
|
189
|
+
private readonly pattern;
|
|
190
|
+
private readonly wholePrefix;
|
|
157
191
|
private closed;
|
|
158
|
-
constructor(adapter: Pick<WorkerAdapter, "inputBox" | "harnessBannerRows"
|
|
192
|
+
constructor(adapter: Pick<WorkerAdapter, "inputBox" | "harnessBannerRows">, pattern?: RegExp, // OBS-1169: or known bootstrap text
|
|
193
|
+
wholePrefix?: boolean);
|
|
159
194
|
sample(output: string, launchedAt: number | undefined): StartupFailureEvidence | undefined;
|
|
160
195
|
}
|
|
161
196
|
/** Test seam — shrink the quota-banner silence gate without minute-long sleeps. */
|