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
|
@@ -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
|
-
|
|
809
|
-
|
|
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
|
-
:
|
|
1144
|
-
? "
|
|
1145
|
-
:
|
|
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
|
-
|
|
1547
|
-
|
|
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
|
|
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
|
});
|