onto-mcp 0.4.11 → 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.
Files changed (139) hide show
  1. package/.onto/authority/core-lexicon.yaml +1 -1
  2. package/.onto/authority/supported-models.yaml +153 -0
  3. package/.onto/domains/accounting-kr/competency_qs.md +153 -153
  4. package/.onto/processes/reconstruct/obligation-coverage-ledger.yaml +842 -0
  5. package/.onto/processes/reconstruct/obligation-coverage-recorded.yaml +225 -0
  6. package/.onto/processes/reconstruct/ontology-seeding-and-maturation-design.md +93 -54
  7. package/.onto/processes/reconstruct/reconstruct-contract-registry.yaml +202 -49
  8. package/.onto/processes/reconstruct/reconstruct-execution-ux-contract.md +72 -1
  9. package/.onto/processes/reconstruct/source-profiles/spreadsheet.md +35 -0
  10. package/.onto/processes/review/nesting-batch-worker-contract.md +1 -1
  11. package/.onto/processes/review/productized-live-path.md +9 -5
  12. package/.onto/processes/review/prompt-execution-runner-contract.md +32 -8
  13. package/.onto/processes/review/review-target-profile-contract.md +47 -4
  14. package/.onto/processes/shared/pipeline-execution-ledger-contract.md +11 -0
  15. package/.onto/processes/shared/target-material-kind-contract.md +1 -1
  16. package/README.md +64 -1
  17. package/dist/cli.js +11 -1
  18. package/dist/core-api/reconstruct-api.js +679 -18
  19. package/dist/core-api/review-api.js +70 -5
  20. package/dist/core-api/review-progress.js +7 -0
  21. package/dist/core-api/runtime-observation.js +7 -0
  22. package/dist/core-runtime/artifact-io.js +131 -0
  23. package/dist/core-runtime/cli/claude-code-review-unit-executor.js +21 -9
  24. package/dist/core-runtime/cli/claude-nesting-batch-worker.js +4 -2
  25. package/dist/core-runtime/cli/materialize-review-prompt-packets.js +68 -14
  26. package/dist/core-runtime/cli/prepare-review-session.js +34 -1
  27. package/dist/core-runtime/cli/render-review-final-output.js +2 -1
  28. package/dist/core-runtime/cli/run-review-prompt-execution.js +758 -48
  29. package/dist/core-runtime/cli/run-seats.js +18 -0
  30. package/dist/core-runtime/cli/runtime-submit-context.js +10 -2
  31. package/dist/core-runtime/cli/unit-resubmit.js +269 -0
  32. package/dist/core-runtime/cli/worker-structured-output.js +80 -2
  33. package/dist/core-runtime/discovery/host-detection.js +71 -22
  34. package/dist/core-runtime/discovery/review-cert-assemble.js +208 -0
  35. package/dist/core-runtime/discovery/review-cert-record.js +442 -0
  36. package/dist/core-runtime/discovery/review-execution-units.js +22 -0
  37. package/dist/core-runtime/discovery/seat-inventory.js +167 -0
  38. package/dist/core-runtime/discovery/settings-chain.js +311 -52
  39. package/dist/core-runtime/discovery/supported-models.js +289 -25
  40. package/dist/core-runtime/discovery/synthesize-cert-assemble.js +197 -0
  41. package/dist/core-runtime/discovery/synthesize-cert-capsule.js +399 -0
  42. package/dist/core-runtime/discovery/synthesize-cert-judge.js +340 -0
  43. package/dist/core-runtime/discovery/synthesize-cert-loop.js +234 -0
  44. package/dist/core-runtime/discovery/synthesize-cert-mutation.js +117 -0
  45. package/dist/core-runtime/discovery/synthesize-cert-packet.js +182 -0
  46. package/dist/core-runtime/discovery/synthesize-cert-record.js +831 -0
  47. package/dist/core-runtime/discovery/synthesize-cert-sampler.js +336 -0
  48. package/dist/core-runtime/effort-calibration-ingest.js +326 -0
  49. package/dist/core-runtime/effort-calibration-reconstruct.js +47 -0
  50. package/dist/core-runtime/effort-calibration-review.js +22 -0
  51. package/dist/core-runtime/effort-calibration-sweep.js +110 -0
  52. package/dist/core-runtime/effort-frontier.js +134 -0
  53. package/dist/core-runtime/llm/claude-bin.js +77 -0
  54. package/dist/core-runtime/llm/dispatch-breaker.js +412 -0
  55. package/dist/core-runtime/llm/dispatch-fallback-adapter-capabilities.js +33 -0
  56. package/dist/core-runtime/llm/llm-caller.js +316 -35
  57. package/dist/core-runtime/llm/mock-llm-realization.js +35 -4
  58. package/dist/core-runtime/llm/model-switcher.js +1 -1
  59. package/dist/core-runtime/llm/openai-responses-incomplete-error.js +46 -0
  60. package/dist/core-runtime/llm/sealed-dispatch-capability.js +359 -0
  61. package/dist/core-runtime/llm/structured-dispatch-error.js +15 -0
  62. package/dist/core-runtime/onboard/claude-profile-scan.js +65 -0
  63. package/dist/core-runtime/onboard/cli-host.js +5 -2
  64. package/dist/core-runtime/onboard/configure-provider.js +33 -1
  65. package/dist/core-runtime/onboard/host-target.js +28 -3
  66. package/dist/core-runtime/onboard/register.js +54 -12
  67. package/dist/core-runtime/pipeline-execution-ledger.js +10 -0
  68. package/dist/core-runtime/reconstruct/artifact-types.js +37 -0
  69. package/dist/core-runtime/reconstruct/benchmark-evidence.js +16 -0
  70. package/dist/core-runtime/reconstruct/claim-projection-validation.js +19 -5
  71. package/dist/core-runtime/reconstruct/competency-projection-contract.js +63 -0
  72. package/dist/core-runtime/reconstruct/comprehension-artifact.js +321 -0
  73. package/dist/core-runtime/reconstruct/comprehension-reduce.js +367 -0
  74. package/dist/core-runtime/reconstruct/comprehension-semantic-map.js +771 -0
  75. package/dist/core-runtime/reconstruct/contract-registry.js +20 -0
  76. package/dist/core-runtime/reconstruct/directive-validation.js +4 -3
  77. package/dist/core-runtime/reconstruct/dispatch-fallback-artifacts.js +640 -0
  78. package/dist/core-runtime/reconstruct/execution-telemetry.js +84 -9
  79. package/dist/core-runtime/reconstruct/final-output-sections.js +89 -0
  80. package/dist/core-runtime/reconstruct/governing-snapshot.js +36 -1
  81. package/dist/core-runtime/reconstruct/leaf-reader.js +305 -0
  82. package/dist/core-runtime/reconstruct/llm-dispatch-failure.js +270 -0
  83. package/dist/core-runtime/reconstruct/llm-touch-fingerprint.js +94 -0
  84. package/dist/core-runtime/reconstruct/material-admission-validation.js +20 -5
  85. package/dist/core-runtime/reconstruct/material-profile-validation.js +24 -6
  86. package/dist/core-runtime/reconstruct/materialize-preparation.js +380 -23
  87. package/dist/core-runtime/reconstruct/maturation-validation.js +1522 -67
  88. package/dist/core-runtime/reconstruct/mock-llm-realization.js +120 -1
  89. package/dist/core-runtime/reconstruct/obligation-assertion.js +18 -0
  90. package/dist/core-runtime/reconstruct/ontology-seed-validation.js +39 -7
  91. package/dist/core-runtime/reconstruct/output-budget.js +6 -0
  92. package/dist/core-runtime/reconstruct/pipeline-execution-ledger.js +112 -5
  93. package/dist/core-runtime/reconstruct/post-seed-validation.js +122 -11
  94. package/dist/core-runtime/reconstruct/proof-authority-validation.js +2 -6
  95. package/dist/core-runtime/reconstruct/purpose-authority-validation.js +119 -7
  96. package/dist/core-runtime/reconstruct/record.js +49 -3
  97. package/dist/core-runtime/reconstruct/registry-verification-validation.js +41 -5
  98. package/dist/core-runtime/reconstruct/run-control-validation.js +1019 -37
  99. package/dist/core-runtime/reconstruct/run.js +5409 -517
  100. package/dist/core-runtime/reconstruct/seed-authoring-readiness-validation.js +56 -8
  101. package/dist/core-runtime/reconstruct/semantic-quality-gate.js +112 -7
  102. package/dist/core-runtime/reconstruct/source-observation-delta-validation.js +53 -5
  103. package/dist/core-runtime/reconstruct/source-observations.js +166 -0
  104. package/dist/core-runtime/reconstruct/source-safety-validation.js +77 -188
  105. package/dist/core-runtime/reconstruct/source-scout-pack-validation.js +50 -6
  106. package/dist/core-runtime/reconstruct/terminal-validation.js +145 -13
  107. package/dist/core-runtime/review/continuation-plan.js +12 -5
  108. package/dist/core-runtime/review/issue-artifact-runtime.js +4 -1
  109. package/dist/core-runtime/review/materializers.js +100 -11
  110. package/dist/core-runtime/review/nesting-batch.js +6 -2
  111. package/dist/core-runtime/review/obligation-shardability.js +138 -0
  112. package/dist/core-runtime/review/pipeline-execution-ledger.js +42 -2
  113. package/dist/core-runtime/review/review-artifact-utils.js +219 -7
  114. package/dist/core-runtime/review/review-execution-steps.js +32 -8
  115. package/dist/core-runtime/review/review-progress-contract.js +8 -0
  116. package/dist/core-runtime/review/review-prompt-budget.js +97 -0
  117. package/dist/core-runtime/review/semantic-quality-gate.js +33 -7
  118. package/dist/core-runtime/review/spreadsheet-review-disposition.js +94 -0
  119. package/dist/core-runtime/route-identity.js +182 -0
  120. package/dist/core-runtime/spreadsheet-header-escalation.js +204 -0
  121. package/dist/core-runtime/spreadsheet-structure-observer.js +2689 -0
  122. package/dist/core-runtime/target-material-kind.js +58 -0
  123. package/dist/mcp/server.js +27 -5
  124. package/dist/mcp/tool-schemas.js +8 -0
  125. package/dist/tui/app.js +167 -0
  126. package/dist/tui/data/event-follower.js +116 -0
  127. package/dist/tui/data/node-detail.js +42 -0
  128. package/dist/tui/data/projection-poll.js +26 -0
  129. package/dist/tui/data/session-discovery.js +65 -0
  130. package/dist/tui/index.js +108 -0
  131. package/dist/tui/screens/log.js +25 -0
  132. package/dist/tui/screens/node-detail.js +17 -0
  133. package/dist/tui/screens/session-selector.js +26 -0
  134. package/dist/tui/screens/workflow-tree.js +90 -0
  135. package/dist/tui/view-model/reconstruct-adapter.js +162 -0
  136. package/dist/tui/view-model/review-adapter.js +270 -0
  137. package/dist/tui/view-model/tree-view-model.js +19 -0
  138. package/package.json +20 -3
  139. 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
- function resolveSelection(parsed, targets) {
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.map((t) => t.id);
110
+ return targets;
95
111
  if (Array.isArray(parsed.hosts)) {
96
112
  const requested = new Set(parsed.hosts);
97
- return targets.map((t) => t.id).filter((id) => requested.has(id));
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
- const targets = deps.targets ??
143
- getDefaultHostTargets(parsed.claudeConfigDir ? { claudeConfigDir: parsed.claudeConfigDir } : {});
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 selected = resolveSelection(parsed, targets);
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((t) => t.id);
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.id,
211
+ id: targetKey(t),
169
212
  label: t.displayName,
170
213
  detail: detectionLabel(t.detect()),
171
- })), defaults.length > 0 ? defaults : targets.map((t) => t.id));
172
- selected = targets.map((t) => t.id).filter((id) => chosen.includes(id));
214
+ })), defaults.length > 0 ? defaults : targets.map(targetKey));
215
+ selectedTargets = targets.filter((t) => chosen.includes(targetKey(t)));
173
216
  }
174
- if (selected.length === 0) {
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",
@@ -70,6 +89,8 @@ export const RECONSTRUCT_STAGE_IDS = [
70
89
  "maturation_authority_response_validation",
71
90
  "answer_support_ledger",
72
91
  "answer_support_ledger_validation",
92
+ "answer_support_judgment",
93
+ "answer_support_judgment_validation",
73
94
  "maturation_answer_claims",
74
95
  "maturation_answer_claims_validation",
75
96
  "ontology_expansion",
@@ -98,3 +119,19 @@ export const RECONSTRUCT_STAGE_IDS = [
98
119
  "record_assembly",
99
120
  "post_publication_run_manifest_validation",
100
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
+ ];
@@ -20,6 +20,22 @@ export const BENCHMARK_DECISION_GRADE_STATUS = "decision-grade";
20
20
  export function requestedEffortForRealization(realization, effort) {
21
21
  return realization === "live" ? (effort ?? null) : null;
22
22
  }
23
+ /**
24
+ * Realization-scoped record of the opt-in answer-support judge override that
25
+ * was REQUESTED for a benchmark run (symmetric to {@link
26
+ * requestedEffortForRealization}). Live-only — the mock route never applies the
27
+ * override — and null when neither lever was requested, so a record always
28
+ * self-describes whether a judge override was in play (reproducibility). The
29
+ * REALIZED judge model/effort remains recoverable from the answer_support_judgment
30
+ * unit telemetry; this captures the operator's intent.
31
+ */
32
+ export function requestedJudgeOverrideForRealization(realization, judgeEffort, judgeModel) {
33
+ if (realization !== "live")
34
+ return null;
35
+ if (!judgeEffort && !judgeModel)
36
+ return null;
37
+ return { effort: judgeEffort ?? null, model: judgeModel ?? null };
38
+ }
23
39
  export function gradeBenchmarkEvidence(input) {
24
40
  const performanceEvidenceMet = input.repetitions >= 3 &&
25
41
  input.fixtureCount >= 2 &&
@@ -1,6 +1,8 @@
1
1
  import fs from "node:fs/promises";
2
2
  import path from "node:path";
3
- import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
3
+ import { parse as parseYaml } from "yaml";
4
+ import { atomicWriteYamlDocument as writeYamlDocument } from "../artifact-io.js";
5
+ import { assertObligation } from "./obligation-assertion.js";
4
6
  const PROJECTION_SURFACES = [
5
7
  "status",
6
8
  "result",
@@ -49,10 +51,6 @@ function isoNow() {
49
51
  async function readYamlDocument(filePath) {
50
52
  return parseYaml(await fs.readFile(filePath, "utf8"));
51
53
  }
52
- async function writeYamlDocument(filePath, value) {
53
- await fs.mkdir(path.dirname(filePath), { recursive: true });
54
- await fs.writeFile(filePath, stringifyYaml(value), "utf8");
55
- }
56
54
  function statusOf(validation) {
57
55
  return validation?.validation_status ?? "not_available";
58
56
  }
@@ -392,6 +390,21 @@ function sameJson(left, right) {
392
390
  }
393
391
  export function validateClaimProjection(args) {
394
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");
395
408
  const seenIds = new Set();
396
409
  const surfaces = new Set();
397
410
  const decisionStateCounts = Object.fromEntries(DECISION_STATES.map((state) => [state, 0]));
@@ -728,6 +741,7 @@ export function validateClaimProjection(args) {
728
741
  validation_results: violations.length === 0
729
742
  ? ["claim_projection_valid"]
730
743
  : ["claim_projection_invalid"],
744
+ asserted_obligation_ids: assertedObligationIds,
731
745
  violations,
732
746
  };
733
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
+ }