tickmarkr 2.5.9 → 2.6.1
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/claude-code.js +19 -6
- package/dist/adapters/prompt.d.ts +2 -1
- package/dist/adapters/prompt.js +11 -1
- package/dist/adapters/types.d.ts +4 -0
- package/dist/adapters/types.js +10 -0
- package/dist/cli/commands/fleet.js +26 -1
- package/dist/cli/commands/init.js +1 -1
- package/dist/cli/commands/plan.js +7 -3
- package/dist/cli/commands/report.js +11 -1
- package/dist/cli/commands/status.js +13 -3
- package/dist/cli/commands/verify.js +2 -0
- package/dist/cli/help.d.ts +2 -0
- package/dist/cli/help.js +2 -0
- package/dist/compile/native.js +39 -4
- package/dist/drivers/orca.d.ts +1 -1
- package/dist/drivers/orca.js +27 -5
- package/dist/gates/baseline.d.ts +2 -0
- package/dist/gates/baseline.js +9 -1
- package/dist/gates/cache.d.ts +11 -1
- package/dist/gates/cache.js +35 -15
- package/dist/gates/llm.js +4 -1
- package/dist/gates/review.d.ts +2 -3
- package/dist/gates/review.js +28 -35
- package/dist/gates/run-gates.d.ts +4 -0
- package/dist/gates/run-gates.js +17 -4
- package/dist/gates/test-manifest.d.ts +17 -0
- package/dist/gates/test-manifest.js +109 -9
- package/dist/gates/test-reporter.js +4 -0
- package/dist/graph/graph.d.ts +7 -3
- package/dist/graph/graph.js +21 -5
- package/dist/graph/schema.d.ts +2 -0
- package/dist/graph/schema.js +2 -0
- package/dist/route/preference.d.ts +1 -1
- package/dist/route/preference.js +10 -39
- package/dist/route/role-pick.d.ts +16 -0
- package/dist/route/role-pick.js +15 -0
- package/dist/route/router.d.ts +14 -0
- package/dist/route/router.js +9 -1
- package/dist/run/consult.js +5 -9
- package/dist/run/daemon.d.ts +16 -1
- package/dist/run/daemon.js +554 -77
- package/dist/run/git.d.ts +46 -1
- package/dist/run/git.js +138 -6
- package/dist/run/host-health.d.ts +20 -0
- package/dist/run/host-health.js +64 -0
- package/dist/run/journal.d.ts +1 -1
- package/dist/run/journal.js +51 -6
- package/dist/run/merge.d.ts +1 -1
- package/dist/run/merge.js +30 -6
- package/dist/run/operator-state.d.ts +24 -2
- package/dist/run/operator-state.js +41 -5
- package/dist/run/stall.d.ts +38 -2
- package/dist/run/stall.js +276 -6
- package/dist/tui/cockpit/board.d.ts +1 -1
- package/dist/tui/cockpit/board.js +27 -19
- package/dist/tui/cockpit/derive.d.ts +2 -0
- package/dist/tui/cockpit/derive.js +4 -0
- package/dist/tui/cockpit/live-runtime.d.ts +4 -0
- package/dist/tui/cockpit/live-runtime.js +37 -3
- package/dist/tui/cockpit/live-store.d.ts +3 -0
- package/dist/tui/cockpit/live-store.js +31 -8
- package/dist/tui/cockpit/run-cockpit.js +2 -1
- package/dist/tui/cockpit/run-view.d.ts +2 -4
- package/dist/tui/cockpit/run-view.js +9 -8
- package/package.json +1 -1
- package/schema/rungraph.schema.json +7 -0
- package/skills/tickmarkr-overseer/SKILL.md +76 -14
package/dist/graph/graph.d.ts
CHANGED
|
@@ -24,7 +24,7 @@ export declare function onDiskSpecHash(_repoRoot: string, graph: RunGraph): OnDi
|
|
|
24
24
|
/** A task's definition minus its files[] (and run state): what a scope amendment must NOT move (OBS-1073). */
|
|
25
25
|
export declare function taskDefinitionFingerprint(task: Task): string;
|
|
26
26
|
export declare function graphDefinitionHash(g: RunGraph): string;
|
|
27
|
-
export declare function taskContentDigest(task: Pick<Task, "goal" | "files" | "acceptance">): string;
|
|
27
|
+
export declare function taskContentDigest(task: Pick<Task, "goal" | "files" | "acceptance" | "outOfScope">): string;
|
|
28
28
|
export declare function tickmarkrDir(repoRoot: string): string;
|
|
29
29
|
export declare function loadGraph(repoRoot: string): RunGraph;
|
|
30
30
|
export declare function saveGraph(repoRoot: string, g: RunGraph): void;
|
|
@@ -36,8 +36,12 @@ export declare function addEvidence(g: RunGraph, id: string, patch: {
|
|
|
36
36
|
gateResults?: unknown[];
|
|
37
37
|
}): RunGraph;
|
|
38
38
|
export declare function chainDepth(g: RunGraph): Map<string, number>;
|
|
39
|
-
export declare function readyTasks(g: RunGraph): Task[];
|
|
40
|
-
export declare function
|
|
39
|
+
export declare function readyTasks(g: RunGraph, prioritized?: ReadonlySet<string>): Task[];
|
|
40
|
+
export declare function batteryPriority(actions: Iterable<{
|
|
41
|
+
taskId: string;
|
|
42
|
+
authority: string;
|
|
43
|
+
}>): Set<string>;
|
|
44
|
+
export declare function dispatchWaves(g: RunGraph, concurrency: number, prioritized?: ReadonlySet<string>): Map<string, number>;
|
|
41
45
|
export declare function isComplete(g: RunGraph): boolean;
|
|
42
46
|
export declare function isStalled(g: RunGraph): boolean;
|
|
43
47
|
export declare function closureReaches(g: RunGraph, taskId: string, pred: (t: Task) => boolean): boolean;
|
package/dist/graph/graph.js
CHANGED
|
@@ -94,9 +94,14 @@ export function graphDefinitionHash(g) {
|
|
|
94
94
|
// finding; changing the goal, write surface or acceptance contract does. Keep the full digest here:
|
|
95
95
|
// unlike graphDefinitionHash this value is persisted beside evidence and is the fail-closed join a
|
|
96
96
|
// later run uses, so there is no benefit in making collision diagnosis less explicit.
|
|
97
|
+
// OBS-1126: outOfScope joins the identity only when present — an absent list keeps the historical
|
|
98
|
+
// bytes so every digest persisted before the field existed still joins.
|
|
97
99
|
export function taskContentDigest(task) {
|
|
98
100
|
return createHash("sha256")
|
|
99
|
-
.update(JSON.stringify({
|
|
101
|
+
.update(JSON.stringify({
|
|
102
|
+
goal: task.goal, files: task.files, acceptance: task.acceptance,
|
|
103
|
+
...(task.outOfScope ? { outOfScope: task.outOfScope } : {}),
|
|
104
|
+
}))
|
|
100
105
|
.digest("hex");
|
|
101
106
|
}
|
|
102
107
|
export function tickmarkrDir(repoRoot) {
|
|
@@ -186,21 +191,32 @@ export function chainDepth(g) {
|
|
|
186
191
|
// OBS-1018: admission is critical-path order — deepest chain root first, ties in declaration order.
|
|
187
192
|
// The daemon's dispatch loop slices this list unchanged. Stated limit: resume-restore of previously
|
|
188
193
|
// in-flight attempts keeps its own precedence (src/run/daemon.ts); only fresh admission is ordered here.
|
|
189
|
-
|
|
194
|
+
// OBS-1158: `prioritized` names tasks holding a pending battery (recheck) approval. Among READY tasks
|
|
195
|
+
// of equal depth an approved recheck precedes fresh work; deeper fresh work keeps its rank, and a
|
|
196
|
+
// recheck that is not ready (dependency-blocked) is not admitted at all, so it gains nothing here.
|
|
197
|
+
export function readyTasks(g, prioritized = NO_PRIORITY) {
|
|
190
198
|
const done = new Set(g.tasks.filter((t) => t.status === "done").map((t) => t.id));
|
|
191
199
|
const depth = chainDepth(g);
|
|
200
|
+
const rank = (t) => (prioritized.has(t.id) ? 1 : 0);
|
|
192
201
|
return g.tasks
|
|
193
202
|
.filter((t) => t.status === "pending" && t.deps.every((d) => done.has(d)))
|
|
194
|
-
.sort((a, b) => depth.get(b.id) - depth.get(a.id)); // Array#sort is stable:
|
|
203
|
+
.sort((a, b) => depth.get(b.id) - depth.get(a.id) || rank(b) - rank(a)); // Array#sort is stable: remaining ties keep declaration order
|
|
204
|
+
}
|
|
205
|
+
const NO_PRIORITY = new Set();
|
|
206
|
+
// OBS-1158: the one seam plan and the daemon share for battery priority — journal approval actions
|
|
207
|
+
// in (src/run/journal.ts pendingApprovalActions), the ids whose pending authority is `battery` out.
|
|
208
|
+
// Waivers, worker-funding approvals and inert releases never qualify; a consumed approval is absent.
|
|
209
|
+
export function batteryPriority(actions) {
|
|
210
|
+
return new Set([...actions].filter((a) => a.authority === "battery").map((a) => a.taskId));
|
|
195
211
|
}
|
|
196
212
|
// OBS-1018: the dispatch wave each pending task would enter at `concurrency` slots if every wave
|
|
197
213
|
// took one tick — computed by draining readyTasks, so plan and daemon can never disagree on order.
|
|
198
214
|
// Tasks already past pending carry no wave.
|
|
199
|
-
export function dispatchWaves(g, concurrency) {
|
|
215
|
+
export function dispatchWaves(g, concurrency, prioritized = NO_PRIORITY) {
|
|
200
216
|
const waves = new Map();
|
|
201
217
|
let sim = g;
|
|
202
218
|
for (let wave = 1;; wave++) {
|
|
203
|
-
const batch = readyTasks(sim).slice(0, Math.max(1, concurrency));
|
|
219
|
+
const batch = readyTasks(sim, prioritized).slice(0, Math.max(1, concurrency));
|
|
204
220
|
if (!batch.length)
|
|
205
221
|
return waves;
|
|
206
222
|
for (const t of batch) {
|
package/dist/graph/schema.d.ts
CHANGED
|
@@ -80,6 +80,7 @@ export declare const TaskSchema: z.ZodObject<{
|
|
|
80
80
|
kind: z.ZodLiteral<"fixture">;
|
|
81
81
|
paths: z.ZodArray<z.ZodString>;
|
|
82
82
|
}, z.core.$strip>], "kind">>>;
|
|
83
|
+
outOfScope: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
83
84
|
gates: z.ZodDefault<z.ZodArray<z.ZodEnum<{
|
|
84
85
|
build: "build";
|
|
85
86
|
test: "test";
|
|
@@ -181,6 +182,7 @@ export declare const RunGraphSchema: z.ZodObject<{
|
|
|
181
182
|
kind: z.ZodLiteral<"fixture">;
|
|
182
183
|
paths: z.ZodArray<z.ZodString>;
|
|
183
184
|
}, z.core.$strip>], "kind">>>;
|
|
185
|
+
outOfScope: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
184
186
|
gates: z.ZodDefault<z.ZodArray<z.ZodEnum<{
|
|
185
187
|
build: "build";
|
|
186
188
|
test: "test";
|
package/dist/graph/schema.js
CHANGED
|
@@ -71,6 +71,8 @@ export const TaskSchema = z.object({
|
|
|
71
71
|
}))
|
|
72
72
|
.optional(),
|
|
73
73
|
pins: z.array(PinSchema).optional(),
|
|
74
|
+
// OBS-1126: declared bounds the task must not cross; absent ⇒ no key (sealed graph bytes hold).
|
|
75
|
+
outOfScope: z.array(z.string().min(1)).optional(),
|
|
74
76
|
gates: z
|
|
75
77
|
.array(z.enum(GATE_NAMES))
|
|
76
78
|
.default(["build", "test", "lint", "evidence", "scope", "acceptance", "review"])
|
package/dist/route/preference.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { channelKey, channelsFromConfig } from "../adapters/types.js";
|
|
2
|
+
import { DENY_SCOPES, denyEntriesAt } from "../config/config.js";
|
|
2
3
|
import { validateGraph } from "../graph/schema.js";
|
|
3
4
|
import { route, RoutingError } from "./router.js";
|
|
4
5
|
const PREFERENCE_ROLES = ["worker", "judge", "review", "consult"];
|
|
@@ -121,47 +122,17 @@ export function entryMatchesChannel(entry, c, allowFamilyMatching = false) {
|
|
|
121
122
|
export function exclusionCollector(c, routingOrCfg, role = "worker") {
|
|
122
123
|
const routing = "routing" in routingOrCfg ? routingOrCfg.routing : routingOrCfg;
|
|
123
124
|
const out = [];
|
|
124
|
-
const { allow
|
|
125
|
-
for (const
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
configPath: "routing.deny.adapters",
|
|
131
|
-
entry,
|
|
132
|
-
by: "deny",
|
|
133
|
-
});
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
for (const entry of deny?.models ?? []) {
|
|
137
|
-
if (entryMatchesChannel(entry, c, true)) {
|
|
138
|
-
out.push({
|
|
139
|
-
scope: "routing.deny.models",
|
|
140
|
-
path: "routing.deny.models",
|
|
141
|
-
configPath: "routing.deny.models",
|
|
142
|
-
entry,
|
|
143
|
-
by: "deny",
|
|
144
|
-
});
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
if (role === "worker") {
|
|
148
|
-
for (const entry of deny?.workers?.adapters ?? []) {
|
|
149
|
-
if (entryMatchesChannel(entry, c, true)) {
|
|
150
|
-
out.push({
|
|
151
|
-
scope: "routing.deny.workers.adapters",
|
|
152
|
-
path: "routing.deny.workers.adapters",
|
|
153
|
-
configPath: "routing.deny.workers.adapters",
|
|
154
|
-
entry,
|
|
155
|
-
by: "deny",
|
|
156
|
-
});
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
for (const entry of deny?.workers?.models ?? []) {
|
|
125
|
+
const { allow } = routing ?? {};
|
|
126
|
+
for (const scope of DENY_SCOPES) {
|
|
127
|
+
// Lists under workers apply only to worker seats; flat deny lists cover every role.
|
|
128
|
+
if (scope.path[2] === "workers" && role !== "worker")
|
|
129
|
+
continue;
|
|
130
|
+
for (const entry of denyEntriesAt(routing, scope) ?? []) {
|
|
160
131
|
if (entryMatchesChannel(entry, c, true)) {
|
|
161
132
|
out.push({
|
|
162
|
-
scope:
|
|
163
|
-
path:
|
|
164
|
-
configPath:
|
|
133
|
+
scope: scope.dotted,
|
|
134
|
+
path: scope.dotted,
|
|
135
|
+
configPath: scope.dotted,
|
|
165
136
|
entry,
|
|
166
137
|
by: "deny",
|
|
167
138
|
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type AuthHealth, type BillingChannel, type WorkerAdapter } from "../adapters/types.js";
|
|
2
|
+
import { type TickmarkrConfig } from "../config/config.js";
|
|
3
|
+
import type { PreferenceRole } from "./preference.js";
|
|
4
|
+
export { preferIndex, rankPreferredChannels, reviewPreferenceTieBreak } from "./router.js";
|
|
5
|
+
export type RolePick = {
|
|
6
|
+
ok: true;
|
|
7
|
+
channel: BillingChannel;
|
|
8
|
+
} | {
|
|
9
|
+
ok: false;
|
|
10
|
+
reason: "missing-prefer" | "no-eligible-preferred-channel";
|
|
11
|
+
};
|
|
12
|
+
/** Strict role selection: no task, author, pin, or default seat is invented. */
|
|
13
|
+
export declare function pickRole(role: PreferenceRole, cfg: TickmarkrConfig, adapters: WorkerAdapter[], health: Record<string, AuthHealth>, opts?: {
|
|
14
|
+
excludeVendors?: ReadonlySet<string>;
|
|
15
|
+
eligible?: (channel: BillingChannel) => boolean;
|
|
16
|
+
}): RolePick;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { discoverChannels } from "../adapters/registry.js";
|
|
2
|
+
import { rankPreferredChannels, reviewPreferenceTieBreak } from "./router.js";
|
|
3
|
+
export { preferIndex, rankPreferredChannels, reviewPreferenceTieBreak } from "./router.js";
|
|
4
|
+
/** Strict role selection: no task, author, pin, or default seat is invented. */
|
|
5
|
+
export function pickRole(role, cfg, adapters, health, opts = {}) {
|
|
6
|
+
// Worker preferences belong to shapes; judge currently has only an explicit pin.
|
|
7
|
+
const prefer = role === "review" ? cfg.review.prefer : role === "consult" ? cfg.consult.prefer : undefined;
|
|
8
|
+
if (!prefer?.length)
|
|
9
|
+
return { ok: false, reason: "missing-prefer" };
|
|
10
|
+
const pool = discoverChannels(cfg, adapters, health, role).filter((c) => !opts.excludeVendors?.has(c.vendor) && (opts.eligible?.(c) ?? true));
|
|
11
|
+
const channel = rankPreferredChannels(pool, prefer, {
|
|
12
|
+
tieBreak: role === "review" ? reviewPreferenceTieBreak : undefined,
|
|
13
|
+
})[0];
|
|
14
|
+
return channel ? { ok: true, channel } : { ok: false, reason: "no-eligible-preferred-channel" };
|
|
15
|
+
}
|
package/dist/route/router.d.ts
CHANGED
|
@@ -31,6 +31,19 @@ export declare class RoutingError extends Error {
|
|
|
31
31
|
constructor(msg: string);
|
|
32
32
|
}
|
|
33
33
|
export declare function marginalCostRank(c: BillingChannel): number;
|
|
34
|
+
type ChannelSeat = {
|
|
35
|
+
adapter: string;
|
|
36
|
+
model: string;
|
|
37
|
+
};
|
|
38
|
+
/** Shared adapter | adapter:model grammar; unmatched channels follow every declared entry. */
|
|
39
|
+
export declare function preferIndex(c: ChannelSeat, prefer?: readonly string[]): number;
|
|
40
|
+
/** Rank an already eligible pool. Review alone retains its legacy unpreferred tail. */
|
|
41
|
+
export declare function rankPreferredChannels<C extends ChannelSeat>(channels: readonly C[], prefer?: readonly string[], opts?: {
|
|
42
|
+
includeUnpreferred?: boolean;
|
|
43
|
+
tieBreak?: (a: C, b: C) => number;
|
|
44
|
+
}): C[];
|
|
45
|
+
/** Review preserves its established tier-then-cost order inside a preference band. */
|
|
46
|
+
export declare const reviewPreferenceTieBreak: (a: BillingChannel, b: BillingChannel) => number;
|
|
34
47
|
export declare function route(task: Task, cfg: TickmarkrConfig, channels: BillingChannel[], profile?: RoutingProfile, preferCtx?: RoutingPreferContext, exclude?: ReadonlySet<string>, exploreCtx?: ExploreContext): Route;
|
|
35
48
|
export declare function nextChannel(current: Assignment, task: Task, cfg: TickmarkrConfig, channels: BillingChannel[], tried: string[], profile?: RoutingProfile, exclude?: ReadonlySet<string>): Assignment | null;
|
|
36
49
|
export type ClimbPick = Assignment & {
|
|
@@ -46,3 +59,4 @@ export type ClimbResult = ClimbPick;
|
|
|
46
59
|
* returns nextChannel's same-or-higher pick flagged climbed: false naming the reason.
|
|
47
60
|
*/
|
|
48
61
|
export declare function climbChannel(current: Assignment, task: Task, cfg: TickmarkrConfig, channels: BillingChannel[], tried: string[], profile?: RoutingProfile, exclude?: ReadonlySet<string>): ClimbPick | null;
|
|
62
|
+
export {};
|
package/dist/route/router.js
CHANGED
|
@@ -52,10 +52,18 @@ function resolvePin(pin, channels) {
|
|
|
52
52
|
}
|
|
53
53
|
return c;
|
|
54
54
|
}
|
|
55
|
-
|
|
55
|
+
/** Shared adapter | adapter:model grammar; unmatched channels follow every declared entry. */
|
|
56
|
+
export function preferIndex(c, prefer = []) {
|
|
56
57
|
const i = prefer.findIndex((p) => p === c.adapter || p === channelKey(c));
|
|
57
58
|
return i === -1 ? prefer.length : i;
|
|
58
59
|
}
|
|
60
|
+
/** Rank an already eligible pool. Review alone retains its legacy unpreferred tail. */
|
|
61
|
+
export function rankPreferredChannels(channels, prefer = [], opts = {}) {
|
|
62
|
+
return channels.filter((c) => opts.includeUnpreferred || preferIndex(c, prefer) < prefer.length)
|
|
63
|
+
.sort((a, b) => preferIndex(a, prefer) - preferIndex(b, prefer) || opts.tieBreak?.(a, b) || 0);
|
|
64
|
+
}
|
|
65
|
+
/** Review preserves its established tier-then-cost order inside a preference band. */
|
|
66
|
+
export const reviewPreferenceTieBreak = (a, b) => TIER_RANK[b.tier] - TIER_RANK[a.tier] || Number(a.channel === "api") - Number(b.channel === "api");
|
|
59
67
|
function ladderFor(task, entry) {
|
|
60
68
|
const escalate = task.routingHints?.escalate ?? entry?.escalate ?? true;
|
|
61
69
|
return escalate ? ["retry", "escalate", "consult", "human"] : ["retry", "consult", "human"];
|
package/dist/run/consult.js
CHANGED
|
@@ -4,6 +4,7 @@ import { getAdapter } from "../adapters/registry.js";
|
|
|
4
4
|
import { bannerShell, paneDispatchCommand } from "../brand.js";
|
|
5
5
|
import { dewrapPaneVerdict, extractVerdictJson, gateExitTrailer, gatePaneName, generateVerdictNonce, verdictNonceLine } from "../gates/llm.js";
|
|
6
6
|
import { classifyVerdictCause } from "../gates/verdict-cause.js";
|
|
7
|
+
import { rankPreferredChannels } from "../route/role-pick.js";
|
|
7
8
|
import { disallowedBy, routingModelProvider } from "../route/preference.js";
|
|
8
9
|
import { sh } from "./git.js";
|
|
9
10
|
import { redactSecrets } from "./redact.js";
|
|
@@ -212,15 +213,10 @@ opts = {}) {
|
|
|
212
213
|
}
|
|
213
214
|
return parsed;
|
|
214
215
|
};
|
|
215
|
-
//
|
|
216
|
-
//
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
// only WHICH seat answers — per-seat parsing and the fail-safe human action below are untouched.
|
|
220
|
-
const live = new Set((opts.channels ?? []).map((c) => c.adapter));
|
|
221
|
-
const seats = (cfg.consult.prefer ?? [])
|
|
222
|
-
.map((entry) => ({ adapter: entry.slice(0, entry.indexOf(":")), model: entry.slice(entry.indexOf(":") + 1) }))
|
|
223
|
-
.filter((s) => live.has(s.adapter));
|
|
216
|
+
// Rank only exact live models from the daemon's role-scoped discovery pool.
|
|
217
|
+
// The explicit pin remains a caller-owned fallback, outside strict preference selection.
|
|
218
|
+
const live = (opts.channels ?? []).filter((c) => typeof c.model === "string");
|
|
219
|
+
const seats = rankPreferredChannels(live.filter((c) => disallowedBy(c, cfg.routing, "consult") === null), cfg.consult.prefer);
|
|
224
220
|
seats.push({ adapter: cfg.consult.adapter, model: cfg.consult.model });
|
|
225
221
|
// v1.87 T2: a consult seat reads the code, so it passes through the operator's policy exactly like
|
|
226
222
|
// a worker does. The filter runs AFTER the pin is pushed, so the final pinned seat is checked by
|
package/dist/run/daemon.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ import { type ExecutorDriver, type Slot } from "../drivers/types.js";
|
|
|
5
5
|
import { type Baseline } from "../gates/baseline.js";
|
|
6
6
|
import type { GateResult } from "../gates/types.js";
|
|
7
7
|
import { type RunGraph } from "../graph/schema.js";
|
|
8
|
+
import { type PreserveProducer } from "./git.js";
|
|
8
9
|
import { Journal, type JournalEvent } from "./journal.js";
|
|
9
10
|
export declare function closeLiveSlot(liveSlots: Set<Slot>, driver: Pick<ExecutorDriver, "close">, slot: Slot): Promise<void>;
|
|
10
11
|
/** Optional authoritative transport receipt. Screen text can prove execution, never nonacceptance. */
|
|
@@ -67,6 +68,7 @@ export interface RunSummary {
|
|
|
67
68
|
/** the accepted approvals that never reached a dispatch — named, never left to the park buckets */
|
|
68
69
|
outstandingApprovals?: string[];
|
|
69
70
|
}
|
|
71
|
+
export declare function pendingDaemonApprovalActions(events: JournalEvent[]): Map<string, import("./journal.js").PendingApprovalAction>;
|
|
70
72
|
/**
|
|
71
73
|
* T14, amended by v2.2 T3: approvals the run accepted and never acted on. `approved` above is still
|
|
72
74
|
* built ONCE at startup — replay determinism depends on it — but a live approval is no longer inert:
|
|
@@ -126,6 +128,9 @@ export declare const APPROVAL_POLL_MS = 250;
|
|
|
126
128
|
export declare const APPROVAL_WINDOW_MS = 120000;
|
|
127
129
|
export declare const setApprovalWindowForTests: (ms: number) => void;
|
|
128
130
|
export declare const resetApprovalWindowForTests: () => void;
|
|
131
|
+
/** Test seam — shrink the capacity backoff without minute-long sleeps. */
|
|
132
|
+
export declare function setCapacityBackoffMsForTests(ms: number): void;
|
|
133
|
+
export declare function resetCapacityBackoffMsForTests(): void;
|
|
129
134
|
export declare const EARLY_LAUNCH_LIVENESS_MS = 60000;
|
|
130
135
|
/** Test seam — lowers the empty-pane liveness window without sleeping 60s per case. */
|
|
131
136
|
export declare function setEarlyLaunchLivenessMsForTests(ms: number): void;
|
|
@@ -170,6 +175,11 @@ export declare const HARVESTED_RESULT_SUMMARY = "harvested: the worktree carries
|
|
|
170
175
|
export declare function commandsHash(commands: Record<string, string>): string;
|
|
171
176
|
export interface TipProof {
|
|
172
177
|
kind: "fresh" | "reused" | "failed" | "incomplete";
|
|
178
|
+
/** Each completed gate keeps its own execution provenance, even in a mixed cycle. */
|
|
179
|
+
gates?: Array<{
|
|
180
|
+
gate: string;
|
|
181
|
+
kind: "fresh" | "reused";
|
|
182
|
+
}>;
|
|
173
183
|
/** the commit the cycle's start row spoke for */
|
|
174
184
|
tip?: string;
|
|
175
185
|
}
|
|
@@ -177,7 +187,8 @@ export interface TipProof {
|
|
|
177
187
|
* OBS-1077 close rider: what the engagement's LATEST verification cycle proved — its
|
|
178
188
|
* `tip-verify-start` row and what followed, never a commit comparison. Exactly one kind per close:
|
|
179
189
|
* fresh (every tip command ran AND passed), reused (an eligible cached cycle carried forward),
|
|
180
|
-
* failed, or incomplete (cancelled, cut short
|
|
190
|
+
* failed, or incomplete (cancelled, cut short or undelimited). Completed mixed cycles retain each
|
|
191
|
+
* gate's kind beside the whole-cycle reused kind. Nothing before the start row
|
|
181
192
|
* is read, so an unfinished cycle inherits nothing from an earlier green one.
|
|
182
193
|
*/
|
|
183
194
|
export declare function runEndTipProof(events: readonly JournalEvent[]): TipProof;
|
|
@@ -212,5 +223,9 @@ export declare function liveSuiteCount(repoRoot: string): Promise<number>;
|
|
|
212
223
|
/** Test seam — exercise the production observer's total read bound with a small real tree. */
|
|
213
224
|
export declare function setObserveBudgetBytesForTests(bytes: number): void;
|
|
214
225
|
export declare function resetObserveBudgetBytesForTests(): void;
|
|
226
|
+
/** The attempt whose worker last launched into the task's current checkout; "unknown" once a
|
|
227
|
+
* recreation replaced that tree without a new launch (recheck, gate-only restore) or when no
|
|
228
|
+
* dispatch assignment is on record. Never the newest dispatch by itself. */
|
|
229
|
+
export declare function knownProducer(events: readonly JournalEvent[], taskId: string): PreserveProducer;
|
|
215
230
|
export declare function recordFatalRunEnd(journal: Journal, runId: string, branch: string, err: unknown, graph?: RunGraph, phase?: string): TipProof | undefined;
|
|
216
231
|
export declare function runDaemon(repoRoot: string, opts?: RunOptions): Promise<RunSummary>;
|