tickmarkr 2.5.5 → 2.5.7
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 +3 -1
- package/dist/adapters/prompt.js +21 -1
- package/dist/cli/commands/approve.js +69 -8
- package/dist/cli/commands/doctor.d.ts +10 -0
- package/dist/cli/commands/fleet.js +4 -0
- package/dist/cli/commands/plan.js +20 -4
- package/dist/cli/commands/status.js +95 -34
- package/dist/cli/commands/verify.js +108 -85
- package/dist/config/config.d.ts +11 -0
- package/dist/config/config.js +21 -12
- package/dist/config/fleet-overlay.js +55 -17
- package/dist/drivers/index.js +2 -1
- package/dist/drivers/orca.d.ts +21 -1
- package/dist/drivers/orca.js +209 -27
- package/dist/gates/baseline.d.ts +21 -5
- package/dist/gates/baseline.js +67 -17
- package/dist/gates/cache.d.ts +14 -13
- package/dist/gates/cache.js +17 -5
- package/dist/gates/llm.d.ts +3 -0
- package/dist/gates/llm.js +11 -0
- package/dist/gates/review.d.ts +28 -3
- package/dist/gates/review.js +118 -15
- package/dist/gates/run-gates.d.ts +10 -2
- package/dist/gates/run-gates.js +362 -108
- package/dist/gates/test-manifest.d.ts +33 -1
- package/dist/gates/test-manifest.js +132 -40
- package/dist/gates/test-reporter.js +20 -7
- package/dist/graph/graph.d.ts +4 -0
- package/dist/graph/graph.js +50 -1
- package/dist/run/activity.d.ts +28 -0
- package/dist/run/activity.js +194 -0
- package/dist/run/consult.js +5 -4
- package/dist/run/daemon.d.ts +11 -0
- package/dist/run/daemon.js +663 -128
- package/dist/run/execution-budget.d.ts +25 -0
- package/dist/run/execution-budget.js +142 -0
- package/dist/run/git.d.ts +46 -1
- package/dist/run/git.js +149 -12
- package/dist/run/journal.d.ts +23 -5
- package/dist/run/journal.js +98 -25
- package/dist/run/lease.d.ts +44 -0
- package/dist/run/lease.js +226 -3
- package/dist/run/operator-page-summary.d.ts +56 -0
- package/dist/run/operator-page-summary.js +68 -0
- package/dist/run/operator-summary.d.ts +69 -0
- package/dist/run/operator-summary.js +77 -0
- package/dist/run/protocol.d.ts +71 -0
- package/dist/run/protocol.js +32 -0
- package/dist/run/recovery.d.ts +8 -0
- package/dist/run/recovery.js +25 -0
- package/dist/run/repair-selection.d.ts +12 -0
- package/dist/run/repair-selection.js +56 -0
- package/dist/run/stall.d.ts +6 -1
- package/dist/run/stall.js +60 -3
- package/dist/tui/cockpit/board.d.ts +9 -0
- package/dist/tui/cockpit/board.js +10 -0
- package/dist/tui/cockpit/derive.d.ts +35 -0
- package/dist/tui/cockpit/derive.js +152 -10
- package/dist/tui/cockpit/evidence-view.d.ts +2 -0
- package/dist/tui/cockpit/evidence-view.js +42 -12
- package/dist/tui/cockpit/run-cockpit.d.ts +8 -1
- package/dist/tui/cockpit/run-cockpit.js +95 -1
- package/dist/tui/cockpit/run-view.d.ts +37 -0
- package/dist/tui/cockpit/run-view.js +189 -2
- package/dist/tui/ink/fleet-app.d.ts +10 -2
- package/dist/tui/ink/fleet-app.js +33 -15
- package/package.json +2 -2
- package/skills/tickmarkr-overseer/SKILL.md +55 -3
- package/skills/tickmarkr-overseer/scripts/grade-ci.sh +29 -2
package/dist/run/activity.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { formatJournalNarration } from "./journal.js";
|
|
2
|
+
import { readCommandReceipt } from "./protocol.js";
|
|
2
3
|
const channelOf = (assignment) => {
|
|
3
4
|
const a = assignment;
|
|
4
5
|
return typeof a?.adapter === "string" && typeof a.model === "string" ? `${a.adapter}:${a.model}` : "unknown channel";
|
|
@@ -94,3 +95,196 @@ export function foldActivity(events, tasks) {
|
|
|
94
95
|
const last = events.at(-1);
|
|
95
96
|
return { ...(last ? { now: formatJournalNarration(last) } : {}), cells };
|
|
96
97
|
}
|
|
98
|
+
const activityRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
99
|
+
const activityOrdinal = (value) => typeof value === "number" && Number.isInteger(value) && value >= 0 ? value : undefined;
|
|
100
|
+
/**
|
|
101
|
+
* Pure, evidence-only successor to foldActivity. Feed Journal.readTracked() and the owning run ID;
|
|
102
|
+
* sourceIndex supplies journal order, never wall time. Unattributed legacy rows belong to their
|
|
103
|
+
* tracked run and current attempt/round. They cannot prove identities absent from the journal.
|
|
104
|
+
* A new gates phase opens a round; completed gates cannot reopen within that round. No declared
|
|
105
|
+
* gate order, graph status, or successful verdict predicts a later phase.
|
|
106
|
+
*/
|
|
107
|
+
export function projectActivity(runId, rows, tasks) {
|
|
108
|
+
const states = new Map(tasks.map((task) => [task.id, {
|
|
109
|
+
projection: { taskId: task.id, state: "unconfirmed", phases: [], build: { state: "start-unrecorded" } },
|
|
110
|
+
round: 0,
|
|
111
|
+
closed: new Set(),
|
|
112
|
+
invocations: new Set(),
|
|
113
|
+
workerReturned: false,
|
|
114
|
+
suspended: false,
|
|
115
|
+
redispatch: false,
|
|
116
|
+
dispatchedSinceRound: false,
|
|
117
|
+
adoptRoundAttempt: false,
|
|
118
|
+
}]));
|
|
119
|
+
for (const row of [...rows].sort((a, b) => a.sourceIndex - b.sourceIndex)) {
|
|
120
|
+
if (row.runId !== runId || row.kind === "protocol-issue")
|
|
121
|
+
continue;
|
|
122
|
+
// Compatibility readers normalize missing legacy attempts to zero. Retain the physical
|
|
123
|
+
// payload here so that absence still means the currently recorded attempt.
|
|
124
|
+
const raw = row.raw;
|
|
125
|
+
if (!activityRecord(raw) || typeof raw.event !== "string" || !activityRecord(raw.data))
|
|
126
|
+
continue;
|
|
127
|
+
const data = raw.data;
|
|
128
|
+
if (typeof data.runId === "string" && data.runId !== runId)
|
|
129
|
+
continue;
|
|
130
|
+
if (raw.event === "run-resume" || raw.event === "run-end") {
|
|
131
|
+
for (const st of states.values()) {
|
|
132
|
+
const p = st.projection;
|
|
133
|
+
if (p.build.state === "started" && "receipt" in p.build) {
|
|
134
|
+
p.build = { state: "unresolved", receipt: p.build.receipt };
|
|
135
|
+
}
|
|
136
|
+
for (const phase of p.phases)
|
|
137
|
+
st.closed.add(phase.gate);
|
|
138
|
+
p.phases = [];
|
|
139
|
+
if (p.state !== "terminal")
|
|
140
|
+
p.state = "unconfirmed";
|
|
141
|
+
st.suspended = true;
|
|
142
|
+
st.redispatch = raw.event === "run-resume";
|
|
143
|
+
st.dispatchedSinceRound = false;
|
|
144
|
+
st.adoptRoundAttempt = false;
|
|
145
|
+
}
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
if (typeof raw.taskId !== "string")
|
|
149
|
+
continue;
|
|
150
|
+
const st = states.get(raw.taskId);
|
|
151
|
+
if (!st)
|
|
152
|
+
continue;
|
|
153
|
+
const p = st.projection;
|
|
154
|
+
const attempt = activityOrdinal(data.attempt);
|
|
155
|
+
if (attempt !== undefined && p.attempt !== undefined && attempt < p.attempt)
|
|
156
|
+
continue;
|
|
157
|
+
if (raw.event === "task-dispatch") {
|
|
158
|
+
const nextAttempt = attempt ?? 0;
|
|
159
|
+
if (p.attempt === nextAttempt && !st.redispatch)
|
|
160
|
+
continue;
|
|
161
|
+
p.attempt = nextAttempt;
|
|
162
|
+
p.state = "preparing";
|
|
163
|
+
p.phases = [];
|
|
164
|
+
p.build = { state: "start-unrecorded" };
|
|
165
|
+
st.closed.clear();
|
|
166
|
+
st.workerReturned = false;
|
|
167
|
+
st.suspended = false;
|
|
168
|
+
st.redispatch = false;
|
|
169
|
+
st.dispatchedSinceRound = true;
|
|
170
|
+
st.adoptRoundAttempt = false;
|
|
171
|
+
continue;
|
|
172
|
+
}
|
|
173
|
+
const round = activityOrdinal(data.gateRound);
|
|
174
|
+
if (round !== undefined && round !== st.round)
|
|
175
|
+
continue;
|
|
176
|
+
const roundGate = (raw.event === "gate-result" || raw.event === "gate-phase-start"
|
|
177
|
+
|| raw.event === "phase-start") && typeof data.gate === "string"
|
|
178
|
+
&& !st.suspended && !st.closed.has(data.gate);
|
|
179
|
+
if (attempt !== undefined && p.attempt !== undefined && attempt !== p.attempt
|
|
180
|
+
&& !(st.adoptRoundAttempt && roundGate))
|
|
181
|
+
continue;
|
|
182
|
+
if (st.adoptRoundAttempt && roundGate && attempt !== undefined) {
|
|
183
|
+
p.attempt = attempt;
|
|
184
|
+
st.adoptRoundAttempt = false;
|
|
185
|
+
}
|
|
186
|
+
if (p.attempt === undefined && attempt !== undefined)
|
|
187
|
+
p.attempt = attempt;
|
|
188
|
+
if (["task-done", "task-failed", "task-human", "task-approved", "merge"].includes(raw.event)) {
|
|
189
|
+
p.state = "terminal";
|
|
190
|
+
p.phases = [];
|
|
191
|
+
st.suspended = true;
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
194
|
+
// Only an explicit new battery may re-enter verification after a resume or completed task.
|
|
195
|
+
if (raw.event === "phase-start" && data.phase === "gates") {
|
|
196
|
+
st.round++;
|
|
197
|
+
// Resume verification has no dispatch and labels the round with the dispatch count,
|
|
198
|
+
// rather than the last worker's attempt. Its first attributed gate/receipt owns the label.
|
|
199
|
+
st.adoptRoundAttempt = !st.dispatchedSinceRound;
|
|
200
|
+
st.dispatchedSinceRound = false;
|
|
201
|
+
st.closed.clear();
|
|
202
|
+
p.phases = [];
|
|
203
|
+
p.build = { state: "start-unrecorded" };
|
|
204
|
+
if (p.state === "terminal")
|
|
205
|
+
p.state = "unconfirmed";
|
|
206
|
+
st.suspended = false;
|
|
207
|
+
continue;
|
|
208
|
+
}
|
|
209
|
+
if (raw.event === "build-receipt" || raw.event === "build-result") {
|
|
210
|
+
// The emitter adds gate/reason metadata beside the strict command receipt.
|
|
211
|
+
const { gate: _gate, reason: _reason, freshBuildRan: _fresh, ...payload } = data;
|
|
212
|
+
const parsed = readCommandReceipt(payload);
|
|
213
|
+
if (parsed.kind !== "receipt")
|
|
214
|
+
continue;
|
|
215
|
+
const receipt = parsed.receipt;
|
|
216
|
+
if (receipt.outcome === "started" && !receipt.confirmedStart)
|
|
217
|
+
continue;
|
|
218
|
+
const identity = receipt.attribution;
|
|
219
|
+
if (identity.runId !== runId || identity.taskId !== p.taskId
|
|
220
|
+
|| identity.gateRound !== st.round)
|
|
221
|
+
continue;
|
|
222
|
+
if (identity.attempt < (p.attempt ?? 0)
|
|
223
|
+
|| (!st.adoptRoundAttempt && identity.attempt !== (p.attempt ?? 0)))
|
|
224
|
+
continue;
|
|
225
|
+
const previous = "receipt" in p.build ? p.build.receipt : undefined;
|
|
226
|
+
const same = previous?.attribution.invocation === identity.invocation;
|
|
227
|
+
if (same) {
|
|
228
|
+
if (p.build.state !== "started" && p.build.state !== "unresolved")
|
|
229
|
+
continue;
|
|
230
|
+
if (receipt.outcome === "started")
|
|
231
|
+
continue;
|
|
232
|
+
}
|
|
233
|
+
else {
|
|
234
|
+
if (st.suspended || st.closed.has("build") || st.invocations.has(identity.invocation))
|
|
235
|
+
continue;
|
|
236
|
+
// A late terminal cannot displace a newer invocation. No-start outcomes are themselves
|
|
237
|
+
// complete receipts and need no preceding start; a first terminal remains useful evidence.
|
|
238
|
+
if (previous && receipt.confirmedStart && receipt.outcome !== "started")
|
|
239
|
+
continue;
|
|
240
|
+
}
|
|
241
|
+
st.invocations.add(identity.invocation);
|
|
242
|
+
if (st.adoptRoundAttempt) {
|
|
243
|
+
p.attempt = identity.attempt;
|
|
244
|
+
st.adoptRoundAttempt = false;
|
|
245
|
+
}
|
|
246
|
+
p.build = { state: receipt.outcome, receipt };
|
|
247
|
+
continue;
|
|
248
|
+
}
|
|
249
|
+
if (st.suspended)
|
|
250
|
+
continue;
|
|
251
|
+
if (raw.event === "worker-launch" && !st.workerReturned && p.phases.length === 0
|
|
252
|
+
&& (p.state === "preparing" || p.state === "unconfirmed"))
|
|
253
|
+
p.state = "implementing";
|
|
254
|
+
if (raw.event === "worker-result" && !st.workerReturned && p.phases.length === 0) {
|
|
255
|
+
st.workerReturned = true;
|
|
256
|
+
p.state = "returned-for-verification";
|
|
257
|
+
}
|
|
258
|
+
if (raw.event === "phase-start" && data.phase === "merge") {
|
|
259
|
+
for (const phase of p.phases)
|
|
260
|
+
st.closed.add(phase.gate);
|
|
261
|
+
p.phases = [];
|
|
262
|
+
p.state = "merging";
|
|
263
|
+
st.workerReturned = true;
|
|
264
|
+
continue;
|
|
265
|
+
}
|
|
266
|
+
if (raw.event === "gate-result" && typeof data.gate === "string") {
|
|
267
|
+
st.workerReturned = true;
|
|
268
|
+
// Infrastructure skips have no verdict; a retry may start again in this round.
|
|
269
|
+
if (typeof data.pass === "boolean" || (data.skipped !== true && data.infra !== true))
|
|
270
|
+
st.closed.add(data.gate);
|
|
271
|
+
if (data.gate === "build" && p.build.state === "awaiting-command")
|
|
272
|
+
p.build = { state: "start-unrecorded" };
|
|
273
|
+
p.phases = p.phases.filter((phase) => phase.gate !== data.gate);
|
|
274
|
+
if (p.state !== "merging")
|
|
275
|
+
p.state = p.phases[0]?.state ?? "unconfirmed";
|
|
276
|
+
}
|
|
277
|
+
if ((raw.event === "phase-start" || raw.event === "gate-phase-start")
|
|
278
|
+
&& typeof data.gate === "string" && !st.closed.has(data.gate) && p.state !== "merging") {
|
|
279
|
+
const gate = data.gate;
|
|
280
|
+
if (!p.phases.some((phase) => phase.gate === gate)) {
|
|
281
|
+
p.phases.push({ gate, state: gate === "review" ? "reviewing" : "validating" });
|
|
282
|
+
}
|
|
283
|
+
st.workerReturned = true;
|
|
284
|
+
p.state = p.phases[0].state;
|
|
285
|
+
if (gate === "build" && p.build.state === "start-unrecorded")
|
|
286
|
+
p.build = { state: "awaiting-command" };
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
return new Map([...states].map(([id, st]) => [id, st.projection]));
|
|
290
|
+
}
|
package/dist/run/consult.js
CHANGED
|
@@ -164,10 +164,11 @@ opts = {}) {
|
|
|
164
164
|
out = r.stdout + r.stderr;
|
|
165
165
|
}
|
|
166
166
|
else {
|
|
167
|
-
//
|
|
168
|
-
//
|
|
169
|
-
|
|
170
|
-
|
|
167
|
+
// OBS-1009: the pane runs the HEADLESS command. A seeded interactive session answers on screen
|
|
168
|
+
// but never exits, so the exit trailer after it never runs, the wait runs to the stall cap and
|
|
169
|
+
// the 300-line read is a TUI frame. The headless command exits, the trailer prints, and the
|
|
170
|
+
// pane is harvested through dewrap exactly like a judge or review seat.
|
|
171
|
+
const command = adapter.headlessCommand(promptFile, seatModel);
|
|
171
172
|
// T8: role-first pane name for fleet visibility (consult · T2); consultSeq stays on the dossier artifact only
|
|
172
173
|
const slot = await driver.slot(cwd, gatePaneName("consult", d.taskId), {
|
|
173
174
|
label: `CONSULT ${d.taskId}`,
|
package/dist/run/daemon.d.ts
CHANGED
|
@@ -6,6 +6,17 @@ import { type Baseline } from "../gates/baseline.js";
|
|
|
6
6
|
import type { GateResult } from "../gates/types.js";
|
|
7
7
|
import { Journal, type JournalEvent } from "./journal.js";
|
|
8
8
|
export declare function closeLiveSlot(liveSlots: Set<Slot>, driver: Pick<ExecutorDriver, "close">, slot: Slot): Promise<void>;
|
|
9
|
+
/** Optional authoritative transport receipt. Screen text can prove execution, never nonacceptance. */
|
|
10
|
+
export interface DispatchObservation {
|
|
11
|
+
slotId: string;
|
|
12
|
+
command: string;
|
|
13
|
+
dispatchId: string;
|
|
14
|
+
outcome: "accepted" | "not-accepted";
|
|
15
|
+
authoritative: true;
|
|
16
|
+
}
|
|
17
|
+
export declare function heldWorkerTransport(driver: ExecutorDriver, slot: Slot, dispatchId: string, held: (data: Record<string, unknown>) => void, sleep?: (ms: number) => Promise<void>): Pick<ExecutorDriver, "run" | "read" | "status" | "waitOutput" | "waitAgentStatus">;
|
|
18
|
+
export declare function setAttemptHardTimeoutMsForTests(ms: number): void;
|
|
19
|
+
export declare function resetAttemptHardTimeoutMsForTests(): void;
|
|
9
20
|
export { harvestCpuFlatWindowMs, resetHarvestCpuFlatMsForTests, setHarvestCpuFlatMsForTests, workerTreeCpuMs, } from "./stall.js";
|
|
10
21
|
export interface RunOptions {
|
|
11
22
|
runId?: string;
|