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
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { ALL_HOST_IDS, } from "./types.js";
|
|
2
2
|
import { getDefaultHostTargets } from "./host-target.js";
|
|
3
|
+
import { discoverClaudeProfiles } from "./claude-profile-scan.js";
|
|
3
4
|
import { promptMultiSelect, promptYesNo } from "./prompt.js";
|
|
4
5
|
const USAGE = [
|
|
5
6
|
"Usage: onto register [options]",
|
|
@@ -20,6 +21,9 @@ const USAGE = [
|
|
|
20
21
|
" --command <cmd> Executable the host launches (default: onto)",
|
|
21
22
|
" --claude-config-dir <path> Target a Claude Code profile (sets",
|
|
22
23
|
" CLAUDE_CONFIG_DIR; default: ambient env or ~/.claude)",
|
|
24
|
+
" --all-claude-profiles Auto-discover every Claude Code config dir under",
|
|
25
|
+
" home (~/.claude and ~/.claude-*) and register each",
|
|
26
|
+
" (mutually exclusive with --claude-config-dir)",
|
|
23
27
|
" --help, -h Show this help",
|
|
24
28
|
].join("\n");
|
|
25
29
|
export function parseRegisterArgs(argv) {
|
|
@@ -33,6 +37,7 @@ export function parseRegisterArgs(argv) {
|
|
|
33
37
|
name: "onto",
|
|
34
38
|
command: "onto",
|
|
35
39
|
claudeConfigDir: undefined,
|
|
40
|
+
allClaudeProfiles: false,
|
|
36
41
|
unknownFlags: [],
|
|
37
42
|
invalidHosts: [],
|
|
38
43
|
};
|
|
@@ -82,6 +87,9 @@ export function parseRegisterArgs(argv) {
|
|
|
82
87
|
case "--claude-config-dir":
|
|
83
88
|
parsed.claudeConfigDir = argv[++i] ?? parsed.claudeConfigDir;
|
|
84
89
|
break;
|
|
90
|
+
case "--all-claude-profiles":
|
|
91
|
+
parsed.allClaudeProfiles = true;
|
|
92
|
+
break;
|
|
85
93
|
default:
|
|
86
94
|
parsed.unknownFlags.push(arg);
|
|
87
95
|
break;
|
|
@@ -89,12 +97,20 @@ export function parseRegisterArgs(argv) {
|
|
|
89
97
|
}
|
|
90
98
|
return parsed;
|
|
91
99
|
}
|
|
92
|
-
|
|
100
|
+
/** Unique selection identity for a target (host *instance*, not just kind). */
|
|
101
|
+
function targetKey(target) {
|
|
102
|
+
return target.key ?? target.id;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Flag-driven selection by host *kind* (`id`). `--hosts claude-code` therefore
|
|
106
|
+
* selects every Claude Code profile target when several are expanded.
|
|
107
|
+
*/
|
|
108
|
+
function resolveSelectedTargets(parsed, targets) {
|
|
93
109
|
if (parsed.hosts === "all")
|
|
94
|
-
return targets
|
|
110
|
+
return targets;
|
|
95
111
|
if (Array.isArray(parsed.hosts)) {
|
|
96
112
|
const requested = new Set(parsed.hosts);
|
|
97
|
-
return targets.
|
|
113
|
+
return targets.filter((t) => requested.has(t.id));
|
|
98
114
|
}
|
|
99
115
|
return [];
|
|
100
116
|
}
|
|
@@ -123,6 +139,29 @@ function printResults(results) {
|
|
|
123
139
|
console.log(` • ${result.displayName}: ${result.outcome} — ${result.detail}`);
|
|
124
140
|
}
|
|
125
141
|
}
|
|
142
|
+
/**
|
|
143
|
+
* Build the default host targets honoring profile flags. When
|
|
144
|
+
* `--all-claude-profiles` is set, discover every Claude Code config dir under
|
|
145
|
+
* home and expand Claude Code into one target per profile; if none are found,
|
|
146
|
+
* fall back to the single default target with a note.
|
|
147
|
+
*/
|
|
148
|
+
function buildDefaultTargets(parsed, deps) {
|
|
149
|
+
if (parsed.allClaudeProfiles) {
|
|
150
|
+
const discover = deps.discoverClaudeProfiles ??
|
|
151
|
+
(() => {
|
|
152
|
+
const env = process.env.CLAUDE_CONFIG_DIR;
|
|
153
|
+
return discoverClaudeProfiles(env ? { configDirEnv: env } : {});
|
|
154
|
+
});
|
|
155
|
+
const profiles = discover();
|
|
156
|
+
if (profiles.length > 0) {
|
|
157
|
+
return getDefaultHostTargets({ claudeProfiles: profiles });
|
|
158
|
+
}
|
|
159
|
+
console.error("[onto register] --all-claude-profiles: no Claude Code config dirs found " +
|
|
160
|
+
"under home; falling back to the default profile.");
|
|
161
|
+
return getDefaultHostTargets({});
|
|
162
|
+
}
|
|
163
|
+
return getDefaultHostTargets(parsed.claudeConfigDir ? { claudeConfigDir: parsed.claudeConfigDir } : {});
|
|
164
|
+
}
|
|
126
165
|
export async function runRegister(argv, deps = {}) {
|
|
127
166
|
const parsed = parseRegisterArgs(argv);
|
|
128
167
|
if (parsed.help) {
|
|
@@ -139,8 +178,12 @@ export async function runRegister(argv, deps = {}) {
|
|
|
139
178
|
`Valid: ${ALL_HOST_IDS.join(", ")}`);
|
|
140
179
|
return 1;
|
|
141
180
|
}
|
|
142
|
-
|
|
143
|
-
|
|
181
|
+
if (parsed.allClaudeProfiles && parsed.claudeConfigDir) {
|
|
182
|
+
console.error("[onto register] Use either --all-claude-profiles or --claude-config-dir, " +
|
|
183
|
+
"not both.");
|
|
184
|
+
return 1;
|
|
185
|
+
}
|
|
186
|
+
const targets = deps.targets ?? buildDefaultTargets(parsed, deps);
|
|
144
187
|
const isTty = deps.isTty ?? Boolean(process.stdin.isTTY);
|
|
145
188
|
if (parsed.list) {
|
|
146
189
|
console.log("Host detection:");
|
|
@@ -155,7 +198,7 @@ export async function runRegister(argv, deps = {}) {
|
|
|
155
198
|
args: ["mcp"],
|
|
156
199
|
};
|
|
157
200
|
// Determine which hosts to register.
|
|
158
|
-
let
|
|
201
|
+
let selectedTargets = resolveSelectedTargets(parsed, targets);
|
|
159
202
|
const selectionGivenByFlag = parsed.hosts !== undefined;
|
|
160
203
|
if (!selectionGivenByFlag) {
|
|
161
204
|
if (!isTty) {
|
|
@@ -163,19 +206,18 @@ export async function runRegister(argv, deps = {}) {
|
|
|
163
206
|
"(interactive selection requires a terminal).");
|
|
164
207
|
return 1;
|
|
165
208
|
}
|
|
166
|
-
const defaults = targets.filter((t) => t.detect() !== "absent").map(
|
|
209
|
+
const defaults = targets.filter((t) => t.detect() !== "absent").map(targetKey);
|
|
167
210
|
const chosen = await promptMultiSelect("Select hosts to register onto with:", targets.map((t) => ({
|
|
168
|
-
id: t
|
|
211
|
+
id: targetKey(t),
|
|
169
212
|
label: t.displayName,
|
|
170
213
|
detail: detectionLabel(t.detect()),
|
|
171
|
-
})), defaults.length > 0 ? defaults : targets.map(
|
|
172
|
-
|
|
214
|
+
})), defaults.length > 0 ? defaults : targets.map(targetKey));
|
|
215
|
+
selectedTargets = targets.filter((t) => chosen.includes(targetKey(t)));
|
|
173
216
|
}
|
|
174
|
-
if (
|
|
217
|
+
if (selectedTargets.length === 0) {
|
|
175
218
|
console.error("[onto register] No hosts selected. Nothing to do.");
|
|
176
219
|
return 1;
|
|
177
220
|
}
|
|
178
|
-
const selectedTargets = targets.filter((t) => selected.includes(t.id));
|
|
179
221
|
const options = { force: parsed.force, dryRun: parsed.dryRun };
|
|
180
222
|
const plans = selectedTargets.map((t) => t.plan(entry, options));
|
|
181
223
|
printPlans(plans);
|
|
@@ -41,6 +41,16 @@ export function isTrustedLedgerUnit(unit) {
|
|
|
41
41
|
unit.trustStatus === "trusted" &&
|
|
42
42
|
hasAllRequiredOutputHashes(unit));
|
|
43
43
|
}
|
|
44
|
+
/**
|
|
45
|
+
* Trusted output OR terminally resolved (demoted complete-with-failure):
|
|
46
|
+
* either way the unit owes no further dispatch. Frontier and convergence
|
|
47
|
+
* consumers use this predicate; artifact-preservation consumers keep using
|
|
48
|
+
* {@link isTrustedLedgerUnit} — a resolved unit has no trusted output to
|
|
49
|
+
* preserve or to consume downstream.
|
|
50
|
+
*/
|
|
51
|
+
export function isResolvedLedgerUnit(unit) {
|
|
52
|
+
return unit.resolution === "demoted" || isTrustedLedgerUnit(unit);
|
|
53
|
+
}
|
|
44
54
|
export function firstUntrustedRequiredUnit(ledger) {
|
|
45
55
|
return ledger.units.find((unit) => !isTrustedLedgerUnit(unit)) ?? null;
|
|
46
56
|
}
|
|
@@ -29,6 +29,16 @@ export const RECONSTRUCT_STAGE_IDS = [
|
|
|
29
29
|
"source_observation_reentry_validation",
|
|
30
30
|
"source_observation_lineage_index",
|
|
31
31
|
"source_observation_lineage_index_validation",
|
|
32
|
+
// P1-C2 leaf-read: the first LLM-touch (capture over structure-incomplete spreadsheet regions).
|
|
33
|
+
// Owns its own telemetry unit so a leaf-read total-failure is recorded, not silently degraded
|
|
34
|
+
// (the defect this stage id fixes: "leaf-read" had no telemetry unit → callLlmRecorded threw
|
|
35
|
+
// before the LLM call → R9 swallowed it → zero capture, forever). Runs after the lineage index
|
|
36
|
+
// exists and before purpose-candidate authoring.
|
|
37
|
+
"leaf_read",
|
|
38
|
+
// Layer-2 semantic_map stage (wiring design 20260702 §7-W3): default-off; runs right after
|
|
39
|
+
// leaf_read; its always-written census is the step's artifact ref (skipped when the author lacks
|
|
40
|
+
// the synthesizeSemanticMapNode/verifySemanticMapBoundary capability pair).
|
|
41
|
+
"semantic_map",
|
|
32
42
|
"source_purpose_candidates",
|
|
33
43
|
"source_purpose_candidates_validation",
|
|
34
44
|
"purpose_confirmation",
|
|
@@ -39,6 +49,7 @@ export const RECONSTRUCT_STAGE_IDS = [
|
|
|
39
49
|
"candidate_disposition_validation",
|
|
40
50
|
"seed_authoring_readiness",
|
|
41
51
|
"seed_authoring_readiness_validation",
|
|
52
|
+
"seed_stage_prompt_source_observations",
|
|
42
53
|
"ontology_seed",
|
|
43
54
|
"ontology_seed_validation",
|
|
44
55
|
"material_admission_validation",
|
|
@@ -62,6 +73,14 @@ export const RECONSTRUCT_STAGE_IDS = [
|
|
|
62
73
|
"maturation_baseline_validation",
|
|
63
74
|
"baseline_actionability_matrix",
|
|
64
75
|
"baseline_actionability_matrix_validation",
|
|
76
|
+
// Maturation value-read cut: the LLM-touch stage that reads authorized runtime-target
|
|
77
|
+
// cell values to discharge value-dependent limitations on baseline matrix rows. A single
|
|
78
|
+
// stage id (leaf_read precedent) — discharge validation is an embedded self-validation
|
|
79
|
+
// step, so terminal run-manifest validation needs exactly one matching manifest step
|
|
80
|
+
// (design §13.5 F3). Runs after the baseline matrix is produced and before the question
|
|
81
|
+
// frontier, so its value-grounded discharge reaches the actionability matrix directly
|
|
82
|
+
// without waking the empty frontier path.
|
|
83
|
+
"maturation_value_read",
|
|
65
84
|
"maturation_question_frontier",
|
|
66
85
|
"maturation_question_frontier_validation",
|
|
67
86
|
"maturation_closure_frontier",
|
|
@@ -100,3 +119,19 @@ export const RECONSTRUCT_STAGE_IDS = [
|
|
|
100
119
|
"record_assembly",
|
|
101
120
|
"post_publication_run_manifest_validation",
|
|
102
121
|
];
|
|
122
|
+
/**
|
|
123
|
+
* The conditional stages that can legitimately run-but-produce-nothing yet leave no
|
|
124
|
+
* artifact-ref witness of their own (unlike leaf_read / maturation_value_read, which always
|
|
125
|
+
* write a census when they run). Canonical single source shared by the graceful-terminal
|
|
126
|
+
* manifest builder (createRunManifest) and the reachability validator (validateReconstructRunManifest):
|
|
127
|
+
* on a graceful-terminal manifest these are the only stages permitted `skip_kind:
|
|
128
|
+
* "legit_conditional"`, and only when the reachability witness (ReconstructSourceObservationLineageCensus)
|
|
129
|
+
* confirms they ran and legitimately produced nothing. See reachability-manifest design v2 §2–§4.
|
|
130
|
+
*/
|
|
131
|
+
export const WITNESS_LESS_CONDITIONAL_STAGE_IDS = [
|
|
132
|
+
"source_observation_delta",
|
|
133
|
+
"source_observation_delta_validation",
|
|
134
|
+
"source_observation_reentry_validation",
|
|
135
|
+
"source_observation_lineage_index",
|
|
136
|
+
"source_observation_lineage_index_validation",
|
|
137
|
+
];
|
|
@@ -2,6 +2,7 @@ import fs from "node:fs/promises";
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { parse as parseYaml } from "yaml";
|
|
4
4
|
import { atomicWriteYamlDocument as writeYamlDocument } from "../artifact-io.js";
|
|
5
|
+
import { assertObligation } from "./obligation-assertion.js";
|
|
5
6
|
const PROJECTION_SURFACES = [
|
|
6
7
|
"status",
|
|
7
8
|
"result",
|
|
@@ -389,6 +390,21 @@ function sameJson(left, right) {
|
|
|
389
390
|
}
|
|
390
391
|
export function validateClaimProjection(args) {
|
|
391
392
|
const violations = [];
|
|
393
|
+
// G(a) slice 23 — record the obligations this validator FULLY enforces with a complete unconditional
|
|
394
|
+
// check (RECORD 2/9). claim-projection's other unconditional checks are only PARTIAL — its strong
|
|
395
|
+
// enforcement is the gated `expectedClaimProjection` derived-match — so 7 stay parked (see obligation-
|
|
396
|
+
// coverage-ledger.yaml notes): governance-bounding is PARTIAL (rollback_quota_incident_governance
|
|
397
|
+
// unchecked); two material-kind-support lineage obligations are GATED on optional targetMaterialProfile
|
|
398
|
+
// +validation args (slice-18/22); and codex R1 parked four more as partial — blocked-recovery keys only
|
|
399
|
+
// on claim_level==blocked (misses material_kind_support machine_status==blocked), cite-validation-refs
|
|
400
|
+
// is presence-only without the optional expectedRequiredValidationRefs, surfaces-present is presence-only
|
|
401
|
+
// (contract design ~1172 wants ONE row per surface), and claim/decision/actionability alignment is
|
|
402
|
+
// one-directional (a continue+ready row passes). The two RECORDED checks fully enforce their named scope
|
|
403
|
+
// whenever a material_kind_support row is present (the required surface). Stamped before any per-row
|
|
404
|
+
// guard so the recorder fires on zero-row input.
|
|
405
|
+
const assertedObligationIds = [];
|
|
406
|
+
assertObligation(assertedObligationIds, "reject_unwired_material_kind_support_levels_above_profile_supported");
|
|
407
|
+
assertObligation(assertedObligationIds, "validate_material_kind_support_row_uses_capability_claim_not_actionability_claim");
|
|
392
408
|
const seenIds = new Set();
|
|
393
409
|
const surfaces = new Set();
|
|
394
410
|
const decisionStateCounts = Object.fromEntries(DECISION_STATES.map((state) => [state, 0]));
|
|
@@ -725,6 +741,7 @@ export function validateClaimProjection(args) {
|
|
|
725
741
|
validation_results: violations.length === 0
|
|
726
742
|
? ["claim_projection_valid"]
|
|
727
743
|
: ["claim_projection_invalid"],
|
|
744
|
+
asserted_obligation_ids: assertedObligationIds,
|
|
728
745
|
violations,
|
|
729
746
|
};
|
|
730
747
|
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Single source of truth for the competency-question assessment prompt-projection
|
|
3
|
+
* contract: the field set + budget constants that define the host-LLM assessment
|
|
4
|
+
* prompt surface. The reconstruct runtime (run.ts) imports the contract fn + budgets
|
|
5
|
+
* from here, and the G(b) parity guard (scripts/check-prompt-projection-parity.ts)
|
|
6
|
+
* asserts the registry node `prompt_projection_contracts.competency_question_assessment`
|
|
7
|
+
* declares exactly this surface — so a field/budget change cannot silently drift from
|
|
8
|
+
* its registry declaration (INV-SCHEMA-1). Side-effect-free.
|
|
9
|
+
*/
|
|
10
|
+
export const COMPETENCY_QUESTION_ASSESSMENT_PROJECTION_CONTRACT_VERSION =
|
|
11
|
+
// v3 added the cited source-evidence bodies surface (source_evidence); v4 bounded it to a
|
|
12
|
+
// deterministic per-payload SERIALIZED-SIZE budget; v5 (M2) derives that evidence reserve
|
|
13
|
+
// from the WHOLE prompt budget per batch (= prompt_char_limit − measured non-evidence
|
|
14
|
+
// payload − margin) instead of a static budget, so the evidence uses the room actually
|
|
15
|
+
// left under the 50K cap. v6 scopes the claim_realization_map projection to each batch's
|
|
16
|
+
// linked claims (questions' linked_claim_ids) instead of embedding the WHOLE map in every
|
|
17
|
+
// batch — the whole-map fixed overhead grew unbounded with claim count and overflowed the
|
|
18
|
+
// 50K cap before M3 (a claim-count-dependent pre-dispatch hard stop). Each version +
|
|
19
|
+
// contract change rotates the reuse-match hash so resume mode cannot reuse an assessment
|
|
20
|
+
// authored under a different (or content-blind, pre-v3) evidence projection of the same
|
|
21
|
+
// sources — in particular a v5 assessment authored under the unscoped whole-map projection.
|
|
22
|
+
"competency_question_assessment_compact_projection:v6";
|
|
23
|
+
export const COMPETENCY_QUESTION_ASSESSMENT_PROMPT_CHAR_LIMIT = 50_000;
|
|
24
|
+
// Per-observation excerpt budget for the cited source-evidence bodies projected
|
|
25
|
+
// into the assessment prompt, so answer_status is judged on evidence content
|
|
26
|
+
// rather than observation-id labels alone.
|
|
27
|
+
// Per-observation excerpt budget on each cited evidence body, kept as a real pre-cap so one
|
|
28
|
+
// huge observation (e.g. a streaming spreadsheet) cannot eat the whole evidence reserve.
|
|
29
|
+
export const COMPETENCY_QUESTION_ASSESSMENT_EVIDENCE_EXCERPT_LIMIT = 4_000;
|
|
30
|
+
// Margin held back from the prompt cap when deriving the per-batch evidence reserve and when
|
|
31
|
+
// building batches, so the projection metadata that grows as evidence is added still fits.
|
|
32
|
+
export const COMPETENCY_QUESTION_ASSESSMENT_BATCH_BUILD_BUDGET_RESERVE_CHARS = 1000;
|
|
33
|
+
export function competencyQuestionAssessmentProjectionContract() {
|
|
34
|
+
return {
|
|
35
|
+
projection_kind: "competency_question_assessment_compact_projection",
|
|
36
|
+
projection_contract_version: COMPETENCY_QUESTION_ASSESSMENT_PROJECTION_CONTRACT_VERSION,
|
|
37
|
+
semantic_authority: "host_llm_assesses_answer_status_and_explanation_fields",
|
|
38
|
+
prompt_char_limit: COMPETENCY_QUESTION_ASSESSMENT_PROMPT_CHAR_LIMIT,
|
|
39
|
+
question_projection: "full question text is included without truncation; runtime keeps the full artifact authority",
|
|
40
|
+
evidence_projection: "evidence_observation_ids and evidence_source_basenames are prompt-visible; full evidence_refs remain runtime authority",
|
|
41
|
+
source_evidence_projection: "cited evidence observation bodies (from linked claim realizations, question evidence_refs, and domain competency semantic assessment evidence_refs) are projected as source_evidence, bounded greedily by serialized payload size to the per-batch evidence reserve, so answer_status is judged on content not id labels alone",
|
|
42
|
+
// The per-observation excerpt budget and the evidence-reserve derivation are part of the
|
|
43
|
+
// contract: tuning either changes the assessment prompt surface, so they rotate the
|
|
44
|
+
// reuse-match sha.
|
|
45
|
+
source_evidence_excerpt_char_limit: COMPETENCY_QUESTION_ASSESSMENT_EVIDENCE_EXCERPT_LIMIT,
|
|
46
|
+
source_evidence_reserve_derivation: "per batch = prompt_char_limit − measured non-evidence payload (system prompt + questions + claim map + validation + policy, empty evidence) − build budget reserve, clamped >= 0; a budget stub (evidence_body_omitted_for_budget) carries no body and is counted as omitted",
|
|
47
|
+
validation_projection: "validation status, counts, required evidence scope count, validation results, and invalid prompt-visible violations are prompt-visible",
|
|
48
|
+
claim_realization_projection: "claim_id, stance, evidence observation ids, evidence source basenames, and compact rationale are prompt-visible, SCOPED to the batch's linked claims (union of the batch questions' linked_claim_ids); claim_realization_count retains the full map count and scoped_claim_realization_count surfaces the in-batch count, so a batch of zero-link (domain-competency) questions honestly shows an empty scoped list against the full count",
|
|
49
|
+
runtime_derivations: [
|
|
50
|
+
"required_seed_refs",
|
|
51
|
+
"linked_claim_ids",
|
|
52
|
+
"evidence_refs",
|
|
53
|
+
"downstream_effect",
|
|
54
|
+
],
|
|
55
|
+
batching_policy: {
|
|
56
|
+
mode: "deterministic_prompt_budget",
|
|
57
|
+
order: "canonical_competency_question_order",
|
|
58
|
+
build_budget_reserve_chars: COMPETENCY_QUESTION_ASSESSMENT_BATCH_BUILD_BUDGET_RESERVE_CHARS,
|
|
59
|
+
single_question_overflow: "fail_loud_before_dispatch",
|
|
60
|
+
},
|
|
61
|
+
fail_loud_policy: "runtime fails before provider dispatch when any final batch exceeds prompt_char_limit",
|
|
62
|
+
};
|
|
63
|
+
}
|
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
2
|
+
// ComprehensionArtifact (§5.7) — the governance contract that guarantees every
|
|
3
|
+
// consumer receives the SAME complete set of dimensions, so a consumer can never
|
|
4
|
+
// silently drop a capped status, a lower-bound flag, a boundary witness, or a safety
|
|
5
|
+
// tier. Completeness is fail-closed: a silently-missing baseline field is a contract
|
|
6
|
+
// violation, never "empty = safe" (§5.7 2nd issue-002).
|
|
7
|
+
//
|
|
8
|
+
// Two editions share this one contract (P1-C2-A §11 R1):
|
|
9
|
+
// - DETERMINISTIC (P1-C1): the COMPANION embedded in the source observation; LLM-free,
|
|
10
|
+
// rebuilt from the inventory every run (Layer 1). producer_kind='deterministic',
|
|
11
|
+
// leaf_read_attempt='not_attempted', every LLM-touch field not_applicable.
|
|
12
|
+
// - LLM (P1-C2-A): a SEPARATE Layer-2 authored artifact carrying the provisional label
|
|
13
|
+
// read for low-confidence regions (§3.2). producer_kind='llm', leaf_read_attempt='produced',
|
|
14
|
+
// leaf-read-owned fields PRESENT, persisted+reused under llm_touch_fingerprint.
|
|
15
|
+
//
|
|
16
|
+
// A failed/empty leaf-read attempt NEVER masquerades as an 'llm' producer with all
|
|
17
|
+
// fields deferred (the gate's convergent loophole — onto issue-001/003/004 / ultracode
|
|
18
|
+
// T4/T6): content provenance (producer_kind) is split from attempt provenance
|
|
19
|
+
// (leaf_read_attempt), an 'llm' producer MUST carry produced leaf-read content, and
|
|
20
|
+
// `deferred` is allowed ONLY for engine-not-yet fields on a stage-scoped allowlist.
|
|
21
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
22
|
+
/** Edited whenever the baseline field SET or its semantics change; folded into the reconstruct
|
|
23
|
+
* reuse digest (§12 T2) so a seed authored under an older/weaker contract fails the resume
|
|
24
|
+
* provenance check rather than being silently reused. Bumped 1→2 for the P1-C2-A LLM edition
|
|
25
|
+
* (leaf_read_attempt provenance + provisional label fields). */
|
|
26
|
+
export const COMPREHENSION_ARTIFACT_CONTRACT_VERSION = 2;
|
|
27
|
+
export const LEAF_SEMANTIC_ROLES = [
|
|
28
|
+
"category",
|
|
29
|
+
"measure",
|
|
30
|
+
"identifier",
|
|
31
|
+
"free_text",
|
|
32
|
+
"reference",
|
|
33
|
+
];
|
|
34
|
+
/** Deterministic baseline fields: PRESENT (or explicit absence with lineage) in both editions. */
|
|
35
|
+
const DETERMINISTIC_BASELINE_FIELDS = [
|
|
36
|
+
"region_identity",
|
|
37
|
+
"value_signature_tile_witness",
|
|
38
|
+
"capped_or_frontier_state",
|
|
39
|
+
"is_lower_bound_by_claim",
|
|
40
|
+
"evidence_quality",
|
|
41
|
+
"examples",
|
|
42
|
+
];
|
|
43
|
+
/** Leaf-read-owned LLM-touch fields: PRESENT when producer_kind='llm' (§11 R4 required-PRESENT
|
|
44
|
+
* guard); never `deferred`. not_applicable only for a deterministic producer. */
|
|
45
|
+
const LEAF_READ_OWNED_FIELDS = [
|
|
46
|
+
"spine_claims",
|
|
47
|
+
"confidence_by_claim",
|
|
48
|
+
"limiting_witness",
|
|
49
|
+
];
|
|
50
|
+
/** Engine-not-yet LLM-touch fields: the ONLY fields allowed to be `deferred` in P1-C2-A
|
|
51
|
+
* (the stage-scoped deferred allowlist — onto issue-003 / §11 R4c). triage·reduce = next cut. */
|
|
52
|
+
const ENGINE_DEFERRABLE_FIELDS = [
|
|
53
|
+
"semantic_depth",
|
|
54
|
+
"consumer_handoff_notes",
|
|
55
|
+
"relation_obligation_lifecycle_state",
|
|
56
|
+
"downstream_blocking_semantics",
|
|
57
|
+
"trigger_provenance",
|
|
58
|
+
"triage_audit_status",
|
|
59
|
+
];
|
|
60
|
+
const LLM_TOUCH_BASELINE_FIELDS = [
|
|
61
|
+
...LEAF_READ_OWNED_FIELDS,
|
|
62
|
+
...ENGINE_DEFERRABLE_FIELDS,
|
|
63
|
+
];
|
|
64
|
+
const ENGINE_DEFERRABLE_SET = new Set(ENGINE_DEFERRABLE_FIELDS);
|
|
65
|
+
/** Stable descriptor of the contract SHAPE (version + baseline field sets). Folded into the source-
|
|
66
|
+
* observations reuse digest (§12 T2) so editing the baseline set rotates the resume key
|
|
67
|
+
* tautologically — a seed authored under an older/weaker contract then fails the provenance check. */
|
|
68
|
+
export const COMPREHENSION_ARTIFACT_CONTRACT_DESCRIPTOR = {
|
|
69
|
+
version: COMPREHENSION_ARTIFACT_CONTRACT_VERSION,
|
|
70
|
+
deterministic_fields: [...DETERMINISTIC_BASELINE_FIELDS],
|
|
71
|
+
leaf_read_owned_fields: [...LEAF_READ_OWNED_FIELDS],
|
|
72
|
+
engine_deferrable_fields: [...ENGINE_DEFERRABLE_FIELDS],
|
|
73
|
+
};
|
|
74
|
+
const VALID_ABSENCE_STATUS = new Set(["unknown", "deferred", "not_applicable"]);
|
|
75
|
+
const VALID_ATTEMPT_STATUS = new Set([
|
|
76
|
+
"not_attempted",
|
|
77
|
+
"produced",
|
|
78
|
+
"unread",
|
|
79
|
+
"failed",
|
|
80
|
+
]);
|
|
81
|
+
const VALID_SAFETY_TIERS = new Set([
|
|
82
|
+
"consumption_allowed",
|
|
83
|
+
"internal_only",
|
|
84
|
+
"no_prompt_use",
|
|
85
|
+
"no_replay_use",
|
|
86
|
+
]);
|
|
87
|
+
function isAbsence(value) {
|
|
88
|
+
return (typeof value === "object" &&
|
|
89
|
+
value !== null &&
|
|
90
|
+
!Array.isArray(value) &&
|
|
91
|
+
"status" in value &&
|
|
92
|
+
"lineage" in value);
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Completeness + honesty validator (§5.7; §12 T3/T4; §11 R4). Fail-closed. Beyond the P1-C1
|
|
96
|
+
* completeness rules it enforces the gate's convergent anti-loophole contract:
|
|
97
|
+
* - leaf_read_attempt (content↔attempt provenance split) is present, valid, and COUPLED to
|
|
98
|
+
* producer_kind: an 'llm' producer requires attempt='produced'; an unread/failed attempt must
|
|
99
|
+
* degrade to 'deterministic'; a 'produced' attempt requires an 'llm' producer.
|
|
100
|
+
* - when producer_kind='llm', every leaf-read-owned field is PRESENT (never absent) — a failed
|
|
101
|
+
* read cannot pass as a successful production with deferred placeholders.
|
|
102
|
+
* - `deferred` is allowed ONLY for engine-not-yet fields on the stage allowlist.
|
|
103
|
+
* Pushes onto `violations` (mirrors validateSpreadsheetObservationHonesty); empty = valid.
|
|
104
|
+
*/
|
|
105
|
+
export function validateComprehensionArtifact(artifact, violations) {
|
|
106
|
+
if (artifact.contract_version !== COMPREHENSION_ARTIFACT_CONTRACT_VERSION) {
|
|
107
|
+
violations.push(`comprehension_artifact.contract_version must be ${COMPREHENSION_ARTIFACT_CONTRACT_VERSION}`);
|
|
108
|
+
}
|
|
109
|
+
if (!artifact.observation_id || !artifact.observation_id.trim()) {
|
|
110
|
+
violations.push("comprehension_artifact.observation_id is required");
|
|
111
|
+
}
|
|
112
|
+
if (!VALID_SAFETY_TIERS.has(artifact.safety_visibility_tier)) {
|
|
113
|
+
violations.push("comprehension_artifact.safety_visibility_tier is invalid");
|
|
114
|
+
}
|
|
115
|
+
const producerKind = artifact.provenance?.producer_kind;
|
|
116
|
+
if (producerKind !== "deterministic" &&
|
|
117
|
+
producerKind !== "llm" &&
|
|
118
|
+
producerKind !== "vision-assist") {
|
|
119
|
+
violations.push("comprehension_artifact.provenance.producer_kind is invalid");
|
|
120
|
+
}
|
|
121
|
+
// ── attempt provenance + coupling to content provenance (§11 R4) ──
|
|
122
|
+
const attempt = artifact.provenance?.leaf_read_attempt;
|
|
123
|
+
if (!attempt || !VALID_ATTEMPT_STATUS.has(attempt.status)) {
|
|
124
|
+
violations.push("comprehension_artifact.provenance.leaf_read_attempt.status is invalid");
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
if (typeof attempt.lineage !== "string" || attempt.lineage.trim() === "") {
|
|
128
|
+
violations.push("comprehension_artifact.provenance.leaf_read_attempt.lineage must not be blank");
|
|
129
|
+
}
|
|
130
|
+
if (producerKind === "llm" && attempt.status !== "produced") {
|
|
131
|
+
violations.push("comprehension_artifact: producer_kind='llm' requires leaf_read_attempt.status='produced' (an attempted-but-unread/failed read must degrade to a deterministic producer)");
|
|
132
|
+
}
|
|
133
|
+
if ((attempt.status === "unread" || attempt.status === "failed") && producerKind !== "deterministic") {
|
|
134
|
+
violations.push("comprehension_artifact: an unread/failed leaf_read_attempt must degrade to producer_kind='deterministic'");
|
|
135
|
+
}
|
|
136
|
+
if (attempt.status === "produced" && producerKind === "deterministic") {
|
|
137
|
+
violations.push("comprehension_artifact: leaf_read_attempt.status='produced' requires an llm producer_kind");
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
const checkBaseline = (name, value, isLlmTouch) => {
|
|
141
|
+
if (value === undefined || value === null) {
|
|
142
|
+
violations.push(`comprehension_artifact.${name} is silently absent (must be present or explicit)`);
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
if (isAbsence(value)) {
|
|
146
|
+
if (!VALID_ABSENCE_STATUS.has(value.status)) {
|
|
147
|
+
violations.push(`comprehension_artifact.${name} absence status is invalid`);
|
|
148
|
+
}
|
|
149
|
+
if (typeof value.lineage !== "string" || value.lineage.trim() === "") {
|
|
150
|
+
violations.push(`comprehension_artifact.${name} absence lineage must not be blank`);
|
|
151
|
+
}
|
|
152
|
+
// T4: an LLM-touch field may be `not_applicable` ONLY for a deterministic producer.
|
|
153
|
+
if (isLlmTouch && value.status === "not_applicable" && producerKind !== "deterministic") {
|
|
154
|
+
violations.push(`comprehension_artifact.${name} not_applicable is only allowed when producer_kind is deterministic`);
|
|
155
|
+
}
|
|
156
|
+
// §11 R4c: `deferred` is allowed ONLY for engine-not-yet fields on the stage allowlist —
|
|
157
|
+
// a leaf-read-owned field may never be quietly deferred (loophole: onto issue-003).
|
|
158
|
+
if (isLlmTouch && value.status === "deferred" && !ENGINE_DEFERRABLE_SET.has(name)) {
|
|
159
|
+
violations.push(`comprehension_artifact.${name} may not be 'deferred' (not on the P1-C2-A deferred allowlist; a leaf-read-owned field must be produced or the region must degrade to deterministic)`);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
// a PRESENT value satisfies completeness; field-shape checks live in the producers/types.
|
|
163
|
+
};
|
|
164
|
+
for (const name of DETERMINISTIC_BASELINE_FIELDS) {
|
|
165
|
+
checkBaseline(name, artifact[name], false);
|
|
166
|
+
}
|
|
167
|
+
for (const name of LLM_TOUCH_BASELINE_FIELDS) {
|
|
168
|
+
checkBaseline(name, artifact[name], true);
|
|
169
|
+
}
|
|
170
|
+
// §11 R4 required-PRESENT guard: a successful llm production must actually carry leaf-read
|
|
171
|
+
// content — the loophole both families flagged (producer='llm' + all-deferred passing as success).
|
|
172
|
+
if (producerKind === "llm") {
|
|
173
|
+
for (const name of LEAF_READ_OWNED_FIELDS) {
|
|
174
|
+
const value = artifact[name];
|
|
175
|
+
if (isAbsence(value)) {
|
|
176
|
+
violations.push(`comprehension_artifact.${name} must be PRESENT when producer_kind='llm' (leaf-read-owned field cannot be '${value.status}')`);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
const NOT_APPLICABLE_LINEAGE = "no semantic reading for this region; deterministic value-tile sidecar only (LLM leaf-read = P1-C2-A llm edition)";
|
|
182
|
+
const ENGINE_DEFERRED_LINEAGE = "triage·reduce engine not built yet (P1-C2-B/C); this cut reads leaf labels only";
|
|
183
|
+
function notApplicable() {
|
|
184
|
+
return { status: "not_applicable", lineage: NOT_APPLICABLE_LINEAGE };
|
|
185
|
+
}
|
|
186
|
+
function engineDeferred() {
|
|
187
|
+
return { status: "deferred", lineage: ENGINE_DEFERRED_LINEAGE };
|
|
188
|
+
}
|
|
189
|
+
function deriveDeterministicBaseline(inventory) {
|
|
190
|
+
const tiles = inventory.segmented_value_tiles ?? [];
|
|
191
|
+
const boundaries = tiles.flatMap((sheet) => sheet.columns.flatMap((col) => col.intra_tile_notes.map((note) => ({
|
|
192
|
+
sheet: sheet.sheet,
|
|
193
|
+
column_index: col.column_index,
|
|
194
|
+
boundary_kind: note.boundary_kind,
|
|
195
|
+
prev_shape: note.prev_shape,
|
|
196
|
+
new_shape: note.new_shape,
|
|
197
|
+
last_prev_format_row: note.last_prev_format_row,
|
|
198
|
+
first_new_format_row: note.first_new_format_row,
|
|
199
|
+
}))));
|
|
200
|
+
const segmentsCapped = tiles.some((sheet) => sheet.columns.some((col) => col.segments_capped));
|
|
201
|
+
const distinctIsLowerBound = tiles.some((sheet) => sheet.columns.some((col) => col.segments.some((seg) => seg.distinct_is_lower_bound)));
|
|
202
|
+
const sheets = inventory.sheets.map((s) => s.name);
|
|
203
|
+
const rowExtents = inventory.sheets.map((s) => s.dimensions.rows).filter((r) => r > 0);
|
|
204
|
+
const rowSpan = rowExtents.length > 0 ? { min: 1, max: Math.max(...rowExtents) } : null;
|
|
205
|
+
// examples = canonical value-tile witnesses (a bounded rendering of "where the shape/format
|
|
206
|
+
// changes"). The downstream LLM names what each change MEANS; the code never names it.
|
|
207
|
+
const examples = boundaries
|
|
208
|
+
.slice(0, 16)
|
|
209
|
+
.map((b) => `${b.sheet}!col${b.column_index} ${b.boundary_kind} ${b.prev_shape}→${b.new_shape} @row${b.first_new_format_row}`);
|
|
210
|
+
return {
|
|
211
|
+
region_identity: { sheets, row_span: rowSpan },
|
|
212
|
+
value_signature_tile_witness: { boundaries, boundaries_are_lower_bound: segmentsCapped },
|
|
213
|
+
capped_or_frontier_state: { segments_capped: segmentsCapped, distinct_is_lower_bound: distinctIsLowerBound },
|
|
214
|
+
is_lower_bound_by_claim: segmentsCapped || distinctIsLowerBound,
|
|
215
|
+
examples,
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Build the DETERMINISTIC-edition ComprehensionArtifact embedded in the source observation
|
|
220
|
+
* (§4; P1-C1). Pure + LLM-free — the COMPANION that stays Layer-1 (its reuse stays content-hash
|
|
221
|
+
* gated; §11 R1 keeps the LLM leaf-read in a SEPARATE artifact). `leafReadAttempt` defaults to
|
|
222
|
+
* 'not_attempted'; pass 'unread'/'failed' to record an attempted-but-degraded read (§11 R9).
|
|
223
|
+
*/
|
|
224
|
+
export function buildDeterministicComprehensionArtifact(args) {
|
|
225
|
+
const base = deriveDeterministicBaseline(args.inventory);
|
|
226
|
+
return {
|
|
227
|
+
contract_version: COMPREHENSION_ARTIFACT_CONTRACT_VERSION,
|
|
228
|
+
observation_id: args.observationId,
|
|
229
|
+
region_identity: base.region_identity,
|
|
230
|
+
value_signature_tile_witness: base.value_signature_tile_witness,
|
|
231
|
+
provenance: {
|
|
232
|
+
producer_kind: "deterministic",
|
|
233
|
+
epoch_fingerprint_contribution: null,
|
|
234
|
+
leaf_read_attempt: args.leafReadAttempt ?? {
|
|
235
|
+
status: "not_attempted",
|
|
236
|
+
lineage: "deterministic companion; no LLM leaf-read run for this observation",
|
|
237
|
+
},
|
|
238
|
+
},
|
|
239
|
+
safety_visibility_tier: "consumption_allowed",
|
|
240
|
+
capped_or_frontier_state: base.capped_or_frontier_state,
|
|
241
|
+
is_lower_bound_by_claim: base.is_lower_bound_by_claim,
|
|
242
|
+
evidence_quality: "structural_only",
|
|
243
|
+
examples: base.examples,
|
|
244
|
+
spine_claims: notApplicable(),
|
|
245
|
+
confidence_by_claim: notApplicable(),
|
|
246
|
+
limiting_witness: notApplicable(),
|
|
247
|
+
semantic_depth: notApplicable(),
|
|
248
|
+
consumer_handoff_notes: notApplicable(),
|
|
249
|
+
relation_obligation_lifecycle_state: notApplicable(),
|
|
250
|
+
downstream_blocking_semantics: notApplicable(),
|
|
251
|
+
trigger_provenance: notApplicable(),
|
|
252
|
+
triage_audit_status: notApplicable(),
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Build the LLM-edition ComprehensionArtifact — the SEPARATE Layer-2 authored artifact carrying the
|
|
257
|
+
* provisional label read for low-confidence regions (§11 R1/R4/R5/R7/R11). producer_kind='llm',
|
|
258
|
+
* leaf_read_attempt='produced', leaf-read-owned fields PRESENT, engine fields explicitly deferred.
|
|
259
|
+
* `fingerprint` = the llm_touch_fingerprint (ⓐ+ⓑ) gating this artifact's reuse (provenance only).
|
|
260
|
+
* Deterministic baseline fields are still inventory-derived (LLM-free); the artifact is producer_kind
|
|
261
|
+
* 'llm' because it CARRIES llm content.
|
|
262
|
+
*/
|
|
263
|
+
export function buildLlmComprehensionArtifact(args) {
|
|
264
|
+
if (args.leafRead.labels.length === 0) {
|
|
265
|
+
// Guard: an empty read is NOT a production — callers must route it to the degraded
|
|
266
|
+
// deterministic builder (status='unread'). Fail loud rather than emit an llm artifact
|
|
267
|
+
// with no content (which the validator would reject anyway).
|
|
268
|
+
throw new Error("buildLlmComprehensionArtifact requires ≥1 produced label; route an empty read to buildDeterministicComprehensionArtifact({leafReadAttempt:{status:'unread'}})");
|
|
269
|
+
}
|
|
270
|
+
const base = deriveDeterministicBaseline(args.inventory);
|
|
271
|
+
const spineClaims = args.leafRead.labels.map((label) => {
|
|
272
|
+
const claim = {
|
|
273
|
+
claim_kind: "provisional_label_read",
|
|
274
|
+
authority: "non_authoritative",
|
|
275
|
+
sheet: label.sheet,
|
|
276
|
+
column_index: label.column_index,
|
|
277
|
+
tentative_label: label.tentative_label,
|
|
278
|
+
};
|
|
279
|
+
if (label.semantic_role)
|
|
280
|
+
claim.semantic_role = label.semantic_role;
|
|
281
|
+
if (label.captured_note)
|
|
282
|
+
claim.captured_note = label.captured_note;
|
|
283
|
+
return claim;
|
|
284
|
+
});
|
|
285
|
+
const confidenceByClaim = args.leafRead.labels.map((label) => ({
|
|
286
|
+
claim_ref: `${label.sheet}!col${label.column_index}`,
|
|
287
|
+
confidence: label.confidence,
|
|
288
|
+
is_lower_bound: label.is_lower_bound,
|
|
289
|
+
}));
|
|
290
|
+
return {
|
|
291
|
+
contract_version: COMPREHENSION_ARTIFACT_CONTRACT_VERSION,
|
|
292
|
+
observation_id: args.observationId,
|
|
293
|
+
region_identity: base.region_identity,
|
|
294
|
+
value_signature_tile_witness: base.value_signature_tile_witness,
|
|
295
|
+
provenance: {
|
|
296
|
+
producer_kind: "llm",
|
|
297
|
+
epoch_fingerprint_contribution: args.fingerprint,
|
|
298
|
+
leaf_read_attempt: {
|
|
299
|
+
status: "produced",
|
|
300
|
+
lineage: `leaf-read produced ${args.leafRead.labels.length} provisional label(s) for low-confidence region(s)`,
|
|
301
|
+
},
|
|
302
|
+
},
|
|
303
|
+
safety_visibility_tier: "consumption_allowed",
|
|
304
|
+
capped_or_frontier_state: base.capped_or_frontier_state,
|
|
305
|
+
is_lower_bound_by_claim: base.is_lower_bound_by_claim, // DETERMINISTIC; leaf bound lives in confidence_by_claim.
|
|
306
|
+
evidence_quality: "structural_plus_provisional_label",
|
|
307
|
+
examples: base.examples,
|
|
308
|
+
spine_claims: spineClaims,
|
|
309
|
+
confidence_by_claim: confidenceByClaim,
|
|
310
|
+
limiting_witness: {
|
|
311
|
+
region_ref: args.leafRead.limiting_region_ref,
|
|
312
|
+
reason: args.leafRead.limiting_reason,
|
|
313
|
+
},
|
|
314
|
+
semantic_depth: engineDeferred(),
|
|
315
|
+
consumer_handoff_notes: engineDeferred(),
|
|
316
|
+
relation_obligation_lifecycle_state: engineDeferred(),
|
|
317
|
+
downstream_blocking_semantics: engineDeferred(),
|
|
318
|
+
trigger_provenance: engineDeferred(),
|
|
319
|
+
triage_audit_status: engineDeferred(),
|
|
320
|
+
};
|
|
321
|
+
}
|