onto-mcp 0.4.12 → 0.4.13
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/.onto/authority/core-lexicon.yaml +1 -1
- package/.onto/authority/supported-models.yaml +137 -0
- package/.onto/domains/accounting-kr/competency_qs.md +153 -153
- package/.onto/processes/reconstruct/obligation-coverage-ledger.yaml +842 -0
- package/.onto/processes/reconstruct/obligation-coverage-recorded.yaml +225 -0
- package/.onto/processes/reconstruct/ontology-seeding-and-maturation-design.md +93 -54
- package/.onto/processes/reconstruct/reconstruct-contract-registry.yaml +169 -8
- package/.onto/processes/reconstruct/reconstruct-execution-ux-contract.md +72 -1
- package/.onto/processes/reconstruct/source-profiles/spreadsheet.md +35 -0
- package/.onto/processes/review/productized-live-path.md +9 -5
- package/.onto/processes/review/prompt-execution-runner-contract.md +32 -8
- package/.onto/processes/review/review-target-profile-contract.md +47 -4
- package/.onto/processes/shared/pipeline-execution-ledger-contract.md +11 -0
- package/.onto/processes/shared/target-material-kind-contract.md +1 -1
- package/README.md +64 -1
- package/dist/cli.js +11 -1
- package/dist/core-api/reconstruct-api.js +575 -24
- package/dist/core-api/review-api.js +70 -5
- package/dist/core-api/review-progress.js +7 -0
- package/dist/core-api/runtime-observation.js +7 -0
- package/dist/core-runtime/artifact-io.js +75 -3
- package/dist/core-runtime/cli/claude-code-review-unit-executor.js +19 -8
- package/dist/core-runtime/cli/materialize-review-prompt-packets.js +68 -14
- package/dist/core-runtime/cli/prepare-review-session.js +34 -1
- package/dist/core-runtime/cli/render-review-final-output.js +2 -1
- package/dist/core-runtime/cli/run-review-prompt-execution.js +758 -48
- package/dist/core-runtime/cli/run-seats.js +18 -0
- package/dist/core-runtime/cli/runtime-submit-context.js +10 -2
- package/dist/core-runtime/cli/unit-resubmit.js +269 -0
- package/dist/core-runtime/cli/worker-structured-output.js +80 -2
- package/dist/core-runtime/discovery/host-detection.js +71 -22
- package/dist/core-runtime/discovery/review-cert-assemble.js +208 -0
- package/dist/core-runtime/discovery/review-cert-record.js +442 -0
- package/dist/core-runtime/discovery/review-execution-units.js +22 -0
- package/dist/core-runtime/discovery/seat-inventory.js +167 -0
- package/dist/core-runtime/discovery/settings-chain.js +311 -52
- package/dist/core-runtime/discovery/supported-models.js +282 -29
- package/dist/core-runtime/discovery/synthesize-cert-assemble.js +197 -0
- package/dist/core-runtime/discovery/synthesize-cert-capsule.js +399 -0
- package/dist/core-runtime/discovery/synthesize-cert-judge.js +340 -0
- package/dist/core-runtime/discovery/synthesize-cert-loop.js +234 -0
- package/dist/core-runtime/discovery/synthesize-cert-mutation.js +117 -0
- package/dist/core-runtime/discovery/synthesize-cert-packet.js +182 -0
- package/dist/core-runtime/discovery/synthesize-cert-record.js +831 -0
- package/dist/core-runtime/discovery/synthesize-cert-sampler.js +336 -0
- package/dist/core-runtime/effort-calibration-ingest.js +135 -0
- package/dist/core-runtime/llm/dispatch-breaker.js +412 -0
- package/dist/core-runtime/llm/dispatch-fallback-adapter-capabilities.js +33 -0
- package/dist/core-runtime/llm/llm-caller.js +188 -47
- package/dist/core-runtime/llm/mock-llm-realization.js +35 -4
- package/dist/core-runtime/llm/model-switcher.js +1 -1
- package/dist/core-runtime/llm/openai-responses-incomplete-error.js +46 -0
- package/dist/core-runtime/llm/sealed-dispatch-capability.js +359 -0
- package/dist/core-runtime/llm/structured-dispatch-error.js +15 -0
- package/dist/core-runtime/onboard/claude-profile-scan.js +65 -0
- package/dist/core-runtime/onboard/cli-host.js +5 -2
- package/dist/core-runtime/onboard/configure-provider.js +33 -1
- package/dist/core-runtime/onboard/host-target.js +28 -3
- package/dist/core-runtime/onboard/register.js +54 -12
- package/dist/core-runtime/pipeline-execution-ledger.js +10 -0
- package/dist/core-runtime/reconstruct/artifact-types.js +35 -0
- package/dist/core-runtime/reconstruct/claim-projection-validation.js +17 -0
- package/dist/core-runtime/reconstruct/competency-projection-contract.js +63 -0
- package/dist/core-runtime/reconstruct/comprehension-artifact.js +321 -0
- package/dist/core-runtime/reconstruct/comprehension-reduce.js +367 -0
- package/dist/core-runtime/reconstruct/comprehension-semantic-map.js +771 -0
- package/dist/core-runtime/reconstruct/contract-registry.js +20 -0
- package/dist/core-runtime/reconstruct/dispatch-fallback-artifacts.js +640 -0
- package/dist/core-runtime/reconstruct/execution-telemetry.js +83 -9
- package/dist/core-runtime/reconstruct/final-output-sections.js +89 -0
- package/dist/core-runtime/reconstruct/governing-snapshot.js +36 -1
- package/dist/core-runtime/reconstruct/leaf-reader.js +305 -0
- package/dist/core-runtime/reconstruct/llm-dispatch-failure.js +270 -0
- package/dist/core-runtime/reconstruct/llm-touch-fingerprint.js +94 -0
- package/dist/core-runtime/reconstruct/material-admission-validation.js +18 -0
- package/dist/core-runtime/reconstruct/material-profile-validation.js +14 -0
- package/dist/core-runtime/reconstruct/materialize-preparation.js +304 -18
- package/dist/core-runtime/reconstruct/maturation-validation.js +1231 -53
- package/dist/core-runtime/reconstruct/mock-llm-realization.js +107 -3
- package/dist/core-runtime/reconstruct/obligation-assertion.js +18 -0
- package/dist/core-runtime/reconstruct/ontology-seed-validation.js +27 -0
- package/dist/core-runtime/reconstruct/output-budget.js +6 -0
- package/dist/core-runtime/reconstruct/pipeline-execution-ledger.js +87 -3
- package/dist/core-runtime/reconstruct/post-seed-validation.js +118 -5
- package/dist/core-runtime/reconstruct/purpose-authority-validation.js +117 -2
- package/dist/core-runtime/reconstruct/record.js +42 -0
- package/dist/core-runtime/reconstruct/registry-verification-validation.js +16 -0
- package/dist/core-runtime/reconstruct/run-control-validation.js +1011 -34
- package/dist/core-runtime/reconstruct/run.js +5198 -536
- package/dist/core-runtime/reconstruct/seed-authoring-readiness-validation.js +54 -3
- package/dist/core-runtime/reconstruct/semantic-quality-gate.js +105 -2
- package/dist/core-runtime/reconstruct/source-observation-delta-validation.js +41 -0
- package/dist/core-runtime/reconstruct/source-observations.js +166 -0
- package/dist/core-runtime/reconstruct/source-safety-validation.js +71 -183
- package/dist/core-runtime/reconstruct/source-scout-pack-validation.js +37 -1
- package/dist/core-runtime/reconstruct/terminal-validation.js +132 -2
- package/dist/core-runtime/review/continuation-plan.js +12 -5
- package/dist/core-runtime/review/issue-artifact-runtime.js +4 -1
- package/dist/core-runtime/review/materializers.js +100 -11
- package/dist/core-runtime/review/nesting-batch.js +6 -2
- package/dist/core-runtime/review/obligation-shardability.js +138 -0
- package/dist/core-runtime/review/pipeline-execution-ledger.js +42 -2
- package/dist/core-runtime/review/review-artifact-utils.js +217 -5
- package/dist/core-runtime/review/review-execution-steps.js +32 -8
- package/dist/core-runtime/review/review-progress-contract.js +8 -0
- package/dist/core-runtime/review/review-prompt-budget.js +97 -0
- package/dist/core-runtime/review/semantic-quality-gate.js +33 -7
- package/dist/core-runtime/review/spreadsheet-review-disposition.js +94 -0
- package/dist/core-runtime/route-identity.js +182 -0
- package/dist/core-runtime/spreadsheet-header-escalation.js +204 -0
- package/dist/core-runtime/spreadsheet-structure-observer.js +2689 -0
- package/dist/core-runtime/target-material-kind.js +58 -0
- package/dist/mcp/server.js +12 -5
- package/dist/tui/app.js +167 -0
- package/dist/tui/data/event-follower.js +116 -0
- package/dist/tui/data/node-detail.js +42 -0
- package/dist/tui/data/projection-poll.js +26 -0
- package/dist/tui/data/session-discovery.js +65 -0
- package/dist/tui/index.js +108 -0
- package/dist/tui/screens/log.js +25 -0
- package/dist/tui/screens/node-detail.js +17 -0
- package/dist/tui/screens/session-selector.js +26 -0
- package/dist/tui/screens/workflow-tree.js +90 -0
- package/dist/tui/view-model/reconstruct-adapter.js +162 -0
- package/dist/tui/view-model/review-adapter.js +270 -0
- package/dist/tui/view-model/tree-view-model.js +19 -0
- package/package.json +18 -3
- package/settings.example.json +77 -16
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* Log screen — a tail of a session's `runtime-events.ndjson` (fed by the
|
|
4
|
+
* event-follower in the app). Pure presentation: shows the most recent events
|
|
5
|
+
* with timestamp · source · stream · message. Read-only.
|
|
6
|
+
*/
|
|
7
|
+
import { Box, Text } from "ink";
|
|
8
|
+
function clockTime(timestamp) {
|
|
9
|
+
// Render only HH:MM:SS from an ISO timestamp without constructing a Date
|
|
10
|
+
// (avoids locale/clock surprises and keeps it test-stable).
|
|
11
|
+
const match = /T(\d{2}:\d{2}:\d{2})/.exec(timestamp);
|
|
12
|
+
return match ? match[1] : timestamp.slice(0, 8);
|
|
13
|
+
}
|
|
14
|
+
const STREAM_COLOR = {
|
|
15
|
+
model_call: "magenta",
|
|
16
|
+
status: undefined,
|
|
17
|
+
chunk: "gray",
|
|
18
|
+
};
|
|
19
|
+
export function LogScreen({ sessionId, events, maxLines = 18, }) {
|
|
20
|
+
const tail = events.slice(-maxLines);
|
|
21
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, children: `onto watch · ${sessionId} · log` }), _jsx(Box, { flexDirection: "column", marginTop: 1, children: tail.length === 0 ? (_jsx(Text, { dimColor: true, children: "no events yet" })) : (tail.map((event, index) => {
|
|
22
|
+
const sourceColor = STREAM_COLOR[event.stream];
|
|
23
|
+
return (_jsxs(Box, { children: [_jsx(Text, { dimColor: true, children: `${clockTime(event.timestamp)} ` }), _jsx(Text, { ...(sourceColor ? { color: sourceColor } : {}), children: `${(event.source.label ?? event.source.kind).padEnd(16)} ` }), _jsx(Text, { children: event.message })] }, index));
|
|
24
|
+
})) }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "[tab] tree [q]uit" }) })] }));
|
|
25
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* Drill-down detail pane for the node selected in the WorkflowTree HUD — shows
|
|
4
|
+
* the node's status/owner/attempts/failure/output and a tail of its output (the
|
|
5
|
+
* running log / authored artifact). Pure presentation.
|
|
6
|
+
*/
|
|
7
|
+
import { Box, Text } from "ink";
|
|
8
|
+
export function NodeDetail({ node, tail }) {
|
|
9
|
+
const meta = [`status ${node.status}`];
|
|
10
|
+
if (node.owner)
|
|
11
|
+
meta.push(`owner ${node.owner}`);
|
|
12
|
+
if (node.attempts && node.attempts > 1)
|
|
13
|
+
meta.push(`try${node.attempts}`);
|
|
14
|
+
if (node.signalAgeSec != null)
|
|
15
|
+
meta.push(`${node.signalAgeSec}s`);
|
|
16
|
+
return (_jsxs(Box, { flexDirection: "column", marginTop: 1, borderStyle: "round", borderColor: "cyan", paddingX: 1, children: [_jsx(Text, { bold: true, children: node.label }), _jsx(Text, { dimColor: true, children: meta.join(" · ") }), node.failureMessage ? _jsx(Text, { color: "red", children: node.failureMessage }) : null, node.outputPath ? _jsx(Text, { dimColor: true, children: node.outputPath }) : null, tail.length > 0 ? (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsx(Text, { dimColor: true, children: "\u2500\u2500 output (tail) \u2500\u2500" }), tail.map((line, index) => (_jsx(Text, { children: line }, index)))] })) : null] }));
|
|
17
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* SessionSelector — the `onto watch` entry screen when no session is given (or
|
|
4
|
+
* `s` from a session view). Lists review/reconstruct sessions (most recent
|
|
5
|
+
* first) with a cursor. Pure presentation: selection state is owned by the app.
|
|
6
|
+
*/
|
|
7
|
+
import { Box, Text } from "ink";
|
|
8
|
+
function relativeAge(modifiedMs, nowMs) {
|
|
9
|
+
if (!modifiedMs)
|
|
10
|
+
return "";
|
|
11
|
+
const sec = Math.max(0, Math.round((nowMs - modifiedMs) / 1000));
|
|
12
|
+
if (sec < 60)
|
|
13
|
+
return `${sec}s ago`;
|
|
14
|
+
if (sec < 3600)
|
|
15
|
+
return `${Math.round(sec / 60)}m ago`;
|
|
16
|
+
if (sec < 86400)
|
|
17
|
+
return `${Math.round(sec / 3600)}h ago`;
|
|
18
|
+
return `${Math.round(sec / 86400)}d ago`;
|
|
19
|
+
}
|
|
20
|
+
export function SessionSelector({ sessions, selectedIndex, nowMs, }) {
|
|
21
|
+
const now = nowMs ?? Date.now();
|
|
22
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, children: "onto watch \u00B7 sessions" }), sessions.length === 0 ? (_jsx(Text, { dimColor: true, children: "no review/reconstruct sessions found" })) : (_jsx(Box, { flexDirection: "column", marginTop: 1, children: sessions.map((session, index) => {
|
|
23
|
+
const active = index === selectedIndex;
|
|
24
|
+
return (_jsxs(Box, { children: [_jsx(Text, { ...(active ? { color: "cyan" } : {}), children: `${active ? "›" : " "} ${session.pipeline.padEnd(11)} ` }), _jsx(Text, { bold: active, children: session.sessionId }), _jsx(Text, { dimColor: true, children: ` ${relativeAge(session.modifiedMs, now)}` })] }, `${session.pipeline}:${session.sessionRoot}`));
|
|
25
|
+
}) })), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "[\u2191\u2193] select [enter] watch [q]uit" }) })] }));
|
|
26
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* WorkflowTree HUD — the primary `onto watch` screen. Pure presentation: renders
|
|
4
|
+
* a {@link TreeViewModel} (header · narrator · phase-grouped unit tree · footer)
|
|
5
|
+
* for either pipeline. The HUD never branches on pipeline; the adapters already
|
|
6
|
+
* normalized the difference into the view model.
|
|
7
|
+
*/
|
|
8
|
+
import { Box, Text } from "ink";
|
|
9
|
+
const NODE_ICON = {
|
|
10
|
+
completed: "✓",
|
|
11
|
+
running: "◐",
|
|
12
|
+
pending: "○",
|
|
13
|
+
failed: "✗",
|
|
14
|
+
halted: "◑",
|
|
15
|
+
skipped: "⊘",
|
|
16
|
+
};
|
|
17
|
+
const NODE_COLOR = {
|
|
18
|
+
completed: "green",
|
|
19
|
+
running: "cyan",
|
|
20
|
+
pending: "gray",
|
|
21
|
+
failed: "red",
|
|
22
|
+
halted: "yellow",
|
|
23
|
+
skipped: "gray",
|
|
24
|
+
};
|
|
25
|
+
const STATUS_BADGE = {
|
|
26
|
+
running: { label: "◐ running", color: "cyan" },
|
|
27
|
+
completed: { label: "✓ completed", color: "green" },
|
|
28
|
+
halted: { label: "◑ halted", color: "yellow" },
|
|
29
|
+
failed: { label: "✗ failed", color: "red" },
|
|
30
|
+
};
|
|
31
|
+
const SEVERITY_ORDER = ["blocker", "high", "medium", "low", "info"];
|
|
32
|
+
const SEVERITY_ABBR = {
|
|
33
|
+
blocker: "blk",
|
|
34
|
+
high: "high",
|
|
35
|
+
medium: "med",
|
|
36
|
+
low: "low",
|
|
37
|
+
info: "info",
|
|
38
|
+
};
|
|
39
|
+
function NodeBadge({ node }) {
|
|
40
|
+
const parts = [];
|
|
41
|
+
if (node.owner)
|
|
42
|
+
parts.push(node.owner);
|
|
43
|
+
if (node.signalAgeSec != null)
|
|
44
|
+
parts.push(`${node.signalAgeSec}s`);
|
|
45
|
+
if (node.attempts && node.attempts > 1)
|
|
46
|
+
parts.push(`try${node.attempts}`);
|
|
47
|
+
if (node.failureMessage)
|
|
48
|
+
parts.push(node.failureMessage);
|
|
49
|
+
if (parts.length === 0)
|
|
50
|
+
return null;
|
|
51
|
+
return _jsx(Text, { dimColor: true, children: ` ${parts.join(" · ")}` });
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Surfaces the run-control availability the status projection carries (observe-
|
|
55
|
+
* only: it reports what the operator can do via the MCP/CLI, the HUD never acts).
|
|
56
|
+
* Most relevant for halted/stale sessions where continue is the next step.
|
|
57
|
+
*/
|
|
58
|
+
function RunControlRow({ control }) {
|
|
59
|
+
const available = [];
|
|
60
|
+
if (control.continuable)
|
|
61
|
+
available.push("continue");
|
|
62
|
+
if (control.advanceable)
|
|
63
|
+
available.push("advance");
|
|
64
|
+
if (control.cancellable)
|
|
65
|
+
available.push("cancel");
|
|
66
|
+
if (available.length === 0)
|
|
67
|
+
return null;
|
|
68
|
+
return _jsx(Text, { dimColor: true, children: `Controls ${available.join(" · ")} available` });
|
|
69
|
+
}
|
|
70
|
+
function Footer({ vm }) {
|
|
71
|
+
const { findings, counts } = vm.summary;
|
|
72
|
+
if (findings) {
|
|
73
|
+
return (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsxs(Text, { children: ["Findings ", SEVERITY_ORDER.map((s) => `⬤${findings[s]} ${SEVERITY_ABBR[s]}`).join(" ")] }), findings.material.map((title, index) => (_jsx(Text, { dimColor: true, children: ` › ${title}` }, index)))] }));
|
|
74
|
+
}
|
|
75
|
+
if (counts) {
|
|
76
|
+
const pairs = Object.entries(counts)
|
|
77
|
+
.filter(([, value]) => value != null)
|
|
78
|
+
.map(([key, value]) => `${key} ${value}`);
|
|
79
|
+
return (_jsx(Box, { marginTop: 1, children: _jsxs(Text, { children: ["Coverage ", pairs.join(" · ")] }) }));
|
|
80
|
+
}
|
|
81
|
+
return _jsx(Box, { marginTop: 1 });
|
|
82
|
+
}
|
|
83
|
+
export function WorkflowTree({ vm, selectedNodeId }) {
|
|
84
|
+
const badge = STATUS_BADGE[vm.status];
|
|
85
|
+
const phasesDone = vm.phases.filter((p) => p.state === "completed").length;
|
|
86
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { children: [_jsx(Text, { bold: true, children: "onto watch" }), _jsx(Text, { children: ` · ${vm.pipeline} · ${vm.sessionId} ` }), _jsx(Text, { color: badge.color, children: badge.label })] }), vm.headline ? _jsx(Text, { dimColor: true, children: vm.headline }) : null, _jsx(Text, { children: `▸ ${vm.narrator}` }), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { children: `Pipeline ${phasesDone}/${vm.phases.length} phases` }), vm.liveness.secondsSinceSignal != null ? (_jsx(Text, { dimColor: true, children: ` · ${vm.liveness.secondsSinceSignal}s since signal` })) : null] }), _jsx(RunControlRow, { control: vm.runControl }), _jsx(Box, { flexDirection: "column", marginTop: 1, children: vm.phases.map((phase) => (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { color: NODE_COLOR[phase.state], children: `${NODE_ICON[phase.state]} ${phase.label}` }), phase.nodes.map((node) => {
|
|
87
|
+
const selected = node.id === selectedNodeId;
|
|
88
|
+
return (_jsxs(Box, { children: [_jsx(Text, { color: NODE_COLOR[node.status], children: `${selected ? "›" : " "} ${NODE_ICON[node.status]} ` }), _jsx(Text, { bold: selected, children: node.label }), _jsx(NodeBadge, { node: node })] }, node.id));
|
|
89
|
+
})] }, phase.id))) }), _jsx(Footer, { vm: vm }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "[q]uit [r]efresh" }) })] }));
|
|
90
|
+
}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* v1 grouping: a single "pipeline" phase holds every reconstruct stage as a
|
|
3
|
+
* node, in canonical stage order (the projection already lists stages in
|
|
4
|
+
* `RECONSTRUCT_STAGE_IDS` order). This keeps the adapter simple and mirrors the
|
|
5
|
+
* design's "stages are the nodes" framing; finer maturation-stage grouping can
|
|
6
|
+
* be layered on later without changing the node mapping.
|
|
7
|
+
*/
|
|
8
|
+
const PIPELINE_PHASE_ID = "pipeline";
|
|
9
|
+
const PIPELINE_PHASE_LABEL = "reconstruct pipeline";
|
|
10
|
+
/**
|
|
11
|
+
* Map a reconstruct stage `state` to the shared {@link NodeState}. Reconstruct
|
|
12
|
+
* stage states are a strict subset (`pending | completed | skipped | halted`) —
|
|
13
|
+
* there is no per-stage `running` signal in the projection, so the in-flight
|
|
14
|
+
* stage is surfaced at the phase/headline level (see {@link derivePhaseState}),
|
|
15
|
+
* not on individual nodes.
|
|
16
|
+
*/
|
|
17
|
+
function stageStateToNodeState(state) {
|
|
18
|
+
switch (state) {
|
|
19
|
+
case "completed":
|
|
20
|
+
return "completed";
|
|
21
|
+
case "skipped":
|
|
22
|
+
return "skipped";
|
|
23
|
+
case "halted":
|
|
24
|
+
return "halted";
|
|
25
|
+
case "pending":
|
|
26
|
+
return "pending";
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
/** Human-ize a snake_case stageId for display, e.g. `ontology_seed` → `ontology seed`. */
|
|
30
|
+
function humanizeStageId(stageId) {
|
|
31
|
+
return stageId.replace(/_/g, " ");
|
|
32
|
+
}
|
|
33
|
+
function stageToNode(stage, hiddenArtifactRef) {
|
|
34
|
+
const outputPath = stage.artifactRefs.find((ref) => ref !== hiddenArtifactRef) ?? null;
|
|
35
|
+
return {
|
|
36
|
+
id: stage.stageId,
|
|
37
|
+
label: humanizeStageId(stage.stageId),
|
|
38
|
+
status: stageStateToNodeState(stage.state),
|
|
39
|
+
// reconstruct nodes badge by stageId (kind) + carry the owner column.
|
|
40
|
+
kind: stage.stageId,
|
|
41
|
+
// owner is optional under exactOptionalPropertyTypes: omit when absent.
|
|
42
|
+
...(stage.owner ? { owner: stage.owner } : {}),
|
|
43
|
+
// No per-stage live signal in the projection.
|
|
44
|
+
signalAgeSec: null,
|
|
45
|
+
// `reason` is the stage's terminal explanation (skip/halt rationale).
|
|
46
|
+
failureMessage: stage.reason,
|
|
47
|
+
// First authored artifact ref is the drill-down target.
|
|
48
|
+
outputPath,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Phase state for the single pipeline phase. A halted stage halts the phase;
|
|
53
|
+
* otherwise a completed run completes the phase; otherwise it is running (the
|
|
54
|
+
* pipeline is mid-flight regardless of which individual stages are still
|
|
55
|
+
* pending).
|
|
56
|
+
*/
|
|
57
|
+
function derivePhaseState(stages, workflowStatus) {
|
|
58
|
+
if (stages.some((stage) => stage.state === "halted"))
|
|
59
|
+
return "halted";
|
|
60
|
+
if (workflowStatus === "completed")
|
|
61
|
+
return "completed";
|
|
62
|
+
return "running";
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Map record_stage + stage states to the shared {@link WorkflowStatus}.
|
|
66
|
+
* reconstruct has no `failed` record_stage and no per-stage `running`; we treat
|
|
67
|
+
* a halted stage as a halt (terminal-ish, surfaced for the operator) and a
|
|
68
|
+
* completed record as completed. Everything else is an in-flight run.
|
|
69
|
+
*/
|
|
70
|
+
function deriveWorkflowStatus(status) {
|
|
71
|
+
if (status.status === "failed")
|
|
72
|
+
return "failed";
|
|
73
|
+
if (status.status === "completed")
|
|
74
|
+
return "completed";
|
|
75
|
+
// A graceful terminal (design §16.7) is terminal but not completed: surface it as `halted` so
|
|
76
|
+
// the watch loop stops polling and the operator sees an honest stop, not a "session completed".
|
|
77
|
+
if (status.status === "blocked" || status.status === "limited")
|
|
78
|
+
return "halted";
|
|
79
|
+
if (status.progress.stages.some((stage) => stage.state === "halted")) {
|
|
80
|
+
return "halted";
|
|
81
|
+
}
|
|
82
|
+
return "running";
|
|
83
|
+
}
|
|
84
|
+
/** Flatten countSummary + answerabilitySummary into the footer label→value map. */
|
|
85
|
+
function deriveCounts(progress) {
|
|
86
|
+
const c = progress.countSummary;
|
|
87
|
+
const a = progress.answerabilitySummary;
|
|
88
|
+
return {
|
|
89
|
+
observations: c.selectedObservationCount ?? c.sourceObservationCount,
|
|
90
|
+
claims: c.semanticClaimCount,
|
|
91
|
+
confirmed: c.confirmedClaimCount,
|
|
92
|
+
deferredClaims: c.deferredClaimCount,
|
|
93
|
+
CQ: c.competencyQuestionCount,
|
|
94
|
+
passRate: c.passRate,
|
|
95
|
+
// Answerability (maturation) summary — null until metrics are computed.
|
|
96
|
+
declared: a ? a.declaredQuestionCount : null,
|
|
97
|
+
supported: a ? a.supportedQuestionCount : null,
|
|
98
|
+
deferred: a ? a.deferredQuestionCount : null,
|
|
99
|
+
unsupported: a ? a.unsupportedQuestionCount : null,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
/** One-line narrator from the current stage + the most informative counts. */
|
|
103
|
+
function deriveNarrator(progress, workflowStatus) {
|
|
104
|
+
const stage = humanizeStageId(progress.currentStageId);
|
|
105
|
+
const a = progress.answerabilitySummary;
|
|
106
|
+
const lead = workflowStatus === "completed"
|
|
107
|
+
? "reconstruct complete"
|
|
108
|
+
: workflowStatus === "failed"
|
|
109
|
+
? `reconstruct failed at ${stage}`
|
|
110
|
+
: workflowStatus === "halted"
|
|
111
|
+
? `halted at ${stage}`
|
|
112
|
+
: `reconstructing — ${stage}`;
|
|
113
|
+
if (a) {
|
|
114
|
+
return `${lead} · CQ ${a.declaredQuestionCount} · supported ${a.supportedQuestionCount} · deferred ${a.deferredQuestionCount} · unsupported ${a.unsupportedQuestionCount}`;
|
|
115
|
+
}
|
|
116
|
+
const claims = progress.countSummary.semanticClaimCount;
|
|
117
|
+
return claims === null ? lead : `${lead} · ${claims} claims`;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Pure projection: {@link ReconstructSessionStatus} → {@link TreeViewModel}.
|
|
121
|
+
*
|
|
122
|
+
* `sessionRoot` is threaded explicitly (the caller already knows it) so the view
|
|
123
|
+
* model header is independent of how the status was fetched.
|
|
124
|
+
*/
|
|
125
|
+
export function reconstructStatusToTreeViewModel(status, sessionRoot) {
|
|
126
|
+
const { progress } = status;
|
|
127
|
+
const workflowStatus = deriveWorkflowStatus(status);
|
|
128
|
+
const hiddenArtifactRef = status.status === "failed"
|
|
129
|
+
? status.failure.failure_artifact_ref
|
|
130
|
+
: null;
|
|
131
|
+
const nodes = progress.stages.map((stage) => stageToNode(stage, hiddenArtifactRef));
|
|
132
|
+
const phase = {
|
|
133
|
+
id: PIPELINE_PHASE_ID,
|
|
134
|
+
label: PIPELINE_PHASE_LABEL,
|
|
135
|
+
state: derivePhaseState(progress.stages, workflowStatus),
|
|
136
|
+
nodes,
|
|
137
|
+
};
|
|
138
|
+
return {
|
|
139
|
+
pipeline: "reconstruct",
|
|
140
|
+
sessionId: status.sessionId,
|
|
141
|
+
sessionRoot,
|
|
142
|
+
status: workflowStatus,
|
|
143
|
+
narrator: deriveNarrator(progress, workflowStatus),
|
|
144
|
+
liveness: {
|
|
145
|
+
state: progress.liveness.state,
|
|
146
|
+
// The projection carries no per-signal timestamp for reconstruct.
|
|
147
|
+
secondsSinceSignal: null,
|
|
148
|
+
pollMs: progress.liveness.recommendedPollIntervalMs,
|
|
149
|
+
},
|
|
150
|
+
phases: [phase],
|
|
151
|
+
summary: { counts: deriveCounts(progress) },
|
|
152
|
+
// reconstruct status carries no rich run_control (unlike review): be
|
|
153
|
+
// conservative — only a still-running session is cancellable; continue /
|
|
154
|
+
// advance are display-only false until a control path exists (design §4,
|
|
155
|
+
// later stage E).
|
|
156
|
+
runControl: {
|
|
157
|
+
cancellable: workflowStatus === "running",
|
|
158
|
+
continuable: false,
|
|
159
|
+
advanceable: false,
|
|
160
|
+
},
|
|
161
|
+
};
|
|
162
|
+
}
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
import { REVIEW_PROGRESS_STEPS, } from "../../core-api/review-progress.js";
|
|
2
|
+
/** Phase that collects units the projection has not bound to a progress step. */
|
|
3
|
+
const UNCATEGORIZED_PHASE_ID = "unassigned";
|
|
4
|
+
const UNCATEGORIZED_PHASE_LABEL = "unassigned units";
|
|
5
|
+
/** Canonical progress-step ids — used to reject non-step `completed_steps` keys. */
|
|
6
|
+
const VALID_PROGRESS_STEP_IDS = new Set(REVIEW_PROGRESS_STEPS.map((step) => step.id));
|
|
7
|
+
function isRecord(value) {
|
|
8
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
9
|
+
}
|
|
10
|
+
/** Narrow the `unknown` presentation payload to the read-only fields we use. */
|
|
11
|
+
function readProgressPresentationInput(status) {
|
|
12
|
+
const input = status.llmPresentation?.progress?.input;
|
|
13
|
+
return isRecord(input) ? input : {};
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Map a runtime unit status to the shared {@link NodeState}. `retrying` and
|
|
17
|
+
* `running_stale` are both still in-flight (the HUD surfaces the retry count /
|
|
18
|
+
* stale signal age separately), so they collapse to `running`.
|
|
19
|
+
*/
|
|
20
|
+
function unitStatusToNodeState(unitStatus) {
|
|
21
|
+
switch (unitStatus) {
|
|
22
|
+
case "pending":
|
|
23
|
+
return "pending";
|
|
24
|
+
case "running":
|
|
25
|
+
case "retrying":
|
|
26
|
+
case "running_stale":
|
|
27
|
+
return "running";
|
|
28
|
+
case "completed":
|
|
29
|
+
return "completed";
|
|
30
|
+
case "failed":
|
|
31
|
+
return "failed";
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
function unitToNode(unit) {
|
|
35
|
+
return {
|
|
36
|
+
id: unit.unitId,
|
|
37
|
+
label: unit.publicAlias,
|
|
38
|
+
status: unitStatusToNodeState(unit.status),
|
|
39
|
+
kind: unit.unitKind,
|
|
40
|
+
signalAgeSec: unit.secondsSinceLatestSignal,
|
|
41
|
+
attempts: unit.attemptCount,
|
|
42
|
+
failureMessage: unit.failureMessage,
|
|
43
|
+
outputPath: drillDownPath(unit),
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Drill-down target for a unit. A completed unit's authored output is the
|
|
48
|
+
* authoritative artifact. For an active/failed unit that planned output may not
|
|
49
|
+
* exist yet, so prefer the live running-log (the projection only sets
|
|
50
|
+
* `runningLogRef` when that file is present) and fall back to the planned output.
|
|
51
|
+
*/
|
|
52
|
+
function drillDownPath(unit) {
|
|
53
|
+
if (unit.status === "completed") {
|
|
54
|
+
return unit.outputPath ?? unit.runningLogRef ?? null;
|
|
55
|
+
}
|
|
56
|
+
return unit.runningLogRef ?? unit.outputPath ?? null;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* State of the current (in-flight) progress step, given the workflow's own
|
|
60
|
+
* status: a halted or failed run is terminal, and its current step is the
|
|
61
|
+
* halt/failure locus — surface that rather than a misleading "running". A live
|
|
62
|
+
* run's current step is running.
|
|
63
|
+
*/
|
|
64
|
+
function currentStepState(workflowStatus) {
|
|
65
|
+
if (workflowStatus === "failed")
|
|
66
|
+
return "failed";
|
|
67
|
+
if (workflowStatus === "halted")
|
|
68
|
+
return "halted";
|
|
69
|
+
return "running";
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Phase state from its nodes (+ the projection's completed step ids). A failed
|
|
73
|
+
* node fails the phase; any in-flight node makes it running; a step listed in
|
|
74
|
+
* `completed_steps` (or all nodes completed) completes it; a halted node halts
|
|
75
|
+
* it. A step at the current step whose units exist but have not signalled yet
|
|
76
|
+
* takes the workflow's current-step state — matching {@link emptyStepState}'s
|
|
77
|
+
* current-step branch so the same current step reports the same state whether or
|
|
78
|
+
* not it carries units. Otherwise it is pending.
|
|
79
|
+
*/
|
|
80
|
+
function derivePhaseState(nodes, stepId, completedStepIds, stepNumber, currentStep, workflowStatus) {
|
|
81
|
+
if (nodes.some((node) => node.status === "failed"))
|
|
82
|
+
return "failed";
|
|
83
|
+
if (nodes.some((node) => node.status === "running"))
|
|
84
|
+
return "running";
|
|
85
|
+
if (nodes.some((node) => node.status === "halted"))
|
|
86
|
+
return "halted";
|
|
87
|
+
if (stepId !== null && completedStepIds.has(stepId))
|
|
88
|
+
return "completed";
|
|
89
|
+
if (nodes.length > 0 && nodes.every((node) => node.status === "completed")) {
|
|
90
|
+
return "completed";
|
|
91
|
+
}
|
|
92
|
+
if (nodes.length > 0 && nodes.every((node) => node.status === "skipped")) {
|
|
93
|
+
return "skipped";
|
|
94
|
+
}
|
|
95
|
+
if (stepNumber != null && currentStep != null && stepNumber === currentStep) {
|
|
96
|
+
return currentStepState(workflowStatus);
|
|
97
|
+
}
|
|
98
|
+
return "pending";
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* State for a progress step that has no bound units: completed when the workflow
|
|
102
|
+
* itself completed, when the step is in `completed_steps`, or when it sits before
|
|
103
|
+
* the current step; the workflow's current-step state at the current step (so a
|
|
104
|
+
* halted/failed run points at its halt/failure locus, not a phantom "running");
|
|
105
|
+
* otherwise pending. This keeps idle/completed steps visible so the HUD shows the
|
|
106
|
+
* whole pipeline (not only the steps that happened to carry units).
|
|
107
|
+
*/
|
|
108
|
+
function emptyStepState(stepId, stepNumber, completedStepIds, currentStep, workflowStatus) {
|
|
109
|
+
if (workflowStatus === "completed")
|
|
110
|
+
return "completed";
|
|
111
|
+
if (completedStepIds.has(stepId))
|
|
112
|
+
return "completed";
|
|
113
|
+
if (currentStep != null) {
|
|
114
|
+
if (stepNumber < currentStep)
|
|
115
|
+
return "completed";
|
|
116
|
+
if (stepNumber === currentStep)
|
|
117
|
+
return currentStepState(workflowStatus);
|
|
118
|
+
}
|
|
119
|
+
return "pending";
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Emit a phase for EVERY progress step (in canonical order), attaching any units
|
|
123
|
+
* bound to it, so completed/idle steps stay visible. Units the projection left
|
|
124
|
+
* unbound (`progressStepId === null`) collect into a trailing uncategorized phase.
|
|
125
|
+
*/
|
|
126
|
+
function derivePhases(status, completedStepIds, currentStep, workflowStatus) {
|
|
127
|
+
const byStep = new Map();
|
|
128
|
+
for (const unit of status.unitProgress ?? []) {
|
|
129
|
+
const key = unit.progressStepId;
|
|
130
|
+
const bucket = byStep.get(key);
|
|
131
|
+
if (bucket)
|
|
132
|
+
bucket.push(unitToNode(unit));
|
|
133
|
+
else
|
|
134
|
+
byStep.set(key, [unitToNode(unit)]);
|
|
135
|
+
}
|
|
136
|
+
const phases = REVIEW_PROGRESS_STEPS.map((spec) => {
|
|
137
|
+
const nodes = byStep.get(spec.id) ?? [];
|
|
138
|
+
const state = nodes.length > 0
|
|
139
|
+
? derivePhaseState(nodes, spec.id, completedStepIds, spec.step, currentStep, workflowStatus)
|
|
140
|
+
: emptyStepState(spec.id, spec.step, completedStepIds, currentStep, workflowStatus);
|
|
141
|
+
return { id: spec.id, label: spec.label, state, nodes };
|
|
142
|
+
});
|
|
143
|
+
const unassigned = byStep.get(null);
|
|
144
|
+
if (unassigned && unassigned.length > 0) {
|
|
145
|
+
phases.push({
|
|
146
|
+
id: UNCATEGORIZED_PHASE_ID,
|
|
147
|
+
label: UNCATEGORIZED_PHASE_LABEL,
|
|
148
|
+
state: derivePhaseState(unassigned, null, completedStepIds, null, currentStep, workflowStatus),
|
|
149
|
+
nodes: unassigned,
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
return phases;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Map the review status union to the shared {@link WorkflowStatus}. A degraded
|
|
156
|
+
* completion is still a completion; `halted_partial` is the operator-facing halt;
|
|
157
|
+
* `prepared` / `unknown` are pre-run states the HUD shows as still-running (the
|
|
158
|
+
* narrator/liveness convey the finer state).
|
|
159
|
+
*/
|
|
160
|
+
function deriveWorkflowStatus(status) {
|
|
161
|
+
switch (status) {
|
|
162
|
+
case "completed":
|
|
163
|
+
case "completed_with_degradation":
|
|
164
|
+
return "completed";
|
|
165
|
+
case "halted_partial":
|
|
166
|
+
return "halted";
|
|
167
|
+
case "failed":
|
|
168
|
+
return "failed";
|
|
169
|
+
case "running":
|
|
170
|
+
case "prepared":
|
|
171
|
+
case "unknown":
|
|
172
|
+
return "running";
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Run control from the review projection. `cancellationAvailable` /
|
|
177
|
+
* `continuationAvailable` are the authoritative flags; review has no per-round
|
|
178
|
+
* advance, so `advanceable` is left undefined.
|
|
179
|
+
*/
|
|
180
|
+
function deriveRunControl(runControl) {
|
|
181
|
+
return {
|
|
182
|
+
cancellable: runControl?.cancellationAvailable ?? false,
|
|
183
|
+
continuable: runControl?.continuationAvailable ?? false,
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
const EMPTY_SEVERITY_COUNTS = {
|
|
187
|
+
blocker: 0,
|
|
188
|
+
high: 0,
|
|
189
|
+
medium: 0,
|
|
190
|
+
low: 0,
|
|
191
|
+
info: 0,
|
|
192
|
+
};
|
|
193
|
+
/**
|
|
194
|
+
* Footer findings: severity counts + material issue titles. Works for both the
|
|
195
|
+
* full and compact result-classification projections — material titles come from
|
|
196
|
+
* `material_issues[]` (full) or `material_issue_signals[]` (compact).
|
|
197
|
+
*/
|
|
198
|
+
function deriveFindings(summary) {
|
|
199
|
+
if (!summary) {
|
|
200
|
+
return { ...EMPTY_SEVERITY_COUNTS, material: [] };
|
|
201
|
+
}
|
|
202
|
+
const counts = {
|
|
203
|
+
blocker: summary.severity_counts.blocker,
|
|
204
|
+
high: summary.severity_counts.high,
|
|
205
|
+
medium: summary.severity_counts.medium,
|
|
206
|
+
low: summary.severity_counts.low,
|
|
207
|
+
info: summary.severity_counts.info,
|
|
208
|
+
};
|
|
209
|
+
return { ...counts, material: materialIssueTitles(summary) };
|
|
210
|
+
}
|
|
211
|
+
function isCompactSummary(summary) {
|
|
212
|
+
return "material_issue_signals" in summary;
|
|
213
|
+
}
|
|
214
|
+
function materialIssueTitles(summary) {
|
|
215
|
+
if (isCompactSummary(summary)) {
|
|
216
|
+
return summary.material_issue_signals.map((signal) => titleLine(signal.issue_id, signal.signal));
|
|
217
|
+
}
|
|
218
|
+
return summary.material_issues.map((issue) => titleLine(issue.issue_id, issue.problem_definition ?? issue.issue_statement ?? issue.impact));
|
|
219
|
+
}
|
|
220
|
+
function titleLine(issueId, text) {
|
|
221
|
+
const trimmed = (text ?? "").trim();
|
|
222
|
+
return trimmed.length > 0 ? `${issueId}: ${trimmed}` : issueId;
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Narrator line: prefer the latest progress signal summary, fall back to the
|
|
226
|
+
* liveness summary, then to the bare status. Mirrors the design's
|
|
227
|
+
* `latest_update.summary / liveness.summary` rule.
|
|
228
|
+
*/
|
|
229
|
+
function deriveNarrator(input, workflowStatus) {
|
|
230
|
+
const latest = input.latest_update?.summary?.trim();
|
|
231
|
+
if (latest)
|
|
232
|
+
return latest;
|
|
233
|
+
const liveness = input.liveness?.summary?.trim();
|
|
234
|
+
if (liveness)
|
|
235
|
+
return liveness;
|
|
236
|
+
return `review ${workflowStatus}`;
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Pure projection: {@link ReviewStatus} → {@link TreeViewModel}.
|
|
240
|
+
*
|
|
241
|
+
* `sessionRoot` is threaded explicitly (the caller already knows it) so the view
|
|
242
|
+
* model header is independent of how the status was fetched.
|
|
243
|
+
*/
|
|
244
|
+
export function reviewStatusToTreeViewModel(status, sessionRoot) {
|
|
245
|
+
const input = readProgressPresentationInput(status);
|
|
246
|
+
const workflowStatus = deriveWorkflowStatus(status.status);
|
|
247
|
+
// Consume only real progress-step ids: a prepared session fills
|
|
248
|
+
// `completed_steps` with preparation-artifact keys (interpretation/binding/
|
|
249
|
+
// execution_plan), a different vocabulary, which must not be matched against
|
|
250
|
+
// (or mistaken for) progress-step ids during phase rollup.
|
|
251
|
+
const completedStepIds = new Set((input.progress?.completed_steps ?? []).filter((id) => VALID_PROGRESS_STEP_IDS.has(id)));
|
|
252
|
+
const phases = derivePhases(status, completedStepIds, input.progress?.current_step ?? null, workflowStatus);
|
|
253
|
+
return {
|
|
254
|
+
pipeline: "review",
|
|
255
|
+
sessionId: status.sessionId,
|
|
256
|
+
sessionRoot,
|
|
257
|
+
status: workflowStatus,
|
|
258
|
+
narrator: deriveNarrator(input, workflowStatus),
|
|
259
|
+
liveness: {
|
|
260
|
+
state: input.liveness?.state ?? "unknown",
|
|
261
|
+
secondsSinceSignal: input.liveness?.seconds_since_last_observed_artifact ?? null,
|
|
262
|
+
pollMs: input.liveness?.poll_after_seconds == null
|
|
263
|
+
? null
|
|
264
|
+
: input.liveness.poll_after_seconds * 1000,
|
|
265
|
+
},
|
|
266
|
+
phases,
|
|
267
|
+
summary: { findings: deriveFindings(input.result_classification_summary) },
|
|
268
|
+
runControl: deriveRunControl(status.runControl),
|
|
269
|
+
};
|
|
270
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pipeline-agnostic view model rendered by the `onto watch` HUD.
|
|
3
|
+
*
|
|
4
|
+
* review and reconstruct expose progress in different shapes (review = per-UNIT
|
|
5
|
+
* live signal; reconstruct = per-STAGE state + owner + domain counts). The
|
|
6
|
+
* review/reconstruct adapters normalize both into this single shape, so the Ink
|
|
7
|
+
* HUD never branches on pipeline. This is a pure derived VIEW — it carries no
|
|
8
|
+
* authority and is never persisted.
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* True when the workflow has reached a terminal state (stop polling). `halted`
|
|
12
|
+
* is terminal for the watch loop too: it sits paused awaiting an explicit
|
|
13
|
+
* operator continuation that may be a long way off, so the live loop stops
|
|
14
|
+
* rather than re-reading an unchanging projection every interval. The operator
|
|
15
|
+
* re-engages with [r]efresh after continuing (consistent with completed/failed).
|
|
16
|
+
*/
|
|
17
|
+
export function isTerminalStatus(status) {
|
|
18
|
+
return status === "completed" || status === "failed" || status === "halted";
|
|
19
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "onto-mcp",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.13",
|
|
4
4
|
"description": "MCP-native ontology review runtime with context-isolated lenses and controlled deliberation",
|
|
5
5
|
"homepage": "https://github.com/kangminlee-maker/onto-mcp#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -49,10 +49,17 @@
|
|
|
49
49
|
"check:invariant-change": "tsx scripts/check-invariant-change-marker.ts",
|
|
50
50
|
"check:invariant-drift": "tsx scripts/check-invariant-drift.ts",
|
|
51
51
|
"check:supported-models": "tsx scripts/check-supported-models.ts",
|
|
52
|
+
"check:prompt-projection-parity": "tsx scripts/check-prompt-projection-parity.ts",
|
|
53
|
+
"check:final-output-sections-parity": "tsx scripts/check-final-output-sections-parity.ts",
|
|
54
|
+
"check:obligation-coverage": "tsx scripts/check-obligation-coverage.ts",
|
|
55
|
+
"check:dispatch-fallback-package-parity": "tsx scripts/check-dispatch-fallback-package-parity.ts",
|
|
56
|
+
"check:graceful-signal-rethrow": "tsx scripts/check-graceful-signal-rethrow.ts",
|
|
52
57
|
"test": "npm run test:e2e",
|
|
53
58
|
"test:vitest": "vitest run",
|
|
54
59
|
"test:review:live": "npm run build:ts-core && tsx scripts/review-mcp-live-e2e.ts",
|
|
55
60
|
"test:review:live:cli": "npm run build:ts-core && tsx scripts/review-live-e2e.ts",
|
|
61
|
+
"test:reconstruct:dispatch-fallback:live": "node --env-file=.env --import tsx scripts/reconstruct-dispatch-fallback-live-e2e.mts",
|
|
62
|
+
"test:reconstruct:output-headroom:live": "node --env-file=.env --import tsx scripts/reconstruct-output-headroom-live-e2e.mts",
|
|
56
63
|
"benchmark:review:pipeline": "tsx scripts/review-pipeline-benchmark.ts",
|
|
57
64
|
"benchmark:reconstruct:pipeline": "tsx scripts/reconstruct-pipeline-benchmark.ts",
|
|
58
65
|
"calibration:effort:report": "tsx scripts/effort-calibration-report.ts",
|
|
@@ -60,13 +67,21 @@
|
|
|
60
67
|
"test:e2e": "npm run build:ts-core && tsx scripts/review-mcp-live-e2e.ts"
|
|
61
68
|
},
|
|
62
69
|
"dependencies": {
|
|
63
|
-
"@anthropic-ai/sdk": "
|
|
64
|
-
"
|
|
70
|
+
"@anthropic-ai/sdk": "0.99.0",
|
|
71
|
+
"fflate": "^0.8.3",
|
|
72
|
+
"ink": "^5.2.1",
|
|
73
|
+
"openai": "6.39.0",
|
|
74
|
+
"proper-lockfile": "^4.1.2",
|
|
75
|
+
"react": "^18.3.1",
|
|
76
|
+
"saxes": "^6.0.0",
|
|
65
77
|
"yaml": "^2.8.2",
|
|
66
78
|
"zod": "^4.3.6"
|
|
67
79
|
},
|
|
68
80
|
"devDependencies": {
|
|
69
81
|
"@types/node": "^25.3.5",
|
|
82
|
+
"@types/proper-lockfile": "^4.1.4",
|
|
83
|
+
"@types/react": "^18.3.31",
|
|
84
|
+
"ink-testing-library": "^4.0.0",
|
|
70
85
|
"tsx": "^4.21.0",
|
|
71
86
|
"typescript": "^5.9.3",
|
|
72
87
|
"vitest": "^4.0.18"
|