tickmarkr 2.5.4 → 2.5.6

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.
Files changed (78) hide show
  1. package/README.md +3 -1
  2. package/dist/adapters/registry.js +6 -1
  3. package/dist/adapters/types.d.ts +3 -0
  4. package/dist/cli/commands/approve.d.ts +1 -0
  5. package/dist/cli/commands/approve.js +118 -9
  6. package/dist/cli/commands/doctor.d.ts +10 -0
  7. package/dist/cli/commands/doctor.js +44 -29
  8. package/dist/cli/commands/fleet.js +199 -33
  9. package/dist/cli/commands/init.js +196 -6
  10. package/dist/cli/commands/plan.js +20 -4
  11. package/dist/cli/commands/resume.js +4 -2
  12. package/dist/cli/commands/run.js +11 -2
  13. package/dist/cli/commands/verify.js +102 -81
  14. package/dist/cli/help.d.ts +2 -0
  15. package/dist/cli/help.js +3 -1
  16. package/dist/config/config.d.ts +25 -8
  17. package/dist/config/config.js +41 -29
  18. package/dist/config/fleet-overlay.d.ts +3 -9
  19. package/dist/config/fleet-overlay.js +114 -19
  20. package/dist/config/fleet-why.d.ts +7 -0
  21. package/dist/config/fleet-why.js +5 -0
  22. package/dist/drivers/index.d.ts +15 -1
  23. package/dist/drivers/index.js +39 -10
  24. package/dist/drivers/orca.d.ts +119 -10
  25. package/dist/drivers/orca.js +781 -110
  26. package/dist/gates/baseline.d.ts +17 -3
  27. package/dist/gates/baseline.js +63 -15
  28. package/dist/gates/cache.d.ts +101 -0
  29. package/dist/gates/cache.js +401 -0
  30. package/dist/gates/llm.d.ts +3 -0
  31. package/dist/gates/llm.js +11 -0
  32. package/dist/gates/review.d.ts +28 -3
  33. package/dist/gates/review.js +106 -14
  34. package/dist/gates/run-gates.d.ts +9 -1
  35. package/dist/gates/run-gates.js +407 -102
  36. package/dist/gates/test-manifest.d.ts +128 -0
  37. package/dist/gates/test-manifest.js +463 -0
  38. package/dist/gates/test-reporter.d.ts +4 -0
  39. package/dist/gates/test-reporter.js +57 -0
  40. package/dist/graph/graph.d.ts +2 -0
  41. package/dist/graph/graph.js +45 -1
  42. package/dist/route/preference.d.ts +22 -1
  43. package/dist/route/preference.js +123 -25
  44. package/dist/route/router.js +31 -6
  45. package/dist/run/consult.js +5 -4
  46. package/dist/run/daemon.d.ts +15 -0
  47. package/dist/run/daemon.js +638 -167
  48. package/dist/run/execution-budget.d.ts +25 -0
  49. package/dist/run/execution-budget.js +142 -0
  50. package/dist/run/git.d.ts +50 -1
  51. package/dist/run/git.js +131 -12
  52. package/dist/run/journal.d.ts +16 -2
  53. package/dist/run/journal.js +115 -25
  54. package/dist/run/lease.d.ts +58 -0
  55. package/dist/run/lease.js +310 -0
  56. package/dist/run/merge.d.ts +2 -0
  57. package/dist/run/merge.js +91 -3
  58. package/dist/run/operator-state.d.ts +11 -0
  59. package/dist/run/operator-state.js +17 -3
  60. package/dist/run/recovery.d.ts +8 -0
  61. package/dist/run/recovery.js +25 -0
  62. package/dist/run/repair-selection.d.ts +12 -0
  63. package/dist/run/repair-selection.js +56 -0
  64. package/dist/run/stall.d.ts +6 -1
  65. package/dist/run/stall.js +60 -3
  66. package/dist/tui/cockpit/board.d.ts +96 -0
  67. package/dist/tui/cockpit/board.js +346 -0
  68. package/dist/tui/cockpit/decision-actions.js +2 -0
  69. package/dist/tui/cockpit/layout.d.ts +5 -1
  70. package/dist/tui/cockpit/layout.js +8 -3
  71. package/dist/tui/cockpit/live-runtime.js +71 -21
  72. package/dist/tui/cockpit/run-view.d.ts +7 -5
  73. package/dist/tui/cockpit/run-view.js +12 -11
  74. package/dist/tui/ink/fleet-app.d.ts +49 -27
  75. package/dist/tui/ink/fleet-app.js +229 -38
  76. package/package.json +2 -2
  77. package/skills/tickmarkr-overseer/SKILL.md +173 -113
  78. package/skills/tickmarkr-overseer/scripts/grade-ci.sh +29 -2
@@ -1,9 +1,11 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { Box } from "ink";
3
+ import { useContext } from "react";
3
4
  import { GLYPHS } from "../../brand.js";
4
5
  import { GATE_NAMES } from "../../graph/schema.js";
5
6
  import { normalizeGateOutcome } from "../../run/outcome.js";
6
- import { BodyText, Panel } from "./components.js";
7
+ import { BOARD_KEYS, boardFooter, clipBoard, renderBoardLines } from "./board.js";
8
+ import { BodyText, Panel, ShellTheme } from "./components.js";
7
9
  import { applyDecisionKey, decisionConfirmLines, decisionKeybar, decisionReceiptLines, initialDecisionSession, } from "./decision-actions.js";
8
10
  import { fitCells, wrapCells } from "./width.js";
9
11
  /** Bounded history first, then the store's page accessor for an evicted line. */
@@ -170,15 +172,13 @@ export function runSummaryLine(s) {
170
172
  }
171
173
  const attemptLabel = (t) => (t.attempt !== undefined ? `attempt ${t.attempt}` : t.dispatches === 0 ? "never dispatched" : "attempt unknown");
172
174
  const parkOf = (t) => (t.state === "human" ? `human · ${t.parkKind ?? "unknown kind"}` : t.state);
173
- /** One matrix row: id, state, the seven letters in declaration order, the recorded attempt. */
174
- export function runTaskRow(t, cells) {
175
- return `${t.id.padEnd(4)} ${parkOf(t).padEnd(20)} ${cells.map((c) => c.letter).join(" ")} ${attemptLabel(t)}`;
176
- }
177
- const MATRIX_HEADER = `${"Task".padEnd(4)} ${"state".padEnd(20)} ${GATE_NAMES.map((g) => g.slice(0, 2)).join(" ")} latest attempt`;
178
- const MATRIX_LEGEND = "P pass · F fail · R running · - not run · D disabled · ? unknown | declaration order, not a timeline · acceptance/review concurrent, optional";
179
- /** The Run body. Every line is measured through the width module; nothing overflows its column. */
180
- export function RunView({ snapshot, rows, page, graph, decisions, session, columns, run }) {
175
+ /** The board's footer keys merged with the cockpit's own — every one of them handled by this view or the shell. */
176
+ 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]];
177
+ /** The Run body: the approved board (BD-1) over the fold, then the selected task's detail panels. */
178
+ export function RunView({ snapshot, rows, page, graph, decisions, session, columns, run, now = Date.now }) {
181
179
  const inner = Math.max(1, Math.floor(columns) - 4);
180
+ const colour = useContext(ShellTheme).mode !== "none";
181
+ const width = Math.max(1, Math.floor(columns));
182
182
  const fit = (text) => fitCells(text, inner);
183
183
  const decisionLines = (lines) => lines.flatMap((line) => wrapCells(line, inner));
184
184
  const lookup = evidenceLookup(rows, page);
@@ -194,9 +194,10 @@ export function RunView({ snapshot, rows, page, graph, decisions, session, colum
194
194
  const decision = task === undefined ? undefined : decisions.find((d) => d.taskId === task.id);
195
195
  const { menu, confirming, receipt, notice } = session.decisions;
196
196
  const blocked = task === undefined ? [] : graph?.tasks.filter((g) => g.deps.includes(task.id)).map((g) => g.id) ?? decision?.blocks ?? [];
197
- return (_jsxs(Box, { flexDirection: "column", width: columns, children: [_jsxs(Panel, { title: `RUN / ${snapshot.lifecycle}`, focused: true, children: [_jsx(BodyText, { emphasis: "dim", children: fit(runSummaryLine(snapshot)) }), _jsx(BodyText, { emphasis: "dim", children: fit(` ${MATRIX_HEADER}`) }), tasks.map((t, i) => (_jsx(BodyText, { emphasis: i === selection ? "strong" : "normal", children: fit(`${i === selection ? `${GLYPHS.pointer} ` : " "}${runTaskRow(t, GATE_NAMES.map((gate) => ({ letter: GATE_CELL_LETTERS[t.gates[gate].state] })))}`) }, t.id))), tasks.length === 0 && _jsx(BodyText, { children: fit("no tasks recorded — no plan") }), _jsx(BodyText, { emphasis: "dim", children: fit(MATRIX_LEGEND) })] }), task !== undefined && (_jsxs(Panel, { title: `SELECTED / ${task.id}${task.title ? ` · ${task.title}` : ""}`, children: [_jsx(BodyText, { children: fit(`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}` : ""}`) }), _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)))] })), 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
197
+ const board = renderBoardLines({ runId: run.runId, snapshot, graph, now: now(), live: run.live, selection: task?.id, keys: false, colour }, width);
198
+ 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: [_jsx(BodyText, { children: fit(`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}` : ""}`) }), _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)))] })), 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
198
199
  ? _jsx(BodyText, { children: fit("park evidence unavailable — refresh before deciding") })
199
200
  : (_jsxs(_Fragment, { children: [_jsx(BodyText, { children: fit(`#L${decision.park.line} ${decision.park.kind ?? "unknown kind"}${decision.park.failedGate ? ` · failed gate ${decision.park.failedGate}` : ""} · ${decision.park.reason ?? "no reason recorded"}`) }), _jsx(BodyText, { children: fit(`blocks ${blocked.length === 0 ? "nothing" : blocked.join(", ")} · attempts ${decision.attempts}`) }), _jsx(BodyText, { children: fit(decision.verbs.length === 0 ? `diagnostic: ${decision.diagnostic ?? "no verb"}` : `decisions: ${decision.verbs.join(" · ")} (a Actions)`) }), _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
200
201
  ? _jsx(BodyText, { children: fit(`no decision — ${decision?.diagnostic ?? "no verb for this park"}`) })
201
- : 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: fitCells(["↑↓ Task", "←→ Verdict gate", "PgUp/PgDn Page", "x Outcome", decisionKeybar(session.decisions, decision)].filter(Boolean).join(" · "), Math.max(1, Math.floor(columns))) })] }));
202
+ : 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) })] }));
202
203
  }
@@ -1,10 +1,14 @@
1
1
  import { type AuthHealth, type WorkerAdapter } from "../../adapters/types.js";
2
- import type { MapEntry, RoutingMode, Tier } from "../../config/config.js";
2
+ import type { MapEntry, RoutingMode, Tier, TickmarkrConfig } from "../../config/config.js";
3
3
  import { type Shape } from "../../graph/schema.js";
4
4
  export type FleetSteeringKey = "review" | "consult";
5
5
  export type FleetEditorState = {
6
6
  denyAdapters: string[];
7
7
  denyModels: string[];
8
+ /** OBS-1046: the staged routing.allow complement, distinct from the authored deny lists above */
9
+ allowOut?: string[];
10
+ denyWorkersAdapters: string[];
11
+ denyWorkersModels: string[];
8
12
  classifications: FleetClassification[];
9
13
  selectedMode: RoutingMode;
10
14
  map: Record<string, MapEntry>;
@@ -56,6 +60,17 @@ export type FleetModelEvidence = {
56
60
  probeMs?: number;
57
61
  /** OBS-519: doctor's failed model-auth verdict reason — the row must not render like a healthy one */
58
62
  unauthed?: string;
63
+ /** OBS-972/FL-1: a floating alias's resolved concrete model id (e.g. "opus" → "claude-opus-5") */
64
+ identity?: string;
65
+ };
66
+ /** The four staged deny sets every preview and the staged routing policy are computed from. */
67
+ export type FleetStagedDeny = {
68
+ adapters: string[];
69
+ models: string[];
70
+ workersAdapters: string[];
71
+ workersModels: string[];
72
+ /** OBS-1046: the staged routing.allow complement; absent ⇒ the session's initial one */
73
+ allowOut?: string[];
59
74
  };
60
75
  export type FleetModelGroup = {
61
76
  adapter: string;
@@ -99,35 +114,43 @@ export type FleetCandidateOption = {
99
114
  };
100
115
  };
101
116
  export declare function formatDoctorAge(ageMs: number | null): string;
102
- export declare function FleetApp({ ageMs, agents, initialDenyAdapters, initialDenyModels, modelGroups, initialMode, modeOptions, initialMap, modePreview, shapeRows, candidatesForShape, preferOptionsForShape, initialSteering, steeringOptionsFor, reviewOverlay, reloadGuard, entry, initialJudge, judgeSeats, viewRows, frameColumns, frameRows, }: {
117
+ export type FleetReach = "in" | "out-workers" | "out-all" | "out-allow" | "unknown";
118
+ /** The staged routing policy, or the loader's refusal of the staged overlay. */
119
+ export type FleetStagedRouting = {
120
+ ok: true;
121
+ routing: TickmarkrConfig["routing"];
122
+ } | {
123
+ ok: false;
124
+ error: string;
125
+ };
126
+ export declare function FleetApp({ ageMs, agents, initialDenyAdapters, initialDenyModels, initialAllowOut, initialDenyWorkersAdapters, initialDenyWorkersModels, modelGroups, initialMode, modeOptions, initialMap, modePreview, shapeRows, candidatesForShape, preferOptionsForShape, initialSteering, steeringOptionsFor, reviewOverlay, reloadGuard, stagedRouting, entry, initialJudge, judgeSeats, viewRows, frameColumns, frameRows, }: {
103
127
  ageMs: number | null;
104
128
  agents: AgentCli[];
105
129
  initialDenyAdapters: string[];
106
130
  initialDenyModels: string[];
131
+ /** OBS-1046: adapter ids and adapter:model keys routing.allow leaves out of the fleet */
132
+ initialAllowOut?: string[];
133
+ initialDenyWorkersAdapters?: string[];
134
+ initialDenyWorkersModels?: string[];
107
135
  modelGroups: FleetModelGroup[];
108
136
  initialMode: RoutingMode;
109
137
  modeOptions: FleetModeOption[];
110
138
  initialMap: Record<string, MapEntry>;
111
- modePreview: (mode: RoutingMode, map: Record<string, MapEntry>, deny: {
112
- adapters: string[];
113
- models: string[];
114
- }) => string[];
115
- shapeRows: (mode: RoutingMode, map: Record<string, MapEntry>, deny: {
116
- adapters: string[];
117
- models: string[];
118
- }) => FleetShapeRow[];
119
- candidatesForShape: (shape: Shape, mode: RoutingMode, map: Record<string, MapEntry>, deny: {
120
- adapters: string[];
121
- models: string[];
122
- }) => {
139
+ modePreview: (mode: RoutingMode, map: Record<string, MapEntry>, deny: FleetStagedDeny) => string[];
140
+ shapeRows: (mode: RoutingMode, map: Record<string, MapEntry>, deny: FleetStagedDeny) => FleetShapeRow[];
141
+ candidatesForShape: (shape: Shape, mode: RoutingMode, map: Record<string, MapEntry>, deny: FleetStagedDeny) => {
123
142
  rows: FleetCandidateOption[];
124
143
  excludedNote?: string;
144
+ ledger?: string[];
125
145
  };
126
146
  preferOptionsForShape: (shape: Shape, current: string[]) => string[];
127
147
  initialSteering: Record<FleetSteeringKey, string[] | undefined>;
128
148
  steeringOptionsFor: (which: FleetSteeringKey, current: string[]) => string[];
129
149
  reviewOverlay: (state: FleetEditorState) => FleetOverlayReview;
130
150
  reloadGuard: (bytes: string) => string | null;
151
+ /** the routing policy the staged deny sets load as (fleet: the config loader over the candidate
152
+ * bytes); absent ⇒ the four staged sets alone, with no allowlist */
153
+ stagedRouting?: (deny: FleetStagedDeny) => FleetStagedRouting;
131
154
  entry?: "presets" | "probe";
132
155
  /** resolved config judge as "adapter:model" — shown on the (keep default) picker row */
133
156
  initialJudge?: string;
@@ -140,36 +163,35 @@ export declare function FleetApp({ ageMs, agents, initialDenyAdapters, initialDe
140
163
  /** terminal height — set only on a real TTY so the footer sticks to the bottom */
141
164
  frameRows?: number;
142
165
  }): import("react").JSX.Element;
143
- export declare function runFleetInkEditor({ ageMs, adapters, health, initialDenyAdapters, initialDenyModels, modelGroups, initialMode, modeOptions, initialMap, modePreview, shapeRows, candidatesForShape, preferOptionsForShape, initialSteering, steeringOptionsFor, reviewOverlay, reloadGuard, entry, initialJudge, judgeSeats, initialInput, input, output, debug, }: {
166
+ export declare function runFleetInkEditor({ ageMs, adapters, health, initialDenyAdapters, initialDenyModels, initialAllowOut, initialDenyWorkersAdapters, initialDenyWorkersModels, modelGroups, initialMode, modeOptions, initialMap, modePreview, shapeRows, candidatesForShape, preferOptionsForShape, initialSteering, steeringOptionsFor, reviewOverlay, reloadGuard, stagedRouting, entry, initialJudge, judgeSeats, initialInput, input, output, debug, }: {
144
167
  ageMs: number | null;
145
168
  adapters: WorkerAdapter[];
146
169
  health: Record<string, AuthHealth>;
147
170
  initialDenyAdapters: string[];
148
171
  initialDenyModels: string[];
172
+ /** OBS-1046: adapter ids and adapter:model keys routing.allow leaves out of the fleet */
173
+ initialAllowOut?: string[];
174
+ initialDenyWorkersAdapters?: string[];
175
+ initialDenyWorkersModels?: string[];
149
176
  modelGroups: FleetModelGroup[];
150
177
  initialMode: RoutingMode;
151
178
  modeOptions: FleetModeOption[];
152
179
  initialMap: Record<string, MapEntry>;
153
- modePreview: (mode: RoutingMode, map: Record<string, MapEntry>, deny: {
154
- adapters: string[];
155
- models: string[];
156
- }) => string[];
157
- shapeRows: (mode: RoutingMode, map: Record<string, MapEntry>, deny: {
158
- adapters: string[];
159
- models: string[];
160
- }) => FleetShapeRow[];
161
- candidatesForShape: (shape: Shape, mode: RoutingMode, map: Record<string, MapEntry>, deny: {
162
- adapters: string[];
163
- models: string[];
164
- }) => {
180
+ modePreview: (mode: RoutingMode, map: Record<string, MapEntry>, deny: FleetStagedDeny) => string[];
181
+ shapeRows: (mode: RoutingMode, map: Record<string, MapEntry>, deny: FleetStagedDeny) => FleetShapeRow[];
182
+ candidatesForShape: (shape: Shape, mode: RoutingMode, map: Record<string, MapEntry>, deny: FleetStagedDeny) => {
165
183
  rows: FleetCandidateOption[];
166
184
  excludedNote?: string;
185
+ ledger?: string[];
167
186
  };
168
187
  preferOptionsForShape: (shape: Shape, current: string[]) => string[];
169
188
  initialSteering: Record<FleetSteeringKey, string[] | undefined>;
170
189
  steeringOptionsFor: (which: FleetSteeringKey, current: string[]) => string[];
171
190
  reviewOverlay: (state: FleetEditorState) => FleetOverlayReview;
172
191
  reloadGuard: (bytes: string) => string | null;
192
+ /** the routing policy the staged deny sets load as (fleet: the config loader over the candidate
193
+ * bytes); absent ⇒ the four staged sets alone, with no allowlist */
194
+ stagedRouting?: (deny: FleetStagedDeny) => FleetStagedRouting;
173
195
  /** "presets" = init's entry: Esc in the browser is HOME to the preset overlay, not quit */
174
196
  entry?: "presets" | "probe";
175
197
  initialJudge?: string;