tickmarkr 2.6.1 → 2.6.3
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 +16 -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 +11 -4
- package/dist/cli/commands/approve.js +82 -27
- package/dist/cli/commands/compile.js +13 -3
- package/dist/cli/commands/doctor.d.ts +8 -2
- package/dist/cli/commands/doctor.js +11 -3
- package/dist/cli/commands/fleet.js +87 -11
- package/dist/cli/commands/plan.js +13 -8
- package/dist/cli/commands/report.d.ts +2 -1
- package/dist/cli/commands/report.js +74 -8
- package/dist/cli/commands/resume.js +4 -2
- package/dist/cli/commands/status.js +43 -20
- package/dist/cli/help.d.ts +2 -0
- package/dist/cli/help.js +9 -2
- package/dist/compile/native.js +7 -0
- package/dist/config/config.d.ts +35 -2
- package/dist/config/config.js +86 -10
- package/dist/config/fleet-overlay.d.ts +13 -2
- package/dist/config/fleet-overlay.js +60 -0
- package/dist/drivers/herdr.d.ts +12 -0
- package/dist/drivers/herdr.js +51 -0
- package/dist/drivers/orca.d.ts +35 -2
- package/dist/drivers/orca.js +222 -67
- package/dist/drivers/types.d.ts +2 -0
- package/dist/drivers/types.js +2 -2
- 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 +9 -1
- package/dist/gates/acceptance.js +31 -4
- package/dist/gates/baseline.d.ts +32 -2
- package/dist/gates/baseline.js +111 -24
- package/dist/gates/cache.d.ts +8 -0
- package/dist/gates/cache.js +12 -2
- package/dist/gates/llm.d.ts +11 -4
- package/dist/gates/llm.js +40 -21
- package/dist/gates/review.d.ts +14 -1
- package/dist/gates/review.js +160 -34
- package/dist/gates/run-gates.d.ts +56 -4
- package/dist/gates/run-gates.js +358 -58
- package/dist/gates/test-manifest.d.ts +45 -1
- package/dist/gates/test-manifest.js +78 -12
- 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.d.ts +12 -1
- package/dist/route/router.js +56 -24
- package/dist/run/consult.d.ts +15 -1
- package/dist/run/consult.js +18 -7
- package/dist/run/daemon.d.ts +38 -2
- package/dist/run/daemon.js +895 -192
- package/dist/run/git.d.ts +8 -0
- package/dist/run/git.js +14 -0
- package/dist/run/interactive-seed.d.ts +4 -0
- package/dist/run/interactive-seed.js +35 -9
- package/dist/run/journal.d.ts +152 -3
- package/dist/run/journal.js +551 -50
- package/dist/run/lease.d.ts +13 -0
- package/dist/run/lease.js +45 -0
- 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 +46 -1
- package/dist/run/protocol.js +14 -2
- package/dist/run/receipt-resolver.d.ts +22 -0
- package/dist/run/receipt-resolver.js +40 -1
- package/dist/run/repair-selection.d.ts +11 -1
- package/dist/run/repair-selection.js +17 -9
- package/dist/run/supervision.d.ts +7 -1
- package/dist/run/supervision.js +5 -2
- package/dist/run/wall-budget.d.ts +48 -0
- package/dist/run/wall-budget.js +280 -0
- 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 +27 -2
- package/dist/tui/cockpit/run-view.d.ts +9 -2
- package/dist/tui/cockpit/run-view.js +66 -9
- package/dist/tui/cockpit/setup-cockpit.d.ts +6 -0
- package/dist/tui/cockpit/setup-cockpit.js +10 -3
- package/dist/tui/ink/fleet-app.d.ts +15 -3
- package/dist/tui/ink/fleet-app.js +91 -22
- package/package.json +3 -1
- package/schema/config.schema.json +825 -0
- package/skills/tickmarkr-loop/SKILL.md +15 -3
- package/skills/tickmarkr-overseer/SKILL.md +42 -0
- package/skills/tickmarkr-overseer/scripts/classify-vitest-log.sh +91 -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
|
@@ -5,7 +5,7 @@ import { GLYPHS, PLAIN_COMPACT_LOCKUP, } from "../../brand.js";
|
|
|
5
5
|
import { allocateBandColumns, BandLines, BodyText, CockpitGrid, composeBandLine, JournalRowPanel, keyRosterLines, Panel, PANEL_CHROME_ROWS, ProgressMeter, Sparkline, StatTile, StatusStrip, } from "./components.js";
|
|
6
6
|
import { authorsNote } from "../../run/operator-state.js";
|
|
7
7
|
import { fieldReading } from "./derive.js";
|
|
8
|
-
import { STALL_MARKER } from "./run-view.js";
|
|
8
|
+
import { MISSING_EVIDENCE, STALL_MARKER } from "./run-view.js";
|
|
9
9
|
import { initialRunInteractionState, projectRunKeyEntries, reconcileRunInteraction, RUN_SIDE_RAIL_COLUMN_FLOOR, runPanelFocusOrder, runSideRailVisible, selectableRunViewRowIds, } from "./keys.js";
|
|
10
10
|
import { DECISIONS_TAB_TITLE, RUN_VIEWS, runViewById, setupSectionAt, SETUP_SECTIONS, } from "./views.js";
|
|
11
11
|
import { FRAME_HEADER_GAP, FRAME_NESTED_ROW_BANDS, FULL_JOURNAL_ROWS, planFrame, RUN_PANEL_HORIZONTAL_CHROME, SIDEBAR_COLUMN_FLOOR, } from "./layout.js";
|
|
@@ -309,8 +309,13 @@ function promotedViewRows(data, viewId) {
|
|
|
309
309
|
* task row — identity, phase, build evidence, blocker, next action — each unrecorded field saying so,
|
|
310
310
|
* each recorded one citing the journal line it was read from, and the OBS-1048 stall marker when the
|
|
311
311
|
* newest attempt's harvest is suspect. No attempt label is printed here: the ruler is the row's own.
|
|
312
|
+
* A task with no recorded dispatch folds identity, phase, and build into one missing-evidence clause
|
|
313
|
+
* (OBS-1104). A recorded blocker or next action — a pre-dispatch park, a dependency wait — stays,
|
|
314
|
+
* cited to the journal row that produced it.
|
|
312
315
|
*/
|
|
313
316
|
export function taskProjectionText(row, journal = []) {
|
|
317
|
+
if ((row.attempts ?? 0) === 0)
|
|
318
|
+
return undispatchedProjectionText(row, journal);
|
|
314
319
|
const identity = row.identities?.at(-1);
|
|
315
320
|
const blocker = row.summary?.blocker;
|
|
316
321
|
const harvest = row.harvests?.at(-1);
|
|
@@ -329,7 +334,27 @@ export function taskProjectionText(row, journal = []) {
|
|
|
329
334
|
`blocker ${blocker ? `${blocker.kind} ${at(evidence.blocker)}` : "none"}`,
|
|
330
335
|
`next ${blocker?.nextAction == null ? fieldReading(undefined) : `${blocker.nextAction} ${at(evidence.nextAction)}`}`,
|
|
331
336
|
...(harvest?.suspectedStalledHarvest ? [`${STALL_MARKER} · launch ${at(evidence.launch)} · ${harvest.nudgeFailures} nudge failed ${at(evidence.nudge)} · ${harvest.pageCount} paged ${at(evidence.page)} · no worker-result`] : []),
|
|
332
|
-
].
|
|
337
|
+
].join(" · ");
|
|
338
|
+
}
|
|
339
|
+
/**
|
|
340
|
+
* Identity, phase, and build are unrecorded until a dispatch. Append the blocker and next action
|
|
341
|
+
* only when the summary recorded one — an unknown placeholder with no journal row is the missing clause.
|
|
342
|
+
*/
|
|
343
|
+
function undispatchedProjectionText(row, journal) {
|
|
344
|
+
const blocker = row.summary?.blocker;
|
|
345
|
+
if (blocker == null)
|
|
346
|
+
return MISSING_EVIDENCE;
|
|
347
|
+
const evidence = projectionEvidence(row, journal);
|
|
348
|
+
const at = (line) => line === undefined ? "(no journal row)" : `#L${line}`;
|
|
349
|
+
const showBlocker = blocker.kind !== "unknown" || evidence.blocker !== undefined;
|
|
350
|
+
const showNext = blocker.nextAction != null;
|
|
351
|
+
if (!showBlocker && !showNext)
|
|
352
|
+
return MISSING_EVIDENCE;
|
|
353
|
+
return [
|
|
354
|
+
MISSING_EVIDENCE,
|
|
355
|
+
...(showBlocker ? [`blocker ${blocker.kind} ${at(evidence.blocker)}`] : []),
|
|
356
|
+
...(showNext ? [`next ${blocker.nextAction} ${at(evidence.nextAction)}`] : []),
|
|
357
|
+
].join(" · ");
|
|
333
358
|
}
|
|
334
359
|
/**
|
|
335
360
|
* Correlate each already-derived field with the row that can have produced that value. This is
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type ReactElement } from "react";
|
|
2
2
|
import type { ApprovalRunOwner } from "../../cli/commands/approve.js";
|
|
3
3
|
import { type GateName, type RunGraph } from "../../graph/schema.js";
|
|
4
|
-
import type
|
|
4
|
+
import { type JournalEvent } from "../../run/journal.js";
|
|
5
5
|
import { type GateOutcome } from "../../run/outcome.js";
|
|
6
6
|
import type { OperatorGateState, OperatorSnapshot, OperatorTask } from "../../run/operator-state.js";
|
|
7
7
|
import { type DecisionCommand, type DecisionPreview, type DecisionSession, type RunDecision } from "./decision-actions.js";
|
|
@@ -95,6 +95,8 @@ export interface TaskProjection {
|
|
|
95
95
|
readonly nextAction: ProjectionField;
|
|
96
96
|
/** OBS-1048: present only when the newest launched attempt has failed nudges, pages and no worker-result. */
|
|
97
97
|
readonly stalled?: ProjectionField;
|
|
98
|
+
/** OBS-1104: a task with no recorded dispatch collapses its line to one missing-evidence clause. */
|
|
99
|
+
readonly neverDispatched?: true;
|
|
98
100
|
}
|
|
99
101
|
export declare const STALL_MARKER = "\u26A0 stalled harvest suspected";
|
|
100
102
|
/**
|
|
@@ -103,7 +105,12 @@ export declare const STALL_MARKER = "\u26A0 stalled harvest suspected";
|
|
|
103
105
|
* Nothing is inferred from a title, a provider or a task id: an unrecorded field says so.
|
|
104
106
|
*/
|
|
105
107
|
export declare function projectRunTasks(snapshot: OperatorSnapshot, rows: readonly RunEvidenceRow[], graph: RunGraph | undefined, decisions: readonly RunDecision[], runId: string): readonly TaskProjection[];
|
|
106
|
-
/**
|
|
108
|
+
/**
|
|
109
|
+
* OBS-1104: a never-dispatched task has no identity, phase, or build row. Those fold into one
|
|
110
|
+
* missing-evidence clause. A substantive or journal-cited blocker and next action stay — a
|
|
111
|
+
* pre-dispatch park and a dependency wait are recorded facts, and dropping them hides the park token.
|
|
112
|
+
*/
|
|
113
|
+
export declare const MISSING_EVIDENCE = "missing evidence";
|
|
107
114
|
export declare const projectionLine: (p: TaskProjection) => string;
|
|
108
115
|
export interface PaneLocator {
|
|
109
116
|
readonly status: "recorded" | "unavailable";
|
|
@@ -5,6 +5,7 @@ import { GLYPHS } from "../../brand.js";
|
|
|
5
5
|
import { formatOwnedName, parseOwnedName } from "../../drivers/types.js";
|
|
6
6
|
import { GATE_NAMES } from "../../graph/schema.js";
|
|
7
7
|
import { projectActivity } from "../../run/activity.js";
|
|
8
|
+
import { effectiveDecisions, withPhysicalLine } from "../../run/journal.js";
|
|
8
9
|
import { projectOperatorSummary } from "../../run/operator-summary.js";
|
|
9
10
|
import { trackJournalRows } from "../../run/protocol.js";
|
|
10
11
|
import { normalizeGateOutcome } from "../../run/outcome.js";
|
|
@@ -122,7 +123,7 @@ export function runGateCells(task, evidence, rows = []) {
|
|
|
122
123
|
const e = later.event;
|
|
123
124
|
if (e.event === "gate-reused" && e.data.gate === gate)
|
|
124
125
|
labels.push(`inherited from ${str(e.data.commit) ?? "unknown commit"} · #L${later.line}`);
|
|
125
|
-
if (e.event === "task-approved" && e.data.release === "gate-satisfied" && e.data.gate === gate)
|
|
126
|
+
if (e.event === "task-approved" && e.data.release === "gate-satisfied" && e.data.gate === gate && !undecidedLines(evidence.later(0)).has(later.line))
|
|
126
127
|
labels.push(`satisfied by approval #L${later.line}`);
|
|
127
128
|
}
|
|
128
129
|
const verdict = row?.event?.event === "gate-result" && typeof data?.details === "string" ? data.details.split("\n") : [];
|
|
@@ -191,7 +192,7 @@ const readLabel = (f) => `${f.label} ${f.line === undefined ? `(${NO_ROW})` : `#
|
|
|
191
192
|
* Nothing is inferred from a title, a provider or a task id: an unrecorded field says so.
|
|
192
193
|
*/
|
|
193
194
|
export function projectRunTasks(snapshot, rows, graph, decisions, runId) {
|
|
194
|
-
const ordered =
|
|
195
|
+
const ordered = decidedRows(rows);
|
|
195
196
|
const graphTask = (id) => graph?.tasks.find((t) => t.id === id);
|
|
196
197
|
const activityTasks = snapshot.tasks.map((t) => ({ id: t.id, gates: graphTask(t.id)?.gates ?? [...GATE_NAMES], deps: graphTask(t.id)?.deps ?? [], status: t.state }));
|
|
197
198
|
const tracked = trackJournalRows(runId, ordered.map((r) => ({ raw: r.event, sourceIndex: r.line })));
|
|
@@ -208,7 +209,16 @@ export function projectRunTasks(snapshot, rows, graph, decisions, runId) {
|
|
|
208
209
|
...(own.length === 0 ? {} : { phase: activity.get(t.id)?.state, lastEvidenceAt: own.at(-1).event.ts }),
|
|
209
210
|
...(responsibility ? { responsible: { role: str(responsibility.event.data.role), agent: str(responsibility.event.data.agent) } } : {}),
|
|
210
211
|
};
|
|
211
|
-
}), decisions.map((d) => ({
|
|
212
|
+
}), decisions.map((d) => ({
|
|
213
|
+
taskId: d.taskId,
|
|
214
|
+
park: {
|
|
215
|
+
kind: d.park.kind, tombstone: d.park.tombstone, line: d.park.line,
|
|
216
|
+
...(d.park.ts === undefined ? {} : { ts: d.park.ts }),
|
|
217
|
+
...(d.park.reason === undefined ? {} : { reason: d.park.reason }),
|
|
218
|
+
},
|
|
219
|
+
verbs: d.verbs,
|
|
220
|
+
...(d.diagnostic === undefined ? {} : { diagnostic: d.diagnostic }),
|
|
221
|
+
}))).map((s) => [s.taskId, s]));
|
|
212
222
|
return snapshot.tasks.map((t) => {
|
|
213
223
|
const own = byTask(t.id);
|
|
214
224
|
const act = activity.get(t.id);
|
|
@@ -241,8 +251,11 @@ export function projectRunTasks(snapshot, rows, graph, decisions, runId) {
|
|
|
241
251
|
const blocker = { label: `blocker ${blk ? `${blk.kind}${blk.diagnostic ? ` · ${blk.diagnostic}` : ""}` : "none"}`, ...(blk && blockerRow ? { line: blockerRow.line } : {}) };
|
|
242
252
|
const nextAction = { label: `next ${blk?.nextAction ?? "none"}`, ...(blk?.nextAction && blockerRow ? { line: blockerRow.line } : {}) };
|
|
243
253
|
// OBS-1048 harvest, chronological: the newest worker-launch opens the attempt; a later worker-result retires it.
|
|
244
|
-
let launched
|
|
245
|
-
|
|
254
|
+
let launched;
|
|
255
|
+
let launchedAttempt;
|
|
256
|
+
let returned = false;
|
|
257
|
+
const nudges = [];
|
|
258
|
+
const pages = [];
|
|
246
259
|
// Finding 1: only rows of the launched attempt count; a row without an attempt belongs to it (derive.ts attemptHarvests).
|
|
247
260
|
const ofLaunched = (d) => launched !== undefined && (ordinal(d.attempt) ?? launchedAttempt) === launchedAttempt;
|
|
248
261
|
for (const r of own) {
|
|
@@ -272,7 +285,7 @@ export function projectRunTasks(snapshot, rows, graph, decisions, runId) {
|
|
|
272
285
|
const stalled = launched !== undefined && !returned && nudges.length > 0 && pages.length > 0
|
|
273
286
|
? { label: `${STALL_MARKER} · launch #L${launched} · nudge failed ${nudges.map((l) => `#L${l}`).join(",")} · paged ${pages.map((l) => `#L${l}`).join(",")} · no worker-result`, line: launched }
|
|
274
287
|
: undefined;
|
|
275
|
-
return { taskId: t.id, identity, phase, build, blocker, nextAction, ...(stalled ? { stalled } : {}) };
|
|
288
|
+
return { taskId: t.id, identity, phase, build, blocker, nextAction, ...(stalled ? { stalled } : {}), ...(t.dispatches === 0 ? { neverDispatched: true } : {}) };
|
|
276
289
|
});
|
|
277
290
|
}
|
|
278
291
|
/** The line at which each projected value last changed, folding the task's rows (and run-level resets) prefix by prefix. */
|
|
@@ -299,8 +312,16 @@ function acceptedChanges(runId, tracked, taskId, task) {
|
|
|
299
312
|
}
|
|
300
313
|
return out;
|
|
301
314
|
}
|
|
302
|
-
/**
|
|
303
|
-
|
|
315
|
+
/**
|
|
316
|
+
* OBS-1104: a never-dispatched task has no identity, phase, or build row. Those fold into one
|
|
317
|
+
* missing-evidence clause. A substantive or journal-cited blocker and next action stay — a
|
|
318
|
+
* pre-dispatch park and a dependency wait are recorded facts, and dropping them hides the park token.
|
|
319
|
+
*/
|
|
320
|
+
export const MISSING_EVIDENCE = "missing evidence";
|
|
321
|
+
const UNRECORDED_BLOCKER = new Set(["blocker none", "blocker unknown"]);
|
|
322
|
+
const UNRECORDED_NEXT = new Set(["next none"]);
|
|
323
|
+
const keptWhenUndispatched = (field, unrecorded) => field.line !== undefined || !unrecorded.has(field.label);
|
|
324
|
+
export const projectionLine = (p) => p.neverDispatched === true ? undispatchedProjectionLine(p) : recordedProjectionLine(p);
|
|
304
325
|
/**
|
|
305
326
|
* Where the current attempt's pane is recorded — and only that. The reading is exact-name: the
|
|
306
327
|
* launch row's slot name must be the owned name of THIS task, attempt and run. No terminal is ever
|
|
@@ -358,6 +379,30 @@ export function paneLocator(task, rows, runId) {
|
|
|
358
379
|
}
|
|
359
380
|
/** The board's footer keys merged with the cockpit's own — every one of them handled by this view or the shell. */
|
|
360
381
|
export const RUN_VIEW_KEYS = [...BOARD_KEYS.slice(0, 2), "←→ verdict gate", "PgUp/PgDn page", "x outcome", "a actions", "o pane", "Tab focus", "? keys", BOARD_KEYS[2]];
|
|
382
|
+
function recordedProjectionLine(p) {
|
|
383
|
+
return [p.taskId, readLabel(p.identity), readLabel(p.phase), readLabel(p.build), readLabel(p.blocker), readLabel(p.nextAction), ...(p.stalled ? [p.stalled.label] : [])].join(" · ");
|
|
384
|
+
}
|
|
385
|
+
function undispatchedProjectionLine(p) {
|
|
386
|
+
return [
|
|
387
|
+
p.taskId,
|
|
388
|
+
MISSING_EVIDENCE,
|
|
389
|
+
...(keptWhenUndispatched(p.blocker, UNRECORDED_BLOCKER) ? [readLabel(p.blocker)] : []),
|
|
390
|
+
...(keptWhenUndispatched(p.nextAction, UNRECORDED_NEXT) ? [readLabel(p.nextAction)] : []),
|
|
391
|
+
...(p.stalled ? [p.stalled.label] : []),
|
|
392
|
+
].join(" · ");
|
|
393
|
+
}
|
|
394
|
+
/**
|
|
395
|
+
* Rows the projection panel paints: each task's clause at its own wrapped height (OBS-1193). A
|
|
396
|
+
* never-dispatched clause is shorter than the field line it replaces, so the block is shorter too;
|
|
397
|
+
* the shell's content counter reads that real height and the pinned run frames record it.
|
|
398
|
+
*/
|
|
399
|
+
function projectionBlockRows(projections, wrap) {
|
|
400
|
+
return projections.flatMap((p) => wrap(projectionLine(p)).map((line, i) => ({
|
|
401
|
+
key: `${p.taskId}:${i}`,
|
|
402
|
+
line,
|
|
403
|
+
strong: p.stalled !== undefined && i === 0,
|
|
404
|
+
})));
|
|
405
|
+
}
|
|
361
406
|
/** The Run body: the approved board (BD-1) over the fold, then the selected task's detail panels. */
|
|
362
407
|
export function RunView({ snapshot, rows, page, graph, decisions, session, columns, run, now = Date.now }) {
|
|
363
408
|
const inner = Math.max(1, Math.floor(columns) - 4);
|
|
@@ -383,9 +428,21 @@ export function RunView({ snapshot, rows, page, graph, decisions, session, colum
|
|
|
383
428
|
const { menu, confirming, receipt, notice } = session.decisions;
|
|
384
429
|
const blocked = task === undefined ? [] : graph?.tasks.filter((g) => g.deps.includes(task.id)).map((g) => g.id) ?? decision?.blocks ?? [];
|
|
385
430
|
const board = renderBoardLines({ runId: run.runId, snapshot, graph, now: now(), live: run.live, selection: task?.id, keys: false, colour }, width);
|
|
386
|
-
return (_jsxs(Box, { flexDirection: "column", width: columns, children: [_jsx(BodyText, { emphasis: "dim", children: fitCells(`RUN / ${snapshot.lifecycle} · ${runSummaryLine(snapshot)}`, width) }), board.map((line, i) => _jsx(BodyText, { children: line || " " }, `${i}:${line}`)), tasks.length === 0 && _jsx(BodyText, { children: fitCells("no tasks recorded — no plan", width) }), task !== undefined && (_jsxs(Panel, { title: `SELECTED / ${task.id}${task.title ? ` · ${task.title}` : ""}`, children: [wrap(`state ${parkOf(task)} · ${attemptLabel(task)} · dispatches ${task.dispatches} · path ${task.path ?? "unknown"} · pane ${task.pane ?? "unknown"} · alarm ${task.alarmMs === undefined ? "unknown" : `${task.alarmMs}ms`}${task.mergeEvidence ? ` · merged #L${task.mergeEvidence.line}` : ""} · locator ${(() => { const l = paneLocator(task, allRows, run.runId); return l.status === "recorded" ? `recorded ${l.reason}` : `unavailable — ${l.reason}`; })()}`).map((line, i) => _jsx(BodyText, { children: line }, `${i}:${line}`)), _jsx(BodyText, { emphasis: "dim", children: fit(`outcome filter ${session.outcomeFilter} (x cycles) · ${shown.length} of ${cells.length} gates shown`) }), shown.map((c) => (_jsx(BodyText, { children: fit(`${c.letter} ${c.gate.padEnd(10)} ${c.labels.join(" · ")}${c.line === undefined ? "" : ` · #L${c.line}`}`) }, c.gate)))] })), projections.length > 0 && (_jsx(Panel, { title: "PROJECTION / every task \u00B7 identity \u00B7 phase \u00B7 build \u00B7 blocker \u00B7 next action, each with its journal line", children: projections
|
|
431
|
+
return (_jsxs(Box, { flexDirection: "column", width: columns, children: [_jsx(BodyText, { emphasis: "dim", children: fitCells(`RUN / ${snapshot.lifecycle} · ${runSummaryLine(snapshot)}`, width) }), board.map((line, i) => _jsx(BodyText, { children: line || " " }, `${i}:${line}`)), tasks.length === 0 && _jsx(BodyText, { children: fitCells("no tasks recorded — no plan", width) }), task !== undefined && (_jsxs(Panel, { title: `SELECTED / ${task.id}${task.title ? ` · ${task.title}` : ""}`, children: [wrap(`state ${parkOf(task)} · ${attemptLabel(task)} · dispatches ${task.dispatches} · path ${task.path ?? "unknown"} · pane ${task.pane ?? "unknown"} · alarm ${task.alarmMs === undefined ? "unknown" : `${task.alarmMs}ms`}${task.mergeEvidence ? ` · merged #L${task.mergeEvidence.line}` : ""} · locator ${(() => { const l = paneLocator(task, allRows, run.runId); return l.status === "recorded" ? `recorded ${l.reason}` : `unavailable — ${l.reason}`; })()}`).map((line, i) => _jsx(BodyText, { children: line }, `${i}:${line}`)), _jsx(BodyText, { emphasis: "dim", children: fit(`outcome filter ${session.outcomeFilter} (x cycles) · ${shown.length} of ${cells.length} gates shown`) }), shown.map((c) => (_jsx(BodyText, { children: fit(`${c.letter} ${c.gate.padEnd(10)} ${c.labels.join(" · ")}${c.line === undefined ? "" : ` · #L${c.line}`}`) }, c.gate)))] })), projections.length > 0 && (_jsx(Panel, { title: "PROJECTION / every task \u00B7 identity \u00B7 phase \u00B7 build \u00B7 blocker \u00B7 next action, each with its journal line", children: projectionBlockRows(projections, wrap).map((row) => _jsx(BodyText, { emphasis: row.strong ? "strong" : "normal", children: row.line }, row.key)) })), verdictCell !== undefined && (_jsxs(Panel, { title: `VERDICT / ${verdictCell.gate}${verdictCell.line === undefined ? "" : ` #L${verdictCell.line}`} · ${verdict.length === 0 ? "no verdict text" : `lines ${from + 1}–${from + window.length} of ${verdict.length}`}`, children: [window.map((line, i) => _jsx(BodyText, { children: fit(`${String(from + i + 1).padStart(4)} ${line}`) }, `${from + i}:${line}`)), _jsx(BodyText, { emphasis: "dim", children: fit("←→ gate · PageUp/PageDown page") })] })), task?.state === "human" && (_jsx(Panel, { title: `PARK / ${task.id}`, children: decision === undefined
|
|
387
432
|
? _jsx(BodyText, { children: fit("park evidence unavailable — refresh before deciding") })
|
|
388
433
|
: (_jsxs(_Fragment, { children: [wrap(`#L${decision.park.line} ${decision.park.kind ?? "unknown kind"}${decision.park.failedGate ? ` · failed gate ${decision.park.failedGate}` : ""} · ${decision.park.reason ?? "no reason recorded"}`).map((line, i) => _jsx(BodyText, { children: line }, `park:${i}`)), _jsx(BodyText, { children: fit(`blocks ${blocked.length === 0 ? "nothing" : blocked.join(", ")} · attempts ${decision.attempts}`) }), wrap(decision.verbs.length === 0 ? `diagnostic: ${decision.diagnostic ?? "no verb"}` : `decisions: ${decision.verbs.join(" · ")} (a Actions)`).map((line, i) => _jsx(BodyText, { children: line }, `verbs:${i}`)), _jsx(BodyText, { emphasis: "dim", children: fit(run.live ? "matching live daemon enacts a release at its next task boundary" : run.blockingRunId ? `other live run ${run.blockingRunId} holds the lock; resume after it ends` : "no live owner: a release records permission; resume dispatches") })] })) })), menu !== null && (_jsx(Panel, { title: `ACTIONS / ${menu.taskId}`, focused: true, children: menu.verbs.length === 0
|
|
389
434
|
? _jsx(BodyText, { children: fit(`no decision — ${decision?.diagnostic ?? "no verb for this park"}`) })
|
|
390
435
|
: menu.verbs.map((verb, i) => _jsx(BodyText, { emphasis: i === menu.selection ? "strong" : "normal", children: fit(`${i === menu.selection ? `${GLYPHS.pointer} ` : " "}${verb}`) }, verb)) })), confirming !== null && (_jsx(Panel, { title: `CONFIRM ${confirming.command.verb.toUpperCase()}`, focused: true, children: decisionLines(decisionConfirmLines(confirming)).map((line, i) => _jsx(BodyText, { children: line }, `${i}:${line}`)) })), receipt !== null && (_jsx(Panel, { title: receipt.ok ? "RECEIPT · appended" : "RECEIPT · refused", children: decisionLines(decisionReceiptLines(receipt)).map((line, i) => _jsx(BodyText, { children: line }, `${i}:${line}`)) })), notice !== null && _jsx(BodyText, { children: notice }), _jsx(BodyText, { emphasis: "dim", children: clipBoard(boardFooter([...RUN_VIEW_KEYS, decisionKeybar(session.decisions, decision)].filter(Boolean), colour), width) })] }));
|
|
391
436
|
}
|
|
437
|
+
// OBS-1178: a refused or unsound decision released and satisfied nothing, so neither the projections nor
|
|
438
|
+
// a gate cell's satisfied label read it; the one decision fold judges every row by its physical line.
|
|
439
|
+
function undecidedLines(rows) {
|
|
440
|
+
const ordered = rows.filter((r) => r.event !== undefined).sort((a, b) => a.line - b.line);
|
|
441
|
+
const effective = effectiveDecisions(ordered.map((r) => withPhysicalLine(r.event, r.line)));
|
|
442
|
+
return new Set(ordered.filter((r) => r.event.event === "task-approved" && !effective.has(r.line)).map((r) => r.line));
|
|
443
|
+
}
|
|
444
|
+
/** Readable rows in physical-line order, minus the decisions that are not effective. */
|
|
445
|
+
function decidedRows(rows) {
|
|
446
|
+
const undecided = undecidedLines(rows);
|
|
447
|
+
return rows.filter((r) => r.event !== undefined && !undecided.has(r.line)).sort((a, b) => a.line - b.line);
|
|
448
|
+
}
|
|
@@ -71,6 +71,10 @@ export type ParkedDecision = {
|
|
|
71
71
|
* so it is drawn read-only and neither verb is offered on it.
|
|
72
72
|
*/
|
|
73
73
|
readonly tombstone: boolean;
|
|
74
|
+
/** OBS-1178: the park's `<line>@<ts>` token — the confirmed write binds to it via `--park`. */
|
|
75
|
+
readonly park?: string;
|
|
76
|
+
/** OBS-1202: a stall park's recorded reap failure — the one stall park recheck may release. */
|
|
77
|
+
readonly reapFailure?: string;
|
|
74
78
|
};
|
|
75
79
|
/** The parks a verb can release — the rows the surface draws live verbs on. */
|
|
76
80
|
export declare function actionableDecisions(decisions: readonly ParkedDecision[]): readonly ParkedDecision[];
|
|
@@ -80,6 +84,8 @@ export type SetupDecisionVerb = "approve" | "waive" | "uphold" | "recheck";
|
|
|
80
84
|
export type SetupDecisionCommand = {
|
|
81
85
|
readonly verb: SetupDecisionVerb;
|
|
82
86
|
readonly taskId: string;
|
|
87
|
+
/** OBS-1178: the park token the confirm inset displayed — approve refuses it once a newer park opens. */
|
|
88
|
+
readonly park?: string;
|
|
83
89
|
};
|
|
84
90
|
/** What the journal file gained this session — one entry per confirmed write. */
|
|
85
91
|
export type SetupDecisionWrite = {
|
|
@@ -5,7 +5,7 @@ import { join } from "node:path";
|
|
|
5
5
|
import { cloneElement, createElement, useState, } from "react";
|
|
6
6
|
import { parse } from "yaml";
|
|
7
7
|
import { approvalDispositionForRelease, approvalEnactment, approvalRunOwner, approve, } from "../../cli/commands/approve.js";
|
|
8
|
-
import { ATTEMPT_CAP_RELEASE, GATE_SATISFIED_RELEASE, Journal, RECHECK_RELEASE, REVIEW_UPHELD_RELEASE, } from "../../run/journal.js";
|
|
8
|
+
import { ATTEMPT_CAP_RELEASE, bindingToken, GATE_SATISFIED_RELEASE, Journal, RECHECK_RELEASE, REVIEW_UPHELD_RELEASE, } from "../../run/journal.js";
|
|
9
9
|
import { BANNER, GLYPHS, PLAIN_BANNER, } from "../../brand.js";
|
|
10
10
|
import { DEFAULT_CONFIG, INTEGRITY_FLOOR_SHAPES, TIER_RANK, TickmarkrConfigSchema, unifiedYamlDiff, } from "../../config/config.js";
|
|
11
11
|
import { GATE_NAMES, SHAPES } from "../../graph/schema.js";
|
|
@@ -576,7 +576,7 @@ function failedGateForNewestPark(events, taskId, parkedIndex) {
|
|
|
576
576
|
}
|
|
577
577
|
export function deriveParkedDecisions(journal) {
|
|
578
578
|
const statuses = journal.replayStatuses();
|
|
579
|
-
const events = journal.
|
|
579
|
+
const { events, lines } = journal.readSourced();
|
|
580
580
|
const decisions = [];
|
|
581
581
|
for (const [taskId, status] of statuses) {
|
|
582
582
|
if (status !== "human")
|
|
@@ -606,6 +606,8 @@ export function deriveParkedDecisions(journal) {
|
|
|
606
606
|
attempts,
|
|
607
607
|
failedGate,
|
|
608
608
|
tombstone: isTombstonePark(kind, reason),
|
|
609
|
+
...(typeof parked?.ts === "string" ? { park: bindingToken({ line: lines[parkedIndex], ts: parked.ts }) } : {}),
|
|
610
|
+
...(kind === "stall" && typeof parked?.data.reapFailure === "string" ? { reapFailure: parked.data.reapFailure } : {}),
|
|
609
611
|
});
|
|
610
612
|
}
|
|
611
613
|
return decisions;
|
|
@@ -622,6 +624,9 @@ export function initialSetupDecisionsSession() {
|
|
|
622
624
|
export function setupDecisionVerbs(decision) {
|
|
623
625
|
if (decision.tombstone)
|
|
624
626
|
return [];
|
|
627
|
+
// OBS-1202: the production table's census-recovery row; an ordinary stall stays approve-only.
|
|
628
|
+
if (decision.kind === "stall" && decision.reapFailure !== undefined)
|
|
629
|
+
return ["approve", "recheck"];
|
|
625
630
|
if (decision.kind !== "gate-fail")
|
|
626
631
|
return ["approve"];
|
|
627
632
|
if (decision.failedGate === undefined)
|
|
@@ -677,7 +682,7 @@ export function applySetupDecisionsKey(session, event, parked) {
|
|
|
677
682
|
session: {
|
|
678
683
|
...session,
|
|
679
684
|
selection,
|
|
680
|
-
confirming: { verb, taskId: decision.taskId },
|
|
685
|
+
confirming: { verb, taskId: decision.taskId, ...(decision.park === undefined ? {} : { park: decision.park }) },
|
|
681
686
|
},
|
|
682
687
|
};
|
|
683
688
|
}
|
|
@@ -704,6 +709,7 @@ export async function executeSetupDecision(command, { cwd, runId, by }) {
|
|
|
704
709
|
runId,
|
|
705
710
|
command.taskId,
|
|
706
711
|
...decisionFlag(command.verb),
|
|
712
|
+
...(command.park === undefined ? [] : ["--park", command.park]),
|
|
707
713
|
"--by",
|
|
708
714
|
by,
|
|
709
715
|
], cwd);
|
|
@@ -800,6 +806,7 @@ function decisionEffectLines(command, decision, run) {
|
|
|
800
806
|
export function setupDecisionConfirmLines(command, decision, actor, journalFile, run) {
|
|
801
807
|
return [
|
|
802
808
|
`task ${command.taskId}${decision === undefined ? "" : ` · ${decision.kind} · attempt ${decision.attempts}`}`,
|
|
809
|
+
...(command.park === undefined ? [] : [`park ${command.park} · bound; refused if a newer park opens`]),
|
|
803
810
|
`actor ${actor}`,
|
|
804
811
|
...decisionEffectLines(command, decision, run),
|
|
805
812
|
`file ${journalFile} · append only`,
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { type AuthHealth, type WorkerAdapter } from "../../adapters/types.js";
|
|
2
2
|
import { type DenyScopeKey, type InitialDenyProps, type MapEntry, type RoutingMode, type StagedDenyKey, type Tier, type TickmarkrConfig } from "../../config/config.js";
|
|
3
|
-
import { type Shape } from "../../graph/schema.js";
|
|
3
|
+
import { type Effort, type Shape } from "../../graph/schema.js";
|
|
4
4
|
export type FleetSteeringKey = "review" | "consult";
|
|
5
5
|
export type FleetEditorState = Record<DenyScopeKey, string[]> & {
|
|
6
6
|
/** OBS-1046: the staged routing.allow complement, distinct from the authored deny lists above */
|
|
7
7
|
allowOut?: string[];
|
|
8
8
|
classifications: FleetClassification[];
|
|
9
|
+
/** OBS-1182: staged launch effort per adapter → model; absent = the CLI's own default */
|
|
10
|
+
efforts?: Record<string, Record<string, Effort>>;
|
|
9
11
|
selectedMode: RoutingMode;
|
|
10
12
|
map: Record<string, MapEntry>;
|
|
11
13
|
judgeSeat?: {
|
|
@@ -22,6 +24,8 @@ export type FleetOverlayReview = {
|
|
|
22
24
|
after: string;
|
|
23
25
|
diff: string;
|
|
24
26
|
path: string;
|
|
27
|
+
/** OBS-1144: dead pool entries the written config would carry, each with its reason. */
|
|
28
|
+
notes?: string[];
|
|
25
29
|
};
|
|
26
30
|
export type FleetEditorResult = {
|
|
27
31
|
kind: "write";
|
|
@@ -121,11 +125,13 @@ export type FleetStagedRouting = {
|
|
|
121
125
|
ok: false;
|
|
122
126
|
error: string;
|
|
123
127
|
};
|
|
124
|
-
export declare function FleetApp({ ageMs, agents, initialAllowOut, modelGroups, initialMode, modeOptions, initialMap, modePreview, shapeRows, candidatesForShape, preferOptionsForShape, initialSteering, steeringOptionsFor, reviewOverlay, reloadGuard, stagedRouting, entry, initialJudge, judgeSeats, viewRows, frameColumns, frameRows, ...initialDenyProps }: InitialDenyProps & {
|
|
128
|
+
export declare function FleetApp({ ageMs, agents, initialAllowOut, initialEfforts, modelGroups, initialMode, modeOptions, initialMap, modePreview, shapeRows, candidatesForShape, preferOptionsForShape, initialSteering, steeringOptionsFor, reviewAdvisories, reviewOverlay, reloadGuard, stagedRouting, entry, initialJudge, judgeSeats, viewRows, frameColumns, frameRows, ...initialDenyProps }: InitialDenyProps & {
|
|
125
129
|
ageMs: number | null;
|
|
126
130
|
agents: AgentCli[];
|
|
127
131
|
/** OBS-1046: adapter ids and adapter:model keys routing.allow leaves out of the fleet */
|
|
128
132
|
initialAllowOut?: string[];
|
|
133
|
+
/** OBS-1182: configured launch effort per adapter → model */
|
|
134
|
+
initialEfforts?: Record<string, Record<string, Effort>>;
|
|
129
135
|
modelGroups: FleetModelGroup[];
|
|
130
136
|
initialMode: RoutingMode;
|
|
131
137
|
modeOptions: FleetModeOption[];
|
|
@@ -140,6 +146,8 @@ export declare function FleetApp({ ageMs, agents, initialAllowOut, modelGroups,
|
|
|
140
146
|
preferOptionsForShape: (shape: Shape, current: string[]) => string[];
|
|
141
147
|
initialSteering: Record<FleetSteeringKey, string[] | undefined>;
|
|
142
148
|
steeringOptionsFor: (which: FleetSteeringKey, current: string[]) => string[];
|
|
149
|
+
/** OBS-1052(3): advisory review no-verdict history lines — display only, never a routing input */
|
|
150
|
+
reviewAdvisories?: string[];
|
|
143
151
|
reviewOverlay: (state: FleetEditorState) => FleetOverlayReview;
|
|
144
152
|
reloadGuard: (bytes: string) => string | null;
|
|
145
153
|
/** the routing policy the staged deny sets load as (fleet: the config loader over the candidate
|
|
@@ -157,12 +165,14 @@ export declare function FleetApp({ ageMs, agents, initialAllowOut, modelGroups,
|
|
|
157
165
|
/** terminal height — set only on a real TTY so the footer sticks to the bottom */
|
|
158
166
|
frameRows?: number;
|
|
159
167
|
}): import("react").JSX.Element;
|
|
160
|
-
export declare function runFleetInkEditor({ ageMs, adapters, health, initialAllowOut, modelGroups, initialMode, modeOptions, initialMap, modePreview, shapeRows, candidatesForShape, preferOptionsForShape, initialSteering, steeringOptionsFor, reviewOverlay, reloadGuard, stagedRouting, entry, initialJudge, judgeSeats, initialInput, input, output, debug, ...initialDenyProps }: InitialDenyProps & {
|
|
168
|
+
export declare function runFleetInkEditor({ ageMs, adapters, health, initialAllowOut, initialEfforts, modelGroups, initialMode, modeOptions, initialMap, modePreview, shapeRows, candidatesForShape, preferOptionsForShape, initialSteering, steeringOptionsFor, reviewAdvisories, reviewOverlay, reloadGuard, stagedRouting, entry, initialJudge, judgeSeats, initialInput, input, output, debug, ...initialDenyProps }: InitialDenyProps & {
|
|
161
169
|
ageMs: number | null;
|
|
162
170
|
adapters: WorkerAdapter[];
|
|
163
171
|
health: Record<string, AuthHealth>;
|
|
164
172
|
/** OBS-1046: adapter ids and adapter:model keys routing.allow leaves out of the fleet */
|
|
165
173
|
initialAllowOut?: string[];
|
|
174
|
+
/** OBS-1182: configured launch effort per adapter → model */
|
|
175
|
+
initialEfforts?: Record<string, Record<string, Effort>>;
|
|
166
176
|
modelGroups: FleetModelGroup[];
|
|
167
177
|
initialMode: RoutingMode;
|
|
168
178
|
modeOptions: FleetModeOption[];
|
|
@@ -177,6 +187,8 @@ export declare function runFleetInkEditor({ ageMs, adapters, health, initialAllo
|
|
|
177
187
|
preferOptionsForShape: (shape: Shape, current: string[]) => string[];
|
|
178
188
|
initialSteering: Record<FleetSteeringKey, string[] | undefined>;
|
|
179
189
|
steeringOptionsFor: (which: FleetSteeringKey, current: string[]) => string[];
|
|
190
|
+
/** OBS-1052(3): advisory review no-verdict history lines — display only, never a routing input */
|
|
191
|
+
reviewAdvisories?: string[];
|
|
180
192
|
reviewOverlay: (state: FleetEditorState) => FleetOverlayReview;
|
|
181
193
|
reloadGuard: (bytes: string) => string | null;
|
|
182
194
|
/** the routing policy the staged deny sets load as (fleet: the config loader over the candidate
|