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.
Files changed (128) hide show
  1. package/.onto/authority/core-lexicon.yaml +1 -1
  2. package/.onto/authority/supported-models.yaml +137 -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 +169 -8
  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/productized-live-path.md +9 -5
  11. package/.onto/processes/review/prompt-execution-runner-contract.md +32 -8
  12. package/.onto/processes/review/review-target-profile-contract.md +47 -4
  13. package/.onto/processes/shared/pipeline-execution-ledger-contract.md +11 -0
  14. package/.onto/processes/shared/target-material-kind-contract.md +1 -1
  15. package/README.md +64 -1
  16. package/dist/cli.js +11 -1
  17. package/dist/core-api/reconstruct-api.js +575 -24
  18. package/dist/core-api/review-api.js +70 -5
  19. package/dist/core-api/review-progress.js +7 -0
  20. package/dist/core-api/runtime-observation.js +7 -0
  21. package/dist/core-runtime/artifact-io.js +75 -3
  22. package/dist/core-runtime/cli/claude-code-review-unit-executor.js +19 -8
  23. package/dist/core-runtime/cli/materialize-review-prompt-packets.js +68 -14
  24. package/dist/core-runtime/cli/prepare-review-session.js +34 -1
  25. package/dist/core-runtime/cli/render-review-final-output.js +2 -1
  26. package/dist/core-runtime/cli/run-review-prompt-execution.js +758 -48
  27. package/dist/core-runtime/cli/run-seats.js +18 -0
  28. package/dist/core-runtime/cli/runtime-submit-context.js +10 -2
  29. package/dist/core-runtime/cli/unit-resubmit.js +269 -0
  30. package/dist/core-runtime/cli/worker-structured-output.js +80 -2
  31. package/dist/core-runtime/discovery/host-detection.js +71 -22
  32. package/dist/core-runtime/discovery/review-cert-assemble.js +208 -0
  33. package/dist/core-runtime/discovery/review-cert-record.js +442 -0
  34. package/dist/core-runtime/discovery/review-execution-units.js +22 -0
  35. package/dist/core-runtime/discovery/seat-inventory.js +167 -0
  36. package/dist/core-runtime/discovery/settings-chain.js +311 -52
  37. package/dist/core-runtime/discovery/supported-models.js +282 -29
  38. package/dist/core-runtime/discovery/synthesize-cert-assemble.js +197 -0
  39. package/dist/core-runtime/discovery/synthesize-cert-capsule.js +399 -0
  40. package/dist/core-runtime/discovery/synthesize-cert-judge.js +340 -0
  41. package/dist/core-runtime/discovery/synthesize-cert-loop.js +234 -0
  42. package/dist/core-runtime/discovery/synthesize-cert-mutation.js +117 -0
  43. package/dist/core-runtime/discovery/synthesize-cert-packet.js +182 -0
  44. package/dist/core-runtime/discovery/synthesize-cert-record.js +831 -0
  45. package/dist/core-runtime/discovery/synthesize-cert-sampler.js +336 -0
  46. package/dist/core-runtime/effort-calibration-ingest.js +135 -0
  47. package/dist/core-runtime/llm/dispatch-breaker.js +412 -0
  48. package/dist/core-runtime/llm/dispatch-fallback-adapter-capabilities.js +33 -0
  49. package/dist/core-runtime/llm/llm-caller.js +188 -47
  50. package/dist/core-runtime/llm/mock-llm-realization.js +35 -4
  51. package/dist/core-runtime/llm/model-switcher.js +1 -1
  52. package/dist/core-runtime/llm/openai-responses-incomplete-error.js +46 -0
  53. package/dist/core-runtime/llm/sealed-dispatch-capability.js +359 -0
  54. package/dist/core-runtime/llm/structured-dispatch-error.js +15 -0
  55. package/dist/core-runtime/onboard/claude-profile-scan.js +65 -0
  56. package/dist/core-runtime/onboard/cli-host.js +5 -2
  57. package/dist/core-runtime/onboard/configure-provider.js +33 -1
  58. package/dist/core-runtime/onboard/host-target.js +28 -3
  59. package/dist/core-runtime/onboard/register.js +54 -12
  60. package/dist/core-runtime/pipeline-execution-ledger.js +10 -0
  61. package/dist/core-runtime/reconstruct/artifact-types.js +35 -0
  62. package/dist/core-runtime/reconstruct/claim-projection-validation.js +17 -0
  63. package/dist/core-runtime/reconstruct/competency-projection-contract.js +63 -0
  64. package/dist/core-runtime/reconstruct/comprehension-artifact.js +321 -0
  65. package/dist/core-runtime/reconstruct/comprehension-reduce.js +367 -0
  66. package/dist/core-runtime/reconstruct/comprehension-semantic-map.js +771 -0
  67. package/dist/core-runtime/reconstruct/contract-registry.js +20 -0
  68. package/dist/core-runtime/reconstruct/dispatch-fallback-artifacts.js +640 -0
  69. package/dist/core-runtime/reconstruct/execution-telemetry.js +83 -9
  70. package/dist/core-runtime/reconstruct/final-output-sections.js +89 -0
  71. package/dist/core-runtime/reconstruct/governing-snapshot.js +36 -1
  72. package/dist/core-runtime/reconstruct/leaf-reader.js +305 -0
  73. package/dist/core-runtime/reconstruct/llm-dispatch-failure.js +270 -0
  74. package/dist/core-runtime/reconstruct/llm-touch-fingerprint.js +94 -0
  75. package/dist/core-runtime/reconstruct/material-admission-validation.js +18 -0
  76. package/dist/core-runtime/reconstruct/material-profile-validation.js +14 -0
  77. package/dist/core-runtime/reconstruct/materialize-preparation.js +304 -18
  78. package/dist/core-runtime/reconstruct/maturation-validation.js +1231 -53
  79. package/dist/core-runtime/reconstruct/mock-llm-realization.js +107 -3
  80. package/dist/core-runtime/reconstruct/obligation-assertion.js +18 -0
  81. package/dist/core-runtime/reconstruct/ontology-seed-validation.js +27 -0
  82. package/dist/core-runtime/reconstruct/output-budget.js +6 -0
  83. package/dist/core-runtime/reconstruct/pipeline-execution-ledger.js +87 -3
  84. package/dist/core-runtime/reconstruct/post-seed-validation.js +118 -5
  85. package/dist/core-runtime/reconstruct/purpose-authority-validation.js +117 -2
  86. package/dist/core-runtime/reconstruct/record.js +42 -0
  87. package/dist/core-runtime/reconstruct/registry-verification-validation.js +16 -0
  88. package/dist/core-runtime/reconstruct/run-control-validation.js +1011 -34
  89. package/dist/core-runtime/reconstruct/run.js +5198 -536
  90. package/dist/core-runtime/reconstruct/seed-authoring-readiness-validation.js +54 -3
  91. package/dist/core-runtime/reconstruct/semantic-quality-gate.js +105 -2
  92. package/dist/core-runtime/reconstruct/source-observation-delta-validation.js +41 -0
  93. package/dist/core-runtime/reconstruct/source-observations.js +166 -0
  94. package/dist/core-runtime/reconstruct/source-safety-validation.js +71 -183
  95. package/dist/core-runtime/reconstruct/source-scout-pack-validation.js +37 -1
  96. package/dist/core-runtime/reconstruct/terminal-validation.js +132 -2
  97. package/dist/core-runtime/review/continuation-plan.js +12 -5
  98. package/dist/core-runtime/review/issue-artifact-runtime.js +4 -1
  99. package/dist/core-runtime/review/materializers.js +100 -11
  100. package/dist/core-runtime/review/nesting-batch.js +6 -2
  101. package/dist/core-runtime/review/obligation-shardability.js +138 -0
  102. package/dist/core-runtime/review/pipeline-execution-ledger.js +42 -2
  103. package/dist/core-runtime/review/review-artifact-utils.js +217 -5
  104. package/dist/core-runtime/review/review-execution-steps.js +32 -8
  105. package/dist/core-runtime/review/review-progress-contract.js +8 -0
  106. package/dist/core-runtime/review/review-prompt-budget.js +97 -0
  107. package/dist/core-runtime/review/semantic-quality-gate.js +33 -7
  108. package/dist/core-runtime/review/spreadsheet-review-disposition.js +94 -0
  109. package/dist/core-runtime/route-identity.js +182 -0
  110. package/dist/core-runtime/spreadsheet-header-escalation.js +204 -0
  111. package/dist/core-runtime/spreadsheet-structure-observer.js +2689 -0
  112. package/dist/core-runtime/target-material-kind.js +58 -0
  113. package/dist/mcp/server.js +12 -5
  114. package/dist/tui/app.js +167 -0
  115. package/dist/tui/data/event-follower.js +116 -0
  116. package/dist/tui/data/node-detail.js +42 -0
  117. package/dist/tui/data/projection-poll.js +26 -0
  118. package/dist/tui/data/session-discovery.js +65 -0
  119. package/dist/tui/index.js +108 -0
  120. package/dist/tui/screens/log.js +25 -0
  121. package/dist/tui/screens/node-detail.js +17 -0
  122. package/dist/tui/screens/session-selector.js +26 -0
  123. package/dist/tui/screens/workflow-tree.js +90 -0
  124. package/dist/tui/view-model/reconstruct-adapter.js +162 -0
  125. package/dist/tui/view-model/review-adapter.js +270 -0
  126. package/dist/tui/view-model/tree-view-model.js +19 -0
  127. package/package.json +18 -3
  128. package/settings.example.json +77 -16
@@ -3,6 +3,44 @@ import { ontologySeedClaimProjections } from "./seed-claim-projections.js";
3
3
  export const RECONSTRUCT_MOCK_REALIZATION_ENV = REVIEW_MOCK_REALIZATION_ENV;
4
4
  export const RECONSTRUCT_MOCK_AUTHOR_ID = "reconstruct-mock-semantic-author";
5
5
  export const RECONSTRUCT_MOCK_CONFIRMATION_PROVIDER_ID = "reconstruct-mock-confirmation-provider";
6
+ /**
7
+ * Layer-2 semantic-map author capability — MOCK realization (wiring design 20260702 §7-W5).
8
+ *
9
+ * Wraps a directive author with the deterministic capability PAIR (the production direct-call
10
+ * author deliberately does NOT implement it — real-LLM authoring is a separate owner-approved
11
+ * cut, so the live path stays default-off). Pure functions of their input:
12
+ * - synthesize: one boundary at the node's first value-shape seam when one exists (reconciled
13
+ * anchored) plus ALWAYS one at row_start — emission is unconditional; the deterministic
14
+ * reconcile CLASSIFIES it (unanchored → adversarial verify path — whenever no seam lies
15
+ * within ±1 of row_start, which holds for every non-seam-adjacent region start);
16
+ * - verify: confirmed on even rows, refuted on odd (exercises kept AND disclosed dispositions).
17
+ * The two-path description above is the mock's FUNCTION CONTRACT given seam-ful input; the W5
18
+ * E2E fixture is seam-less through the production observer (adversary F1, probe-confirmed), so
19
+ * the E2E itself traverses only the unanchored→refuted path — anchored/confirmed execution
20
+ * coverage lives in semantic-map-stage.test.ts.
21
+ * Mock output verifies wiring/contracts only — never product semantic quality.
22
+ */
23
+ export function withMockSemanticMapCapability(author) {
24
+ return {
25
+ ...author,
26
+ async synthesizeSemanticMapNode(input) {
27
+ const seam = input.value_shape_seams[0];
28
+ return {
29
+ semantic_summary: `mock ${input.node_ref.sheet}#${input.node_ref.column_index}:` +
30
+ `${input.node_ref.row_start}-${input.node_ref.row_end} kids=${input.child_summaries.length}`,
31
+ boundaries: [
32
+ ...(seam
33
+ ? [{ row: seam.row, character_before: "seam-prev", character_after: "seam-next" }]
34
+ : []),
35
+ { row: input.node_ref.row_start, character_before: "prev", character_after: "next" },
36
+ ],
37
+ };
38
+ },
39
+ async verifySemanticMapBoundary(input) {
40
+ return input.boundary.row % 2 === 0 ? "adversarial_confirmed" : "adversarial_refuted";
41
+ },
42
+ };
43
+ }
6
44
  export function isReconstructMockLlmRealizationEnabled(env = process.env) {
7
45
  return env[RECONSTRUCT_MOCK_REALIZATION_ENV] === "1";
8
46
  }
@@ -805,9 +843,38 @@ export function callReconstructMockLlm(systemPrompt, userPrompt) {
805
843
  });
806
844
  }
807
845
  else if (systemPrompt.includes("Author answer-support-ledger.yaml")) {
808
- text = JSON.stringify({
809
- evidence_clusters: [],
810
- });
846
+ // Defect-3 G4: derive a deterministic direct_authority cluster from the
847
+ // prompt's frontier questions + prompt-visible observations. With the shared
848
+ // empty-frontier mock (questions:[]) this yields [] so the ~12 full-pipeline
849
+ // completion tests are unaffected; a non-empty-frontier fixture exercises the
850
+ // real G1/G2/G3 per-evidence-ref path (the path the empty mock hid).
851
+ const frontierQuestions = (input.questions ?? []);
852
+ const promptVisibleObservationIds = (input.prompt_visible_observation_ids ?? []);
853
+ const questionRefs = frontierQuestions
854
+ .map((question) => question.question_id)
855
+ .filter((id) => typeof id === "string" && id.length > 0);
856
+ if (questionRefs.length === 0 || promptVisibleObservationIds.length === 0) {
857
+ text = JSON.stringify({ evidence_clusters: [] });
858
+ }
859
+ else {
860
+ text = JSON.stringify({
861
+ evidence_clusters: [
862
+ {
863
+ evidence_cluster_id: "mock-evidence-cluster-1",
864
+ question_refs: questionRefs,
865
+ support_mode: "direct_authority",
866
+ proposed_answer_summary: "Mock direct-authority support derived from the observed runtime-target source.",
867
+ evidence_observation_ids: [promptVisibleObservationIds[0]],
868
+ proof_refs: [],
869
+ user_confirmation_refs: [],
870
+ authority_response_refs: [],
871
+ independence_basis: "Deterministic mock structural observation of the runtime-target source.",
872
+ contradiction_refs: [],
873
+ limitation_refs: [],
874
+ },
875
+ ],
876
+ });
877
+ }
811
878
  }
812
879
  else if (systemPrompt.includes("Author answer-support-judgment.yaml")) {
813
880
  // Mirrors the judge author payload shape: per-cluster
@@ -834,6 +901,43 @@ export function callReconstructMockLlm(systemPrompt, userPrompt) {
834
901
  expansions: [],
835
902
  });
836
903
  }
904
+ else if (systemPrompt.includes("Read provisional column labels for a low-confidence")) {
905
+ // P1-C2-A §11 R10 / P1-C2-B′ §3: the leaf-reader (CAPTURE) fixture branch. Returns one
906
+ // provisional label per known column PLUS a deterministic capture (role/note) so the generalized
907
+ // capture path is exercised. The mock model_id is a constant ("reconstruct-mock-model" in the
908
+ // result below), so the model-identity-rotation test must mutate the PRODUCTION LlmCallConfig,
909
+ // never this constant (avoids CG-2 contamination).
910
+ const columns = (input.columns ?? []);
911
+ // Deterministic role rotation across the bounded vocabulary (mock only — no domain meaning).
912
+ const mockRoles = ["category", "measure", "identifier", "free_text", "reference"];
913
+ text = JSON.stringify({
914
+ labels: columns.map((column, i) => ({
915
+ column_index: column.column_index,
916
+ tentative_label: `provisional column ${column.column_index}`,
917
+ semantic_role: mockRoles[i % mockRoles.length],
918
+ captured_note: `mock capture for column ${column.column_index}`,
919
+ })),
920
+ unread_columns: [],
921
+ });
922
+ }
923
+ else if (systemPrompt.includes("Select spreadsheet cell locations")) {
924
+ // Maturation value-read location pick (design §15.4). Echo the FIRST allowed grid scope so an
925
+ // incidental value-read exercises the cell-read path; an empty allowed set yields no pick (honest).
926
+ const allowed = (input.allowed_locations ?? []);
927
+ text = JSON.stringify({
928
+ picked_locations: allowed.length > 0 ? [allowed[0]] : [],
929
+ });
930
+ }
931
+ else if (systemPrompt.includes("Judge whether read spreadsheet cell values")) {
932
+ // Value-read judgment (design §15.4). Default INCONCLUSIVE so an incidental value-read in an
933
+ // existing full mock run discharges nothing (regression-0 / byte-parity — never flips a row to
934
+ // value_resolved). A dedicated value-read E2E drives `satisfied` via a stub llmCall, not this
935
+ // dispatcher. Honest "ran but discharged zero".
936
+ text = JSON.stringify({
937
+ satisfaction_status: "inconclusive",
938
+ rationale: "mock value-read judgment: read values do not decide the limitation",
939
+ });
940
+ }
837
941
  else if (systemPrompt.includes("writing the final reconstruct result")) {
838
942
  text = [
839
943
  "# Reconstruct Result",
@@ -0,0 +1,18 @@
1
+ /**
2
+ * G(a) — obligation recorder (INV-OBLIGATION-COVERAGE-1).
3
+ *
4
+ * A validator calls `assertObligation(acc, id)` at an UNCONDITIONAL position before each
5
+ * check-block's guard, accumulating the obligation ids whose enforcer block was reached.
6
+ * The accumulator is emitted as `asserted_obligation_ids: string[]` on the validation
7
+ * artifact (off the reuse-hash five). The obligation-coverage harvest test re-derives this
8
+ * set dynamically (deleting the call reds the harvest) and the gate proves every active
9
+ * `(validator_id, obligation_id)` is either recorded here or parked in the pending ledger.
10
+ *
11
+ * Scope honesty: this records that control REACHED the enforcer block — NOT that the
12
+ * enforcer is semantically correct. Idempotent: a repeat id is not pushed twice.
13
+ */
14
+ export function assertObligation(asserted, obligationId) {
15
+ if (!asserted.includes(obligationId)) {
16
+ asserted.push(obligationId);
17
+ }
18
+ }
@@ -3,6 +3,7 @@ import path from "node:path";
3
3
  import { parse as parseYaml } from "yaml";
4
4
  import { assertArrayField, atomicWriteYamlDocument as writeYamlDocument } from "../artifact-io.js";
5
5
  import { loadReconstructContractRegistry, } from "./contract-registry.js";
6
+ import { assertObligation } from "./obligation-assertion.js";
6
7
  function isoNow() {
7
8
  return new Date().toISOString();
8
9
  }
@@ -199,6 +200,17 @@ function candidateValidationViolation(args) {
199
200
  export function validateCandidateDisposition(args) {
200
201
  assertArrayField(args.sourceObservations.observations, "source-observations", "observations");
201
202
  const violations = [];
203
+ // G(a) obligation recorder (INV-OBLIGATION-COVERAGE-1): record that control reached the
204
+ // per-candidate / per-disposition enforcement loops below. Unconditional, before any per-row
205
+ // guard so a zero-candidate/zero-disposition input still stamps. Only the four obligations with a
206
+ // distinct, name-matching enforcer are recorded; salience-scoped and surface/purpose/limitation/
207
+ // frontier obligations are PARKED (this validator is salience-blind and takes no purpose-frame or
208
+ // surface input) — see obligation-coverage-ledger.yaml.
209
+ const assertedObligationIds = [];
210
+ assertObligation(assertedObligationIds, "validate_candidate_inventory_candidate_kind_against_candidate_kind_registry");
211
+ assertObligation(assertedObligationIds, "validate_candidate_disposition_against_candidate_disposition_registry");
212
+ assertObligation(assertedObligationIds, "require_rationale_and_evidence_refs_for_each_disposition");
213
+ assertObligation(assertedObligationIds, "validate_promoted_candidate_target_seed_refs_are_declared_for_promoted_dispositions");
202
214
  const addShapeViolation = (_code, message) => {
203
215
  violations.push(candidateValidationViolation({
204
216
  code: "schema_shape_invalid",
@@ -387,6 +399,7 @@ export function validateCandidateDisposition(args) {
387
399
  validation_results: violations.length === 0
388
400
  ? ["candidate_disposition_valid"]
389
401
  : ["candidate_disposition_invalid"],
402
+ asserted_obligation_ids: assertedObligationIds,
390
403
  violations,
391
404
  };
392
405
  }
@@ -737,6 +750,19 @@ export function validateOntologySeed(args) {
737
750
  const addShapeViolation = (_code, message) => {
738
751
  violations.push(seedValidationViolation({ code: "schema_shape_invalid", message }));
739
752
  };
753
+ // G(a) slice 26: record the six obligations this validator FULLY enforces with a distinct,
754
+ // name-matching site (stamped before the per-root/per-row guards so a zero-row seed still records).
755
+ // Parked (see obligation-coverage-ledger.yaml notes): surface_dimension_closure / closure_status /
756
+ // mixed-member-lineage / dynamic_boundaries name fields this function never reads; the three
757
+ // purpose-projection/confirmation obligations are activation_gated_dormant (their conditional inputs
758
+ // — source-purpose-candidates-validation / purpose-confirmation-validation — are never received here).
759
+ const assertedObligationIds = [];
760
+ assertObligation(assertedObligationIds, "require_static_kinetic_dynamic_actionability_coverage_axes");
761
+ assertObligation(assertedObligationIds, "validate_purpose_required_element_seed_ref_refs_against_known_seed_refs");
762
+ assertObligation(assertedObligationIds, "validate_promoted_candidate_target_seed_refs_are_realized");
763
+ assertObligation(assertedObligationIds, "validate_instance_assertion_mapping_status");
764
+ assertObligation(assertedObligationIds, "require_limitation_ref_when_instance_availability_status_is_absent_or_unknown");
765
+ assertObligation(assertedObligationIds, "require_ready_ontology_handoff_mappings_to_have_substantive_content_or_limitation_refs");
740
766
  const seed = readRecord(args.ontologySeed, "ontology_seed", addShapeViolation);
741
767
  const disposition = readRecord(args.candidateDisposition, "candidate_disposition", addShapeViolation);
742
768
  const sessionId = optionalString(disposition?.session_id) ??
@@ -1914,6 +1940,7 @@ export function validateOntologySeed(args) {
1914
1940
  validation_results: violations.length === 0
1915
1941
  ? ["ontology_seed_valid"]
1916
1942
  : ["ontology_seed_invalid"],
1943
+ asserted_obligation_ids: assertedObligationIds,
1917
1944
  violations,
1918
1945
  };
1919
1946
  }
@@ -0,0 +1,6 @@
1
+ export const RECONSTRUCT_SEMANTIC_AUTHOR_OUTPUT_CEILINGS = {
2
+ candidate_disposition: 4_000,
3
+ ontology_seed: 9_000,
4
+ json_parse_repair: 16_000,
5
+ };
6
+ export const RECONSTRUCT_SEMANTIC_AUTHOR_MAX_BASE_OUTPUT_TOKENS = Math.max(...Object.values(RECONSTRUCT_SEMANTIC_AUTHOR_OUTPUT_CEILINGS));
@@ -3,6 +3,7 @@ import path from "node:path";
3
3
  import { parse as parseYaml } from "yaml";
4
4
  import { PIPELINE_EXECUTION_LEDGER_SCHEMA_VERSION, buildLedgerTrust, buildOutputHashes, isTrustedLedgerUnit, normalizeLedgerRefs, } from "../pipeline-execution-ledger.js";
5
5
  import { terminalFailureMessageFromTelemetry } from "./execution-telemetry.js";
6
+ import { DispatchFallbackOutcomeSchema, projectDispatchFallbackRecordBlock, } from "./dispatch-fallback-artifacts.js";
6
7
  const RECONSTRUCT_LEDGER_STAGE_SPECS = [
7
8
  {
8
9
  unitId: "run_control",
@@ -193,6 +194,23 @@ const RECONSTRUCT_LEDGER_STAGE_SPECS = [
193
194
  "source_observation_reentry_validation",
194
195
  ],
195
196
  },
197
+ {
198
+ // Layer-2 semantic_map stage (W3 review: codex W3-002 ≡ onto issue-001/002/006 — 4-lens
199
+ // convergence on audit-graph registration). The LIVE run does not consume this ledger (it is a
200
+ // post-hoc audit projection derived from the record), so this row is descriptive evidence, not
201
+ // continuation authority. Authored-artifact reuse still folds the stage fingerprint into the
202
+ // seed key; breaker recovery is stage-local and consumes dispatch-incomplete.yaml only after
203
+ // semantic-map-resume-validation.yaml validates retained census/sidecar rows. unitKind is NOT
204
+ // "semantic_map" — that kind name is already taken by claim_realization (pre-existing vocabulary
205
+ // collision).
206
+ unitId: "semantic_map",
207
+ unitKind: "semantic_map_accumulation",
208
+ owner: "host_llm",
209
+ artifactKey: "semantic_map_census",
210
+ // DATA dependency (ultracode audit E: the earlier lineage-validation edge was a false/temporal
211
+ // edge — the stage reads source_observations' inventory tiles, nothing from the lineage index).
212
+ upstreamUnitIds: ["source_observation"],
213
+ },
196
214
  {
197
215
  unitId: "source_purpose_candidates",
198
216
  unitKind: "semantic_source_purpose",
@@ -286,6 +304,16 @@ const RECONSTRUCT_LEDGER_STAGE_SPECS = [
286
304
  artifactKey: "seed_authoring_readiness_validation",
287
305
  upstreamUnitIds: ["seed_authoring_readiness"],
288
306
  },
307
+ {
308
+ // M3c: runtime snapshot of the pre-maturation seed-stage projected observations,
309
+ // captured at the seed-authoring gate; the conserved authority the resume truncation
310
+ // fallback measures. Runtime-owned, derived from the source-observation unit.
311
+ unitId: "seed_stage_prompt_source_observations",
312
+ unitKind: "source_observation_snapshot",
313
+ owner: "runtime",
314
+ artifactKey: "seed_stage_prompt_source_observations",
315
+ upstreamUnitIds: ["source_observation", "seed_authoring_readiness_validation"],
316
+ },
289
317
  {
290
318
  unitId: "ontology_seed",
291
319
  unitKind: "semantic_ontology_seed",
@@ -633,6 +661,7 @@ const RECONSTRUCT_LEDGER_STAGE_SPECS = [
633
661
  "baseline_actionability_matrix_validation",
634
662
  "maturation_answer_claims_validation",
635
663
  "ontology_expansion_validation",
664
+ "maturation_question_frontier_validation",
636
665
  ],
637
666
  },
638
667
  {
@@ -645,6 +674,7 @@ const RECONSTRUCT_LEDGER_STAGE_SPECS = [
645
674
  "maturation_baseline_validation",
646
675
  "maturation_answer_claims_validation",
647
676
  "ontology_expansion_validation",
677
+ "maturation_question_frontier_validation",
648
678
  ],
649
679
  },
650
680
  {
@@ -842,6 +872,19 @@ const RECONSTRUCT_LEDGER_STAGE_SPECS = [
842
872
  upstreamUnitIds: ["record_assembly"],
843
873
  },
844
874
  ];
875
+ export function reconstructStageIdForArtifactRef(artifactRef) {
876
+ const artifactKey = path.basename(artifactRef, path.extname(artifactRef))
877
+ .replaceAll("-", "_");
878
+ return RECONSTRUCT_LEDGER_STAGE_SPECS.find((spec) => spec.artifactKey === artifactKey)?.unitId ?? null;
879
+ }
880
+ export function reconstructStageOwner(stageId) {
881
+ const owner = RECONSTRUCT_LEDGER_STAGE_SPECS.find((spec) => spec.unitId === stageId)?.owner;
882
+ if (owner === "runtime" || owner === "host_llm")
883
+ return owner;
884
+ if (owner === "user_or_host_mediated")
885
+ return "host_or_user";
886
+ return null;
887
+ }
845
888
  const VALIDATION_GATE_BY_AUTHORED_UNIT = new Map([
846
889
  ["observation_directive", "observation_directive_validation"],
847
890
  ["source_frontier", "source_frontier_validation"],
@@ -1093,6 +1136,31 @@ export async function buildReconstructPipelineExecutionLedger(params) {
1093
1136
  }),
1094
1137
  ]),
1095
1138
  ]));
1139
+ const dispatchFallback = params.reconstructRecord.dispatch_fallback;
1140
+ if (dispatchFallback) {
1141
+ const semanticMapManifestStep = manifestStepByUnitId.get("semantic_map");
1142
+ if (!semanticMapManifestStep?.artifact_refs.includes(dispatchFallback.outcome_ref)) {
1143
+ throw new Error("dispatch fallback record/manifest mismatch: semantic_map step must consume the completed outcome ref.");
1144
+ }
1145
+ const parsedOutcome = DispatchFallbackOutcomeSchema.safeParse(parseYaml(await fs.readFile(dispatchFallback.outcome_ref, "utf8")));
1146
+ if (!parsedOutcome.success || parsedOutcome.data.status !== "completed") {
1147
+ throw new Error("dispatch fallback record/ledger mismatch: outcome artifact is not a valid completed canonical outcome.");
1148
+ }
1149
+ const canonicalBlock = projectDispatchFallbackRecordBlock({
1150
+ outcome: parsedOutcome.data,
1151
+ outcomeIntegrity: {
1152
+ path: dispatchFallback.outcome_ref,
1153
+ sha256: dispatchFallback.outcome_sha256,
1154
+ },
1155
+ });
1156
+ if (Object.entries(canonicalBlock).some(([key, value]) => JSON.stringify(dispatchFallback[key]) !== JSON.stringify(value))) {
1157
+ throw new Error("dispatch fallback record/ledger mismatch: record block is not the canonical outcome projection.");
1158
+ }
1159
+ artifactRefsByUnitId.set("semantic_map", normalizeLedgerRefs([
1160
+ ...(artifactRefsByUnitId.get("semantic_map") ?? []),
1161
+ dispatchFallback.outcome_ref,
1162
+ ]));
1163
+ }
1096
1164
  const sourceDeltaFrontierKind = await sourceObservationDeltaFrontierKind(artifactRefsByUnitId.get("source_observation_delta") ?? []);
1097
1165
  const sourceObservationReentryPresent = (artifactRefsByUnitId.get("source_observation_reentry_validation") ?? [])
1098
1166
  .length > 0;
@@ -1111,6 +1179,12 @@ export async function buildReconstructPipelineExecutionLedger(params) {
1111
1179
  const outputRefs = artifactRefsByUnitId.get(spec.unitId) ?? [];
1112
1180
  const outputHashes = await buildOutputHashes(outputRefs);
1113
1181
  outputHashesByUnitId.set(spec.unitId, outputHashes);
1182
+ if (spec.unitId === "semantic_map" &&
1183
+ dispatchFallback &&
1184
+ outputHashes[dispatchFallback.outcome_ref] !==
1185
+ dispatchFallback.outcome_sha256) {
1186
+ throw new Error("dispatch fallback record/ledger mismatch: outcome file hash does not match the completed record block.");
1187
+ }
1114
1188
  if (spec.unitKind === "runtime_validation") {
1115
1189
  validationStatusesByUnitId.set(spec.unitId, await runtimeValidationOutputStatus(outputRefs));
1116
1190
  }
@@ -1137,12 +1211,22 @@ export async function buildReconstructPipelineExecutionLedger(params) {
1137
1211
  }
1138
1212
  return trustedUnitIds.has(unitId);
1139
1213
  });
1214
+ // Pre-stage sessions (ultracode audit E, read-path): when a manifest IS provided but carries
1215
+ // NO step for this unit, the run predates the unit's registration — the terminal validator
1216
+ // (manifest_step_missing, W3-004) forces every CURRENT manifest to carry every stage, so an
1217
+ // absent step can only mean "this run never had the stage". Reporting it "missing" would raise
1218
+ // a false defect signal on every historical session each time a stage is added.
1219
+ const predatesUnit = params.reconstructRunManifest != null &&
1220
+ manifestStatus(params.reconstructRunManifest, spec.unitId) == null &&
1221
+ !manifestStepByUnitId.has(spec.unitId);
1140
1222
  const status = manifestStatus(params.reconstructRunManifest, spec.unitId) ??
1141
1223
  (outputPresent
1142
1224
  ? "completed"
1143
- : upstreamTrusted
1144
- ? "missing"
1145
- : "not_reached");
1225
+ : predatesUnit
1226
+ ? "not_reached"
1227
+ : upstreamTrusted
1228
+ ? "missing"
1229
+ : "not_reached");
1146
1230
  const trust = trustForReconstructUnit({
1147
1231
  spec,
1148
1232
  status,
@@ -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
  import { ontologySeedClaimProjections, ontologySeedExcludedClaimIds, } from "./seed-claim-projections.js";
6
7
  import { collectOntologySeedRefs } from "./ontology-seed-validation.js";
7
8
  import { loadReconstructContractRegistry, } from "./contract-registry.js";
@@ -78,6 +79,16 @@ const REVISION_ACTIONS = [
78
79
  "reject",
79
80
  "defer",
80
81
  ];
82
+ // Shared revision-proposal predicates (M4: one blocker predicate used identically at the
83
+ // stop gate (run.ts) and the continuation gate (maturation-validation.ts); both import from
84
+ // here, the revision-proposal concept owner, so the two gates cannot drift). reject|defer
85
+ // block continuation/stop; every non-reuse proposal is disclosed.
86
+ export function isRevisionBlocker(proposal) {
87
+ return proposal.action === "reject" || proposal.action === "defer";
88
+ }
89
+ export function isRevisionDisclosed(proposal) {
90
+ return proposal.action !== "reuse";
91
+ }
81
92
  function isoNow() {
82
93
  return new Date().toISOString();
83
94
  }
@@ -163,7 +174,7 @@ function seedLimitationIds(seed) {
163
174
  .map((record) => stringField(record, "limitation_id"))
164
175
  .filter((id) => id !== null));
165
176
  }
166
- function knownSeedRefs(seed) {
177
+ export function knownSeedRefs(seed) {
167
178
  if (!seed)
168
179
  return new Set();
169
180
  const refs = collectOntologySeedRefs(seed);
@@ -564,6 +575,14 @@ function validateClaimRealizationMapAgainstClaims(args) {
564
575
  const seen = new Set();
565
576
  const stanceCounts = initCountMap(CLAIM_REALIZATION_STANCES);
566
577
  const observations = observationsById(args.sourceObservations);
578
+ // G(a) slice 7: record the three claim-realization obligations before the per-realization loop so
579
+ // they are proven wired on a zero-realization map. Each was audited to a distinct enforcement region
580
+ // (no laundering): require_exactly_one = duplicate_id (in-loop) + missing_required_coverage (the
581
+ // coverage loop below); claim_ids = unknown_id; evidence_refs = validateEvidenceRef vs source obs.
582
+ const assertedObligationIds = [];
583
+ assertObligation(assertedObligationIds, "require_exactly_one_realization_for_each_seed_claim");
584
+ assertObligation(assertedObligationIds, "validate_realization_claim_ids_against_ontology_seed_claim_ids");
585
+ assertObligation(assertedObligationIds, "validate_realization_evidence_refs_against_source_observations");
567
586
  for (const realization of args.claimRealizationMap.claim_realizations) {
568
587
  const subjectId = realization.claim_id;
569
588
  if (seen.has(subjectId)) {
@@ -636,6 +655,7 @@ function validateClaimRealizationMapAgainstClaims(args) {
636
655
  validation_results: violations.length === 0
637
656
  ? ["claim_realization_map_valid"]
638
657
  : ["claim_realization_map_invalid"],
658
+ asserted_obligation_ids: assertedObligationIds,
639
659
  violations,
640
660
  };
641
661
  }
@@ -891,6 +911,41 @@ export function validateCompetencyQuestionsForOntologySeed(args) {
891
911
  }
892
912
  function validateCompetencyQuestionsAgainstEligibleClaims(args) {
893
913
  const violations = [];
914
+ // G(a) slice 27: record the 9 obligations this validator FULLY enforces with a distinct,
915
+ // name-matching site. These are the REF-RESOLUTION obligations ("validate X refs against/close-to
916
+ // authority Y" — every PRESENT ref must resolve to its registry/seed/manifest authority), plus
917
+ // question-id uniqueness and the exactly-one admitted-competency disposition rule. Authority-backed
918
+ // stamps are GATED on the authoritative input being supplied (codex #142 R1): the thin
919
+ // validateCompetencyQuestions path passes no registry/seed/governing-snapshot, so a zero-question
920
+ // artifact must NOT claim those obligations were enforced against an absent authority. Each stamp is
921
+ // before the per-question loop so a zero-question set still records once the authority is present; the
922
+ // enforcement code always runs, only the stamp is gated.
923
+ // Parked (see obligation-coverage-ledger.yaml notes): emit_required_evidence_scope (PROJECTION_ONLY);
924
+ // classification_level_axis / hidden_label facet coverage (NOT_FOUND — fields absent); p3-diagnostic
925
+ // (no code); query/visualization/graph refs (presence-only — codex #142 R1); and — codex #142 R2 — the
926
+ // 8 *_facet_coverage obligations (presence-only: resolve present facets but never require facets when
927
+ // the axis is applicable) and validate_required_ontology_handoff_axis_coverage_or_limitation_backed_
928
+ // disposition (counts inactive/unsupported questions as coverage; no active/limitation-backed filter).
929
+ const assertedObligationIds = [];
930
+ // Pure question-set check — enforceable with no external authority.
931
+ assertObligation(assertedObligationIds, "require_unique_question_id");
932
+ // Registry-backed ref resolution — only when the contract registry authority is supplied.
933
+ if (args.contractRegistry) {
934
+ assertObligation(assertedObligationIds, "validate_coverage_axes_against_coverage_axis_registry");
935
+ assertObligation(assertedObligationIds, "validate_ontology_handoff_axes_against_ontology_handoff_axis_registry");
936
+ assertObligation(assertedObligationIds, "validate_reference_standard_refs_against_reference_standard_registry");
937
+ assertObligation(assertedObligationIds, "validate_pattern_catalog_refs_against_reference_pattern_catalog_registry");
938
+ }
939
+ // Seed-backed closure — only when the ontology seed authority is supplied.
940
+ if (args.ontologySeed) {
941
+ assertObligation(assertedObligationIds, "validate_seed_ref_refs_close_against_known_seed_refs");
942
+ assertObligation(assertedObligationIds, "validate_limitation_refs_close_against_handoff_limitations");
943
+ }
944
+ // Run-manifest governing-snapshot authority — only when the snapshot is supplied.
945
+ if (args.governingSnapshot) {
946
+ assertObligation(assertedObligationIds, "validate_domain_competency_trace_refs_against_run_manifest_context_snapshot");
947
+ assertObligation(assertedObligationIds, "enforce_admitted_domain_competency_disposition_rule");
948
+ }
894
949
  const normalizedCompetencyQuestions = normalizeCompetencyQuestionsAtBoundary(args.competencyQuestions);
895
950
  violations.push(...normalizedCompetencyQuestions.violations);
896
951
  const eligibleClaims = new Set(args.eligibleClaimIds);
@@ -1275,11 +1330,36 @@ function validateCompetencyQuestionsAgainstEligibleClaims(args) {
1275
1330
  validation_results: violations.length === 0
1276
1331
  ? ["competency_questions_valid"]
1277
1332
  : ["competency_questions_invalid"],
1333
+ asserted_obligation_ids: assertedObligationIds,
1278
1334
  violations,
1279
1335
  };
1280
1336
  }
1281
1337
  export function validateCompetencyQuestionAssessment(args) {
1282
1338
  const violations = [];
1339
+ // G(a) slice 12: record the three obligations with a distinct, name-matching enforcement site,
1340
+ // stamped unconditionally before the per-assessment / per-question loops so they fire on a
1341
+ // zero-assessment artifact:
1342
+ // - require_exactly_one_assessment_per_authoritative_question (duplicate_id on a repeat +
1343
+ // missing_required_coverage when a question has no assessment → exactly one).
1344
+ // - validate_downstream_effect_consistent_with_answer_status (downstream_effect must equal
1345
+ // expectedDownstreamEffect(answer_status): invalid_enum).
1346
+ // - validate_required_seed_refs_close_against_question_seed_refs (bidirectional closure:
1347
+ // unknown_id for a ref outside the question, missing_required_coverage for a missing one).
1348
+ // PARKED (not recorded), name broader than code — honest declared≠wired, NOT laundered (see
1349
+ // obligation-coverage-ledger.yaml notes):
1350
+ // - validate_answer_status_against_active_answerability_contract: the validator checks enum
1351
+ // validity + per-status STRUCTURAL requirements (answerable→evidence_refs/required_seed_refs,
1352
+ // unsupported/deferred→missing_source_or_confirmation), but never verifies the answer_status
1353
+ // reflects actual answerability from evidence CONTENT — the answerability judgment is the
1354
+ // content-blind host-LLM step, not validated here.
1355
+ // - validate_answerability_trace_refs_close_against_seed_evidence_limitations_and_proofs: there
1356
+ // is no answerability_trace_refs / limitation_refs / proof_refs field; the validator closes
1357
+ // only evidence_refs and required_seed_refs (the latter already recorded above), never
1358
+ // limitations or proofs.
1359
+ const assertedObligationIds = [];
1360
+ assertObligation(assertedObligationIds, "require_exactly_one_assessment_per_authoritative_question");
1361
+ assertObligation(assertedObligationIds, "validate_downstream_effect_consistent_with_answer_status");
1362
+ assertObligation(assertedObligationIds, "validate_required_seed_refs_close_against_question_seed_refs");
1283
1363
  const questionIds = new Set(args.competencyQuestions.questions.map((question) => question.question_id));
1284
1364
  const questionById = new Map(args.competencyQuestions.questions.map((question) => [
1285
1365
  question.question_id,
@@ -1454,6 +1534,7 @@ export function validateCompetencyQuestionAssessment(args) {
1454
1534
  validation_results: violations.length === 0
1455
1535
  ? ["competency_question_assessment_valid"]
1456
1536
  : ["competency_question_assessment_invalid"],
1537
+ asserted_obligation_ids: assertedObligationIds,
1457
1538
  violations,
1458
1539
  };
1459
1540
  }
@@ -1531,7 +1612,25 @@ export function validateFailureClassification(args) {
1531
1612
  }
1532
1613
  export function validateRevisionProposal(args) {
1533
1614
  const violations = [];
1615
+ // Every published proposal is carry-forward scope, so its target must resolve
1616
+ // to a real authority — not just the `failure` case. Claim/question targets
1617
+ // come from the failures' own claim_id/question_id; seed targets from the
1618
+ // ontology-seed's seed_refs (knownSeedRefs). An unresolved target is a
1619
+ // fabricated/stale carry-forward and is rejected as unknown_id.
1534
1620
  const failureIds = new Set(args.failureClassification.failures.map((failure) => failure.failure_id));
1621
+ const failureClaimIds = new Set(args.failureClassification.failures
1622
+ .map((failure) => failure.claim_id)
1623
+ .filter((claimId) => Boolean(claimId)));
1624
+ const failureQuestionIds = new Set(args.failureClassification.failures
1625
+ .map((failure) => failure.question_id)
1626
+ .filter((questionId) => Boolean(questionId)));
1627
+ const seedRefs = knownSeedRefs(args.ontologySeed);
1628
+ const targetAuthorities = {
1629
+ failure: { ids: failureIds, authority: "failure" },
1630
+ claim: { ids: failureClaimIds, authority: "failure claim" },
1631
+ question: { ids: failureQuestionIds, authority: "failure question" },
1632
+ seed: { ids: seedRefs, authority: "ontology-seed seed_ref" },
1633
+ };
1535
1634
  const seen = new Set();
1536
1635
  const actionCounts = initCountMap(REVISION_ACTIONS);
1537
1636
  for (const proposal of args.revisionProposal.proposals) {
@@ -1543,11 +1642,23 @@ export function validateRevisionProposal(args) {
1543
1642
  }));
1544
1643
  }
1545
1644
  seen.add(proposal.proposal_id);
1546
- if (proposal.target_type === "failure" &&
1547
- !failureIds.has(proposal.target_id)) {
1645
+ // target_type is the typed union for fresh-authored proposals, but resume /
1646
+ // manual artifact validation loads it from YAML, so a stale value (e.g. the
1647
+ // removed domain_context) can reach here. An unknown type has no authority to
1648
+ // validate against, so reject it as invalid_enum instead of skipping the
1649
+ // authority check and disclosing an unbacked target as carry-forward.
1650
+ const targetAuthority = targetAuthorities[proposal.target_type];
1651
+ if (!targetAuthority) {
1652
+ violations.push(violation({
1653
+ code: "invalid_enum",
1654
+ message: `invalid proposal target_type: ${proposal.target_type}`,
1655
+ subjectId: proposal.proposal_id,
1656
+ }));
1657
+ }
1658
+ else if (!targetAuthority.ids.has(proposal.target_id)) {
1548
1659
  violations.push(violation({
1549
1660
  code: "unknown_id",
1550
- message: `proposal references unknown failure: ${proposal.target_id}`,
1661
+ message: `proposal references unknown ${targetAuthority.authority}: ${proposal.target_id}`,
1551
1662
  subjectId: proposal.proposal_id,
1552
1663
  }));
1553
1664
  }
@@ -1750,13 +1861,15 @@ export async function writeFailureClassificationValidationArtifact(args) {
1750
1861
  return validation;
1751
1862
  }
1752
1863
  export async function writeRevisionProposalValidationArtifact(args) {
1753
- const [revisionProposal, failureClassification] = await Promise.all([
1864
+ const [revisionProposal, failureClassification, ontologySeed] = await Promise.all([
1754
1865
  readYamlDocument(args.revisionProposalPath),
1755
1866
  readYamlDocument(args.failureClassificationPath),
1867
+ readYamlDocument(args.ontologySeedPath),
1756
1868
  ]);
1757
1869
  const validation = validateRevisionProposal({
1758
1870
  revisionProposal,
1759
1871
  failureClassification,
1872
+ ontologySeed,
1760
1873
  revisionProposalRef: path.resolve(args.revisionProposalPath),
1761
1874
  failureClassificationRef: path.resolve(args.failureClassificationPath),
1762
1875
  });