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
|
@@ -5,7 +5,7 @@ import fs from "node:fs/promises";
|
|
|
5
5
|
import os from "node:os";
|
|
6
6
|
import path from "node:path";
|
|
7
7
|
import { resolveOntoHome } from "../core-runtime/discovery/onto-home.js";
|
|
8
|
-
import { defaultReviewRetrySettings, resolveSettingsChain, } from "../core-runtime/discovery/settings-chain.js";
|
|
8
|
+
import { completeDispatchBreakerSettings, defaultReviewRetrySettings, resolveSettingsChain, } from "../core-runtime/discovery/settings-chain.js";
|
|
9
9
|
import { loadCoreLensRegistry } from "../core-runtime/discovery/lens-registry.js";
|
|
10
10
|
import { fileExists, isDeprecatedDomainAlias, isoFromTimestamp, isoNow, normalizeDomainValue, readYamlDocument, writeYamlDocument, } from "../core-runtime/review/review-artifact-utils.js";
|
|
11
11
|
import { assertPathInsideRoot, realpathIfExists, } from "../core-runtime/path-boundary.js";
|
|
@@ -29,6 +29,7 @@ import { appendRuntimeStatusEventSync, } from "../core-runtime/observability/run
|
|
|
29
29
|
import { buildReviewPipelineExecutionLedger, } from "../core-runtime/review/pipeline-execution-ledger.js";
|
|
30
30
|
import { buildReviewContinuationPlan, } from "../core-runtime/review/continuation-plan.js";
|
|
31
31
|
import { reviewAdvance as coreReviewAdvance, reviewRound as coreReviewRound, } from "../core-runtime/review/review-execution-steps.js";
|
|
32
|
+
import { dispatchIncompleteArtifactPath } from "../core-runtime/llm/dispatch-breaker.js";
|
|
32
33
|
export class ReviewContinuationError extends Error {
|
|
33
34
|
failureContent;
|
|
34
35
|
originalError;
|
|
@@ -1228,6 +1229,7 @@ function buildHaltPresentation(input) {
|
|
|
1228
1229
|
prompt: [
|
|
1229
1230
|
"Render a concise halted-partial onto review update from bounded runtime facts.",
|
|
1230
1231
|
"Use only the provided input facts. Lead with halt identity, produced artifacts, absent artifacts, and available action candidates.",
|
|
1232
|
+
"When run_control.continuationAvailable is true, present the default recovery as calling onto_review_continue with the same sessionRoot: it resumes the session, reuses already-completed units, and reruns only the failed/missing frontier — prefer it over restarting a new review. This operational resume is distinct from the continue_review action-candidate, which means expanding the review's evidence boundary.",
|
|
1231
1233
|
"Do not present partial findings as a completed review. Use the user's conversation language.",
|
|
1232
1234
|
].join("\n"),
|
|
1233
1235
|
input,
|
|
@@ -1511,12 +1513,28 @@ async function deriveRuntimeUnitProgress(args) {
|
|
|
1511
1513
|
unitKind: "lens",
|
|
1512
1514
|
packetPath,
|
|
1513
1515
|
outputPath: seat.output_path,
|
|
1516
|
+
sidecarOutputPath: seat.sidecar_output_path ?? null,
|
|
1514
1517
|
runningLogRef: path.join(path.dirname(seat.output_path), `.${seat.lens_id}.running.log`),
|
|
1515
1518
|
};
|
|
1516
1519
|
});
|
|
1517
1520
|
const projections = [];
|
|
1518
1521
|
for (const unit of lensUnits) {
|
|
1519
|
-
|
|
1522
|
+
// In sidecar mode (review.artifacts.write_lens_markdown=false) the authored
|
|
1523
|
+
// `.md` is never written and the lens's real artifact is the findings
|
|
1524
|
+
// sidecar (`.findings.yaml`). Resolve the unit's output to whichever artifact
|
|
1525
|
+
// actually exists, preferring the authored `.md`, so drill-down and the
|
|
1526
|
+
// output-presence completion signal see the real file instead of an absent
|
|
1527
|
+
// planned path.
|
|
1528
|
+
const authoredOutput = await observeFile(unit.outputPath);
|
|
1529
|
+
let output = authoredOutput;
|
|
1530
|
+
let outputPath = unit.outputPath;
|
|
1531
|
+
if (!authoredOutput.exists && unit.sidecarOutputPath) {
|
|
1532
|
+
const sidecar = await observeFile(unit.sidecarOutputPath);
|
|
1533
|
+
if (sidecar.exists) {
|
|
1534
|
+
output = sidecar;
|
|
1535
|
+
outputPath = unit.sidecarOutputPath;
|
|
1536
|
+
}
|
|
1537
|
+
}
|
|
1520
1538
|
const runningLog = await observeFile(unit.runningLogRef);
|
|
1521
1539
|
const signals = signalsByUnit.get(unit.unitId) ?? [];
|
|
1522
1540
|
const latestSignal = latestRuntimeSignal(signals);
|
|
@@ -1579,7 +1597,7 @@ async function deriveRuntimeUnitProgress(args) {
|
|
|
1579
1597
|
progressStepId: runtimeUnitStepId(unit.unitKind),
|
|
1580
1598
|
status,
|
|
1581
1599
|
packetPath: unit.packetPath,
|
|
1582
|
-
outputPath
|
|
1600
|
+
outputPath,
|
|
1583
1601
|
runningLogRef: runningLog.exists ? unit.runningLogRef : null,
|
|
1584
1602
|
latestSignal: latestSignalName,
|
|
1585
1603
|
latestSignalAt,
|
|
@@ -2156,7 +2174,10 @@ function reviewExecutionHostFromRoute(args) {
|
|
|
2156
2174
|
return args.modelProvider;
|
|
2157
2175
|
return reviewExecutionHostFromRuntime(args.hostRuntime, args.workerExecutor);
|
|
2158
2176
|
}
|
|
2159
|
-
|
|
2177
|
+
/** Exported for tests: pins the continue-path round-trip contract — every
|
|
2178
|
+
* opt-in stamped in the manifest retry policy (salvage, resubmit) must
|
|
2179
|
+
* survive profile reconstruction, or a resumed run silently loses it. */
|
|
2180
|
+
export function reviewRetrySettingsFromUnknown(value) {
|
|
2160
2181
|
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
2161
2182
|
return null;
|
|
2162
2183
|
const record = value;
|
|
@@ -2201,7 +2222,47 @@ function reviewRetrySettingsFromUnknown(value) {
|
|
|
2201
2222
|
...(transcription !== undefined ? { transcription_llm: transcription } : {}),
|
|
2202
2223
|
delta_completion: "unit_llm",
|
|
2203
2224
|
};
|
|
2204
|
-
|
|
2225
|
+
// Round-trip the resubmit opt-in for the same reason as salvage: a
|
|
2226
|
+
// resubmit-enabled run resumed via continue must not silently revert to
|
|
2227
|
+
// the whole-run-halt promotion rule (artifacts stamped before the field
|
|
2228
|
+
// existed resume with resubmit disabled — conservative).
|
|
2229
|
+
const resubmitRecord = record.resubmit &&
|
|
2230
|
+
typeof record.resubmit === "object" &&
|
|
2231
|
+
!Array.isArray(record.resubmit)
|
|
2232
|
+
? record.resubmit
|
|
2233
|
+
: undefined;
|
|
2234
|
+
const resubmit = {
|
|
2235
|
+
enabled: resubmitRecord?.enabled === true,
|
|
2236
|
+
};
|
|
2237
|
+
// Round-trip the dispatch breaker for the same reason: the canonical
|
|
2238
|
+
// recovery flow for a TRIPPED batch is onto_review_continue during the
|
|
2239
|
+
// same outage — dropping the key here would resume the incomplete set
|
|
2240
|
+
// with the breaker silently OFF (the exact retry-storm class 설계 B
|
|
2241
|
+
// prevents). Artifacts stamped before the field existed resume with the
|
|
2242
|
+
// breaker disabled — conservative (completeDispatchBreakerSettings
|
|
2243
|
+
// defaults enabled:false).
|
|
2244
|
+
const breakerRecord = record.dispatch_breaker &&
|
|
2245
|
+
typeof record.dispatch_breaker === "object" &&
|
|
2246
|
+
!Array.isArray(record.dispatch_breaker)
|
|
2247
|
+
? record.dispatch_breaker
|
|
2248
|
+
: undefined;
|
|
2249
|
+
const breakerNumber = (item, min) => typeof item === "number" && Number.isInteger(item) && item >= min
|
|
2250
|
+
? item
|
|
2251
|
+
: undefined;
|
|
2252
|
+
const breakerInput = {
|
|
2253
|
+
enabled: breakerRecord?.enabled === true,
|
|
2254
|
+
systemic_threshold: breakerNumber(breakerRecord?.systemic_threshold, 1),
|
|
2255
|
+
per_call_max_attempts: breakerNumber(breakerRecord?.per_call_max_attempts, 1),
|
|
2256
|
+
backoff_initial_ms: breakerNumber(breakerRecord?.backoff_initial_ms, 0),
|
|
2257
|
+
backoff_cap_ms: breakerNumber(breakerRecord?.backoff_cap_ms, 0),
|
|
2258
|
+
};
|
|
2259
|
+
const dispatchBreaker = completeDispatchBreakerSettings(breakerInput);
|
|
2260
|
+
return {
|
|
2261
|
+
...retry,
|
|
2262
|
+
salvage,
|
|
2263
|
+
resubmit,
|
|
2264
|
+
dispatch_breaker: dispatchBreaker,
|
|
2265
|
+
};
|
|
2205
2266
|
}
|
|
2206
2267
|
function reviewExecutionProfileFromManifest(manifest) {
|
|
2207
2268
|
const profile = manifest?.review_execution_profile;
|
|
@@ -2480,6 +2541,10 @@ function continuationSessionArtifactRefs(args) {
|
|
|
2480
2541
|
args.executionPlan.deliberation_output_path,
|
|
2481
2542
|
args.executionPlan.final_output_path,
|
|
2482
2543
|
args.executionPlan.review_record_path,
|
|
2544
|
+
// 설계 B: 트립 배치의 회복 집합 기록 — continuation이 breaker-ON 배치를
|
|
2545
|
+
// 재실행하면 덮어써지므로, 직전 상태를 continuation-attempts/에 백업해
|
|
2546
|
+
// 어느 트립 상태를 회복했는지 감사 가능하게 남긴다.
|
|
2547
|
+
dispatchIncompleteArtifactPath(args.sessionRoot),
|
|
2483
2548
|
];
|
|
2484
2549
|
}
|
|
2485
2550
|
async function resolveContinuationRequestText(args) {
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* core-api re-export of the review progress-step contract (the canonical step
|
|
3
|
+
* ids, ordered specs, and label lookup that review-api itself uses), so
|
|
4
|
+
* consumers like the `onto watch` TUI reuse the same label authority through
|
|
5
|
+
* core-api instead of reaching into core-runtime/review or duplicating labels.
|
|
6
|
+
*/
|
|
7
|
+
export { REVIEW_PROGRESS_STEPS, reviewProgressStepById, } from "../core-runtime/review/review-progress-contract.js";
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* core-api re-export of the runtime event-stream surface, so consumers (the
|
|
3
|
+
* `onto watch` TUI) depend on core-api only — never reaching into
|
|
4
|
+
* core-runtime/observability directly. The event log is the append-only
|
|
5
|
+
* `runtime-events.ndjson` produced by every review/reconstruct session.
|
|
6
|
+
*/
|
|
7
|
+
export { runtimeStreamEventLogPath, } from "../core-runtime/observability/runtime-stream-observation.js";
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { constants as fsConstants } from "node:fs";
|
|
1
2
|
import fs from "node:fs/promises";
|
|
2
3
|
import path from "node:path";
|
|
3
4
|
import { stringify as stringifyYaml } from "yaml";
|
|
@@ -35,13 +36,84 @@ export async function atomicWriteFile(filePath, contents) {
|
|
|
35
36
|
throw error;
|
|
36
37
|
}
|
|
37
38
|
}
|
|
39
|
+
async function fsyncDirectory(directoryPath) {
|
|
40
|
+
const handle = await fs.open(directoryPath, fsConstants.O_RDONLY);
|
|
41
|
+
try {
|
|
42
|
+
await handle.sync();
|
|
43
|
+
}
|
|
44
|
+
finally {
|
|
45
|
+
await handle.close();
|
|
46
|
+
}
|
|
47
|
+
}
|
|
38
48
|
/**
|
|
39
|
-
*
|
|
40
|
-
*
|
|
49
|
+
* Atomic write with crash-durable file and directory metadata. Use for small
|
|
50
|
+
* control-plane authorities whose rename must survive a process or host crash.
|
|
51
|
+
*/
|
|
52
|
+
export async function durableAtomicWriteFile(filePath, contents) {
|
|
53
|
+
const directoryPath = path.dirname(filePath);
|
|
54
|
+
await fs.mkdir(directoryPath, { recursive: true });
|
|
55
|
+
tempWriteCounter += 1;
|
|
56
|
+
const tempPath = `${filePath}.${process.pid}.${tempWriteCounter}.durable.tmp`;
|
|
57
|
+
let handle = null;
|
|
58
|
+
try {
|
|
59
|
+
handle = await fs.open(tempPath, fsConstants.O_CREAT |
|
|
60
|
+
fsConstants.O_EXCL |
|
|
61
|
+
fsConstants.O_WRONLY |
|
|
62
|
+
fsConstants.O_NOFOLLOW, 0o600);
|
|
63
|
+
await handle.writeFile(contents, "utf8");
|
|
64
|
+
await handle.sync();
|
|
65
|
+
await handle.close();
|
|
66
|
+
handle = null;
|
|
67
|
+
await fs.rename(tempPath, filePath);
|
|
68
|
+
await fsyncDirectory(directoryPath);
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
await handle?.close().catch(() => undefined);
|
|
72
|
+
await fs.rm(tempPath, { force: true });
|
|
73
|
+
throw error;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Fields stamped on in-memory artifacts for same-process consumers but NEVER persisted. Persisting one
|
|
78
|
+
* changes the on-disk bytes, which feeds reuse-provenance: the scout-pack snapshot hashes the raw
|
|
79
|
+
* validation-file bytes via `sha256File`, so a persisted field rotates that snapshot (the
|
|
80
|
+
* `reuseMatchArtifactHash` in-memory digest is neutralized separately, in run.ts
|
|
81
|
+
* `stripVolatileArtifactFields`). The only such field today is the G(a) obligation-coverage telemetry
|
|
82
|
+
* (`asserted_obligation_ids`): validators stamp it and the coverage harvest reads it off the in-memory
|
|
83
|
+
* return value — no consumer reads it from disk. Keeping it out of persistence lets the reuse-hashed /
|
|
84
|
+
* scout-captured validators be instrumented with zero hash rotation and no resume migration.
|
|
85
|
+
* See development-records/design/20260623-deferred7-obligation-telemetry-design.md.
|
|
86
|
+
*/
|
|
87
|
+
const IN_MEMORY_ONLY_ARTIFACT_FIELDS = ["asserted_obligation_ids"];
|
|
88
|
+
/**
|
|
89
|
+
* Return `value` with any in-memory-only telemetry fields dropped from the top level. Top-level only by
|
|
90
|
+
* design: validation artifacts carry these fields at top level and the reuse channels only hash
|
|
91
|
+
* standalone validation files, so a recursive walk would add an O(size) cost on every large artifact
|
|
92
|
+
* write (e.g. a 190K-row workbook inventory) for no benefit. Returns the input unchanged (no copy) when
|
|
93
|
+
* no such field is present — the common case for every non-validation artifact.
|
|
94
|
+
*/
|
|
95
|
+
export function stripInMemoryOnlyArtifactFields(value) {
|
|
96
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
97
|
+
return value;
|
|
98
|
+
const record = value;
|
|
99
|
+
if (!IN_MEMORY_ONLY_ARTIFACT_FIELDS.some((key) => key in record))
|
|
100
|
+
return value;
|
|
101
|
+
const persisted = { ...record };
|
|
102
|
+
for (const key of IN_MEMORY_ONLY_ARTIFACT_FIELDS)
|
|
103
|
+
delete persisted[key];
|
|
104
|
+
return persisted;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Serialize `value` to YAML and write it atomically, dropping in-memory-only telemetry fields
|
|
108
|
+
* (see `stripInMemoryOnlyArtifactFields`). Byte-for-byte identical to a direct `stringifyYaml(value)`
|
|
109
|
+
* write for any value without those fields — only the write mechanism (and the telemetry omission)
|
|
41
110
|
* changes.
|
|
42
111
|
*/
|
|
43
112
|
export async function atomicWriteYamlDocument(filePath, value) {
|
|
44
|
-
await atomicWriteFile(filePath, stringifyYaml(value));
|
|
113
|
+
await atomicWriteFile(filePath, stringifyYaml(stripInMemoryOnlyArtifactFields(value)));
|
|
114
|
+
}
|
|
115
|
+
export async function durableAtomicWriteYamlDocument(filePath, value) {
|
|
116
|
+
await durableAtomicWriteFile(filePath, stringifyYaml(stripInMemoryOnlyArtifactFields(value)));
|
|
45
117
|
}
|
|
46
118
|
/**
|
|
47
119
|
* Fail-closed shape guard for trusted artifact reads. The pipeline reads its
|
|
@@ -30,9 +30,10 @@ import { SALVAGE_INCOMPLETE_SENTINEL, buildDeltaRowsSalvagePrompt, buildTranscri
|
|
|
30
30
|
const CLAUDE_READONLY_ALLOWED_TOOLS = ["Read", "Grep", "Glob"];
|
|
31
31
|
const CLAUDE_BIN = resolveClaudeBin();
|
|
32
32
|
/**
|
|
33
|
-
* Embed the submit-tool JSON Schema into the bounded prompt
|
|
34
|
-
*
|
|
35
|
-
*
|
|
33
|
+
* Embed the submit-tool JSON Schema into the bounded prompt as a redundant
|
|
34
|
+
* semantic hint. The same schema is also passed through Claude Code's
|
|
35
|
+
* `--json-schema` flag when available; the submit tool remains the artifact
|
|
36
|
+
* validator.
|
|
36
37
|
*/
|
|
37
38
|
function appendSchemaToPrompt(boundedPrompt, schema) {
|
|
38
39
|
if (schema === undefined)
|
|
@@ -192,6 +193,7 @@ async function runSubmitSalvageMode(args) {
|
|
|
192
193
|
process.stderr.write(`[plan:executor] kind=claude_code unit_id=${args.unitId} salvage=${mode.mode}\n`);
|
|
193
194
|
const workerBase = {
|
|
194
195
|
projectRoot: args.projectRoot,
|
|
196
|
+
jsonSchema: args.submitSchema.schema,
|
|
195
197
|
sandboxMode: args.sandboxMode,
|
|
196
198
|
unitId: args.unitId,
|
|
197
199
|
unitKind: args.unitKind,
|
|
@@ -269,11 +271,19 @@ async function runClaudeWorker(args) {
|
|
|
269
271
|
if (args.reasoningEffort && args.reasoningEffort.length > 0) {
|
|
270
272
|
claudeArgs.push("--effort", args.reasoningEffort);
|
|
271
273
|
}
|
|
272
|
-
//
|
|
273
|
-
//
|
|
274
|
-
//
|
|
275
|
-
//
|
|
276
|
-
//
|
|
274
|
+
// --json-schema constrains output at the API level (grammar-based structured
|
|
275
|
+
// outputs) so the model CANNOT emit syntactically malformed JSON; the CLI then
|
|
276
|
+
// returns a parsed `structured_output` object (extractClaudeStructuredPayload
|
|
277
|
+
// reads that first). Empirically (opus-4.8 + haiku, 2026-07) the flag accepts
|
|
278
|
+
// the runtime's submit schemas (additionalProperties:false + deep required,
|
|
279
|
+
// ~20 params, enums) and returns native objects — the earlier "silently
|
|
280
|
+
// rejects complex schemas" note was inaccurate. Without this, opus free-forms
|
|
281
|
+
// the JSON in-prompt and can drop a comma on large findings → output_contract
|
|
282
|
+
// failure with no recovery. The in-prompt schema stays as a redundant semantic
|
|
283
|
+
// hint; the submit tool remains the authoritative VALIDATOR of field content.
|
|
284
|
+
if (args.jsonSchema !== undefined) {
|
|
285
|
+
claudeArgs.push("--json-schema", JSON.stringify(args.jsonSchema));
|
|
286
|
+
}
|
|
277
287
|
// Never load project/user MCP servers in the bounded worker (no side effects,
|
|
278
288
|
// no mcp__* tools to auto-approve).
|
|
279
289
|
claudeArgs.push("--strict-mcp-config", "--mcp-config", '{"mcpServers":{}}');
|
|
@@ -557,6 +567,7 @@ export async function runClaudeCodeReviewUnitExecutorCli(argv) {
|
|
|
557
567
|
const stdout = await runClaudeWorker({
|
|
558
568
|
projectRoot,
|
|
559
569
|
boundedPrompt,
|
|
570
|
+
jsonSchema: submitSchema?.schema,
|
|
560
571
|
model,
|
|
561
572
|
reasoningEffort,
|
|
562
573
|
sandboxMode,
|
|
@@ -6,7 +6,9 @@ import os from "node:os";
|
|
|
6
6
|
import path from "node:path";
|
|
7
7
|
import { parseArgs } from "node:util";
|
|
8
8
|
import { pathToFileURL } from "node:url";
|
|
9
|
+
import { reviewMaterialGoals } from "../target-material-kind.js";
|
|
9
10
|
import { fileExists, isoNow, readYamlDocument, writeYamlDocument, toRelativePath, truncateForEmbedding, } from "../review/review-artifact-utils.js";
|
|
11
|
+
import { DEFAULT_MAX_EMBED_LINES } from "../review/review-prompt-budget.js";
|
|
10
12
|
import { printOntoReleaseChannelNotice } from "../release-channel/release-channel.js";
|
|
11
13
|
import { writeAndThrowStructuredFailureRecord } from "../review/failure-records.js";
|
|
12
14
|
import { resolveInstallationPath } from "../discovery/installation-paths.js";
|
|
@@ -52,19 +54,52 @@ export function renderEmbeddedMaterializedInputSection(materializedInput) {
|
|
|
52
54
|
${materializedInput}
|
|
53
55
|
<!-- onto:embedded-materialized-input:end -->`;
|
|
54
56
|
}
|
|
55
|
-
function materialKindReviewObligations(
|
|
56
|
-
switch (
|
|
57
|
+
function materialKindReviewObligations(profile) {
|
|
58
|
+
switch (profile.target_material_kind) {
|
|
57
59
|
case "code":
|
|
58
60
|
return [
|
|
59
61
|
"Treat declared types, exported API signatures, documented contracts, and observable runtime behavior as review evidence.",
|
|
60
62
|
"Check visible type/runtime contract mismatches, edge-case input behavior, error/null/undefined paths, and caller-facing failure modes.",
|
|
61
63
|
"Classify a visible correctness or runtime-contract failure as material when it can violate the declared review goal inside the bounded target.",
|
|
62
64
|
];
|
|
63
|
-
case "spreadsheet":
|
|
65
|
+
case "spreadsheet": {
|
|
66
|
+
// The obligation prose stays in lockstep with the per-ref disposition projected into
|
|
67
|
+
// review_goal (the SSOT): a goal is named only when its specific structure was
|
|
68
|
+
// rendered. Three honest cases, so the lens is never told to audit absent formulas nor
|
|
69
|
+
// told the data is unavailable when it was actually rendered:
|
|
70
|
+
const spreadsheetGoals = reviewMaterialGoals("spreadsheet");
|
|
71
|
+
const backed = profile.review_goal.filter((goal) => spreadsheetGoals.includes(goal));
|
|
72
|
+
if (backed.length > 0) {
|
|
73
|
+
// Project the prose from the BACKED goal SUBSET (not a single any-backed bit), so the
|
|
74
|
+
// lens is told to audit only the structure the inventory actually rendered for this
|
|
75
|
+
// target — a macro-only or validation-only workbook is never told to audit formulas
|
|
76
|
+
// that review_goal and the render both show are absent. Emitted in stable catalog order.
|
|
77
|
+
const GOAL_PROSE = {
|
|
78
|
+
formula_integrity: "Treat formulas and their recalculation behavior as review evidence; check visible formula mismatches, stale derived values, and decision-impacting calculation errors.",
|
|
79
|
+
cross_sheet_reference_integrity: "Treat cross-sheet references as review evidence; check broken, stale, or misaimed references across sheets.",
|
|
80
|
+
named_range_hygiene: "Treat named ranges as review evidence; check missing, overlapping, or wrongly-scoped named ranges.",
|
|
81
|
+
data_validation_coverage: "Treat data-validation rules (type, operator, bounds) as review evidence; check missing or inconsistent input guards.",
|
|
82
|
+
access_and_protection_hygiene: "Treat sheet protection, hidden sheets, and macro presence as review evidence; check the access/protection posture and any macro-carried risk.",
|
|
83
|
+
structural_risk_signals: "Treat the recorded structural risk signals, external links, and error cells as review evidence; check decision-impacting structural risks.",
|
|
84
|
+
};
|
|
85
|
+
return spreadsheetGoals
|
|
86
|
+
.filter((goal) => backed.includes(goal))
|
|
87
|
+
.map((goal) => GOAL_PROSE[goal]);
|
|
88
|
+
}
|
|
89
|
+
// No structural obligation is backed. Distinguish an inspected plain-data workbook
|
|
90
|
+
// (read — e.g. a flat CSV — but with no formula/named-range/validation/protection
|
|
91
|
+
// structure to audit) from one that could not be inspected at all.
|
|
92
|
+
if (profile.material_profile.support_status === "supported") {
|
|
93
|
+
return [
|
|
94
|
+
"The target workbook(s) were structurally inspected but carry no formula, cross-sheet reference, named range, data validation, or protection structure to audit beyond the rendered columns/data.",
|
|
95
|
+
"Treat the rendered structural inventory as the evidence; do not infer calculation logic the workbook does not contain.",
|
|
96
|
+
];
|
|
97
|
+
}
|
|
64
98
|
return [
|
|
65
|
-
"
|
|
66
|
-
"
|
|
99
|
+
"The target workbook(s) could not be structurally inspected (unsupported format, unreadable, or empty); the materialized input carries only an unsupported note, not formula/reference structure.",
|
|
100
|
+
"Treat only what the materialized input actually renders as evidence; do not assume formulas, cross-sheet references, or recalculated values are available, and preserve material uncertainty about the uninspected structure.",
|
|
67
101
|
];
|
|
102
|
+
}
|
|
68
103
|
case "document":
|
|
69
104
|
return [
|
|
70
105
|
"Treat declared purpose, audience, claims, evidence, structure, and unresolved ambiguity as review evidence.",
|
|
@@ -87,13 +122,13 @@ function materialKindReviewObligations(targetMaterialKind) {
|
|
|
87
122
|
];
|
|
88
123
|
}
|
|
89
124
|
}
|
|
90
|
-
function renderReviewTargetProfileSummary(profile) {
|
|
125
|
+
export function renderReviewTargetProfileSummary(profile) {
|
|
91
126
|
if (!profile) {
|
|
92
127
|
return `## Review Target Profile Summary
|
|
93
128
|
- profile: unavailable
|
|
94
129
|
- consequence: use only the materialized input and explicit request summary as target authority.`;
|
|
95
130
|
}
|
|
96
|
-
const obligations = materialKindReviewObligations(profile
|
|
131
|
+
const obligations = materialKindReviewObligations(profile);
|
|
97
132
|
return `## Review Target Profile Summary
|
|
98
133
|
- target_material_kind: ${profile.target_material_kind}
|
|
99
134
|
- target_input_kind: ${profile.target_input_kind}
|
|
@@ -190,7 +225,9 @@ Submit \`domain_context_assumptions\` as a list of strings.
|
|
|
190
225
|
If there are no findings, submit \`findings: []\` and a concise \`no_findings_rationale\`.
|
|
191
226
|
${args.humanOutputPath ? `The runtime may render a human-readable projection at ${toRelativePath(args.humanOutputPath, args.projectRoot)}.` : "No human-readable lens markdown projection is requested for this session."}`;
|
|
192
227
|
}
|
|
193
|
-
|
|
228
|
+
// DEFAULT_MAX_EMBED_LINES is owned by review-prompt-budget.ts (the single
|
|
229
|
+
// conversion point), imported above and shared with the prepare-time budget
|
|
230
|
+
// resolution so the no-regression floor lives in one place.
|
|
194
231
|
// Core role IDs derived from .onto/authority/core-lens-registry.yaml (single source of truth)
|
|
195
232
|
import { loadCoreLensRegistry } from "../discovery/lens-registry.js";
|
|
196
233
|
const CORE_ROLE_IDS = new Set(loadCoreLensRegistry().core_role_ids);
|
|
@@ -731,12 +768,20 @@ export async function runMaterializeReviewPromptPacketsCli(argv) {
|
|
|
731
768
|
allowPositionals: false,
|
|
732
769
|
args: argv,
|
|
733
770
|
});
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
771
|
+
// Explicit CLI --max-embed-lines wins (an operator override). An invalid value
|
|
772
|
+
// falls back (warned) so it does NOT mask the plan's window-proportional budget.
|
|
773
|
+
// The plan's persisted max_embed_lines (Stage 1) is applied below, after the
|
|
774
|
+
// plan is read; absent both → DEFAULT (no regression).
|
|
775
|
+
let cliMaxEmbedLines;
|
|
776
|
+
if (typeof values["max-embed-lines"] === "string" &&
|
|
777
|
+
values["max-embed-lines"].length > 0) {
|
|
778
|
+
const parsed = Number.parseInt(values["max-embed-lines"], 10);
|
|
779
|
+
if (Number.isFinite(parsed) && parsed >= 1) {
|
|
780
|
+
cliMaxEmbedLines = parsed;
|
|
781
|
+
}
|
|
782
|
+
else {
|
|
783
|
+
console.warn(`[onto] Invalid max-embed-lines value (${values["max-embed-lines"]}), using default ${DEFAULT_MAX_EMBED_LINES}.`);
|
|
784
|
+
}
|
|
740
785
|
}
|
|
741
786
|
const projectRoot = path.resolve(requireString(values["project-root"], "project-root"));
|
|
742
787
|
const sessionRoot = path.resolve(requireString(values["session-root"], "session-root"));
|
|
@@ -749,6 +794,15 @@ export async function runMaterializeReviewPromptPacketsCli(argv) {
|
|
|
749
794
|
const binding = await readYamlDocument(bindingPath);
|
|
750
795
|
const sessionMetadata = await readYamlDocument(sessionMetadataPath);
|
|
751
796
|
const executionPlan = await readYamlDocument(executionPlanPath);
|
|
797
|
+
// Stage 1 embed-budget precedence: explicit CLI override wins; else the plan's
|
|
798
|
+
// window-proportional max_embed_lines (persisted at prepare time); else DEFAULT
|
|
799
|
+
// (model-unaware run — no regression).
|
|
800
|
+
const planMaxEmbedLines = typeof executionPlan.max_embed_lines === "number" &&
|
|
801
|
+
Number.isFinite(executionPlan.max_embed_lines) &&
|
|
802
|
+
executionPlan.max_embed_lines >= 1
|
|
803
|
+
? executionPlan.max_embed_lines
|
|
804
|
+
: undefined;
|
|
805
|
+
const maxEmbedLines = cliMaxEmbedLines ?? planMaxEmbedLines ?? DEFAULT_MAX_EMBED_LINES;
|
|
752
806
|
const promptPacketsRoot = executionPlan.prompt_packets_root ?? path.join(sessionRoot, "prompt-packets");
|
|
753
807
|
const materializedInputText = await readOptionalText(binding.materialized_input_path);
|
|
754
808
|
const reviewTargetProfile = await readYamlDocument(binding.review_target_profile_path);
|
|
@@ -3,11 +3,32 @@ import path from "node:path";
|
|
|
3
3
|
import { parseArgs } from "node:util";
|
|
4
4
|
import { pathToFileURL } from "node:url";
|
|
5
5
|
import { DEFAULT_DIRECTORY_LISTING_OPTIONS, hasOptionFlag, parseBooleanFlag, } from "../review/review-artifact-utils.js";
|
|
6
|
-
import { bootstrapInvocationBindingArtifacts, materializeReviewExecutionPreparationArtifacts, writeInvocationInterpretationArtifact, } from "../review/materializers.js";
|
|
6
|
+
import { bootstrapInvocationBindingArtifacts, materializeReviewExecutionPreparationArtifacts, resolveReviewExecutionSettingsForArtifacts, writeInvocationInterpretationArtifact, } from "../review/materializers.js";
|
|
7
|
+
import { DEFAULT_MAX_EMBED_LINES, deriveReviewMaxEmbedLines, resolveReviewLensContextWindowTokens, reviewWindowMultiplier, } from "../review/review-prompt-budget.js";
|
|
8
|
+
import { deriveWorkbookInventoryPromptCaps } from "../spreadsheet-structure-observer.js";
|
|
9
|
+
import { loadSupportedModelRegistry } from "../discovery/supported-models.js";
|
|
7
10
|
import { printOntoReleaseChannelNotice } from "../release-channel/release-channel.js";
|
|
8
11
|
import { detectCodexEnvSignal } from "../discovery/host-detection.js";
|
|
9
12
|
import { resolveOntoHome } from "../discovery/onto-home.js";
|
|
10
13
|
import { resolveSettingsChain } from "../discovery/settings-chain.js";
|
|
14
|
+
/**
|
|
15
|
+
* Stage 1 — resolve the window-proportional review prompt budget multiplier from
|
|
16
|
+
* the lens model's registered context window. The registry load and model
|
|
17
|
+
* resolution are best-effort: any failure (missing/malformed authority,
|
|
18
|
+
* unresolved model, no registered window) returns multiplier 1, so the budget
|
|
19
|
+
* floors to DEFAULT caps + DEFAULT embed lines (no regression). Never throws.
|
|
20
|
+
*/
|
|
21
|
+
function resolveReviewPromptBudget(ontoConfig) {
|
|
22
|
+
try {
|
|
23
|
+
const registry = loadSupportedModelRegistry();
|
|
24
|
+
const execution = resolveReviewExecutionSettingsForArtifacts(ontoConfig);
|
|
25
|
+
const window = resolveReviewLensContextWindowTokens(execution, registry);
|
|
26
|
+
return { multiplier: reviewWindowMultiplier(window) };
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
return { multiplier: 1 };
|
|
30
|
+
}
|
|
31
|
+
}
|
|
11
32
|
function requireString(value, optionName) {
|
|
12
33
|
if (typeof value !== "string" || value.length === 0) {
|
|
13
34
|
throw new Error(`Missing required option --${optionName}`);
|
|
@@ -175,6 +196,16 @@ export async function runPrepareReviewSessionCli(argv) {
|
|
|
175
196
|
const projectRoot = path.resolve(requireString(values["project-root"], "project-root"));
|
|
176
197
|
const ontoHome = resolveOntoHome(optionalString(values["onto-home"]) || undefined);
|
|
177
198
|
const ontoConfig = await resolveSettingsChain(ontoHome, projectRoot);
|
|
199
|
+
// Stage 1 — resolve the window-proportional review prompt budget ONCE, here,
|
|
200
|
+
// and thread it to both consumers: the spreadsheet inventory prompt caps
|
|
201
|
+
// (into the materialize call) and the embed line budget (persisted into the
|
|
202
|
+
// execution plan for the packet stage). The lens MODEL's registered context
|
|
203
|
+
// window drives a single multiplier. Any failure (registry load, unresolved
|
|
204
|
+
// model, no registered window) falls through to multiplier 1 → DEFAULT caps +
|
|
205
|
+
// DEFAULT embed lines (no regression). Never throws.
|
|
206
|
+
const reviewPromptBudget = resolveReviewPromptBudget(ontoConfig);
|
|
207
|
+
const inventoryPromptCaps = deriveWorkbookInventoryPromptCaps(reviewPromptBudget.multiplier);
|
|
208
|
+
const maxEmbedLines = deriveReviewMaxEmbedLines(reviewPromptBudget.multiplier, DEFAULT_MAX_EMBED_LINES);
|
|
178
209
|
const bindingParams = {
|
|
179
210
|
projectRoot,
|
|
180
211
|
ontoConfig,
|
|
@@ -195,6 +226,7 @@ export async function runPrepareReviewSessionCli(argv) {
|
|
|
195
226
|
filesystemAllowedRoots: values["filesystem-allowed-root"],
|
|
196
227
|
bindingNotes: values["binding-note"],
|
|
197
228
|
ontoHome,
|
|
229
|
+
maxEmbedLines,
|
|
198
230
|
...(typeof values["session-id"] === "string" && values["session-id"].length > 0
|
|
199
231
|
? { sessionId: values["session-id"] }
|
|
200
232
|
: {}),
|
|
@@ -253,6 +285,7 @@ export async function runPrepareReviewSessionCli(argv) {
|
|
|
253
285
|
roleDefinitionRefs: values["role-definition-ref"],
|
|
254
286
|
executionRuleRefs: values["execution-rule-ref"],
|
|
255
287
|
directoryListingOptions,
|
|
288
|
+
inventoryPromptCaps,
|
|
256
289
|
});
|
|
257
290
|
console.log(JSON.stringify({
|
|
258
291
|
session_root: sessionRoot,
|
|
@@ -642,6 +642,7 @@ export async function runRenderReviewFinalOutputCli(argv) {
|
|
|
642
642
|
const defaultPurposeAlignment = executionStatus === "completed"
|
|
643
643
|
? "- bounded review execution completed"
|
|
644
644
|
: `- execution status: ${executionStatus}`;
|
|
645
|
+
const fallbackImmediateActions = renderActionCandidates(classificationSummary);
|
|
645
646
|
const finalOutputText = `---
|
|
646
647
|
session_id: ${bindingArtifact.session_id}
|
|
647
648
|
process: review
|
|
@@ -737,7 +738,7 @@ ${renderedBoundaryNotes}
|
|
|
737
738
|
### Immediate Actions Required
|
|
738
739
|
${synthesisLedger
|
|
739
740
|
? renderLedgerActions(synthesisLedger)
|
|
740
|
-
:
|
|
741
|
+
: fallbackImmediateActions}
|
|
741
742
|
|
|
742
743
|
### Recommendations
|
|
743
744
|
${synthesisLedger
|