onto-mcp 0.3.0 → 0.3.2

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 (61) hide show
  1. package/.onto/authority/core-lexicon.yaml +12 -0
  2. package/.onto/domains/software-engineering/competency_qs.md +192 -63
  3. package/.onto/domains/software-engineering/concepts.md +67 -5
  4. package/.onto/domains/software-engineering/conciseness_rules.md +22 -2
  5. package/.onto/domains/software-engineering/dependency_rules.md +78 -8
  6. package/.onto/domains/software-engineering/domain_scope.md +181 -150
  7. package/.onto/domains/software-engineering/extension_cases.md +318 -542
  8. package/.onto/domains/software-engineering/logic_rules.md +75 -3
  9. package/.onto/domains/software-engineering/problem_framing_profile.md +29 -2
  10. package/.onto/domains/software-engineering/prompt_interface.md +122 -0
  11. package/.onto/domains/software-engineering/structure_spec.md +53 -4
  12. package/.onto/principles/llm-native-development-guideline.md +20 -0
  13. package/.onto/principles/productization-charter.md +6 -0
  14. package/.onto/processes/evolve/material-kind-adapter-contract.md +6 -0
  15. package/.onto/processes/reconstruct/reconstruct-boundary-contract.md +468 -81
  16. package/.onto/processes/reconstruct/reconstruct-execution-ux-contract.md +177 -0
  17. package/.onto/processes/reconstruct/source-profile-contract.md +39 -6
  18. package/.onto/processes/reconstruct/top-level-concept-discovery-contract.md +387 -0
  19. package/.onto/processes/review/binding-contract.md +8 -0
  20. package/.onto/processes/review/lens-registry.md +16 -0
  21. package/.onto/processes/review/pre-dispatch-contracts.md +34 -13
  22. package/.onto/processes/review/productized-live-path.md +3 -1
  23. package/.onto/processes/shared/pipeline-execution-ledger-contract.md +185 -0
  24. package/.onto/processes/shared/target-material-kind-contract.md +24 -2
  25. package/.onto/roles/axiology.md +7 -2
  26. package/AGENTS.md +4 -2
  27. package/README.md +52 -29
  28. package/dist/core-api/reconstruct-api.js +92 -5
  29. package/dist/core-api/review-api.js +1744 -371
  30. package/dist/core-runtime/cli/mock-review-unit-executor.js +17 -0
  31. package/dist/core-runtime/cli/render-review-final-output.js +9 -0
  32. package/dist/core-runtime/cli/review-invoke.js +387 -55
  33. package/dist/core-runtime/cli/run-review-prompt-execution.js +361 -90
  34. package/dist/core-runtime/path-boundary.js +58 -0
  35. package/dist/core-runtime/pipeline-execution-ledger.js +100 -0
  36. package/dist/core-runtime/reconstruct/artifact-types.js +33 -1
  37. package/dist/core-runtime/reconstruct/materialize-preparation.js +54 -4
  38. package/dist/core-runtime/reconstruct/pipeline-execution-ledger.js +342 -0
  39. package/dist/core-runtime/reconstruct/post-seed-validation.js +630 -0
  40. package/dist/core-runtime/reconstruct/record.js +105 -1
  41. package/dist/core-runtime/reconstruct/run.js +1594 -38
  42. package/dist/core-runtime/reconstruct/seed-candidate-validation.js +29 -0
  43. package/dist/core-runtime/review/continuation-plan.js +160 -0
  44. package/dist/core-runtime/review/execution-plan-boundary.js +123 -0
  45. package/dist/core-runtime/review/materializers.js +8 -3
  46. package/dist/core-runtime/review/pipeline-execution-ledger.js +250 -0
  47. package/dist/core-runtime/review/review-artifact-utils.js +15 -2
  48. package/dist/core-runtime/review/review-invocation-runner.js +604 -0
  49. package/dist/core-runtime/target-material-kind.js +43 -5
  50. package/dist/mcp/server.js +289 -59
  51. package/dist/mcp/tool-schemas.js +28 -2
  52. package/package.json +4 -2
  53. package/.onto/domains/llm-native-development/competency_qs.md +0 -430
  54. package/.onto/domains/llm-native-development/concepts.md +0 -242
  55. package/.onto/domains/llm-native-development/conciseness_rules.md +0 -163
  56. package/.onto/domains/llm-native-development/dependency_rules.md +0 -216
  57. package/.onto/domains/llm-native-development/domain_scope.md +0 -197
  58. package/.onto/domains/llm-native-development/extension_cases.md +0 -474
  59. package/.onto/domains/llm-native-development/logic_rules.md +0 -123
  60. package/.onto/domains/llm-native-development/prompt_interface.md +0 -49
  61. package/.onto/domains/llm-native-development/structure_spec.md +0 -245
@@ -0,0 +1,100 @@
1
+ import { createHash } from "node:crypto";
2
+ import fs from "node:fs/promises";
3
+ export const PIPELINE_EXECUTION_LEDGER_SCHEMA_VERSION = "1";
4
+ export async function fileSha256IfPresent(filePath) {
5
+ if (!filePath)
6
+ return null;
7
+ try {
8
+ const content = await fs.readFile(filePath);
9
+ return createHash("sha256").update(content).digest("hex");
10
+ }
11
+ catch (error) {
12
+ const code = error.code;
13
+ if (code === "ENOENT" || code === "ENOTDIR")
14
+ return null;
15
+ throw error;
16
+ }
17
+ }
18
+ export async function fileExists(filePath) {
19
+ if (!filePath)
20
+ return false;
21
+ try {
22
+ await fs.access(filePath);
23
+ return true;
24
+ }
25
+ catch {
26
+ return false;
27
+ }
28
+ }
29
+ export async function buildOutputHashes(outputRefs) {
30
+ const entries = await Promise.all(outputRefs.map(async (outputRef) => [
31
+ outputRef,
32
+ await fileSha256IfPresent(outputRef),
33
+ ]));
34
+ return Object.fromEntries(entries);
35
+ }
36
+ export function hasAllRequiredOutputHashes(unit) {
37
+ return unit.outputRefs.every((outputRef) => unit.outputHashes[outputRef] !== null);
38
+ }
39
+ export function isTrustedLedgerUnit(unit) {
40
+ return (unit.status === "completed" &&
41
+ unit.trustStatus === "trusted" &&
42
+ hasAllRequiredOutputHashes(unit));
43
+ }
44
+ export function firstUntrustedRequiredUnit(ledger) {
45
+ return ledger.units.find((unit) => !isTrustedLedgerUnit(unit)) ?? null;
46
+ }
47
+ export function normalizeLedgerRefs(refs) {
48
+ return [...new Set(refs.filter((ref) => typeof ref === "string" && ref.length > 0))]
49
+ .sort();
50
+ }
51
+ export function buildLedgerTrust(args) {
52
+ if (!args.upstreamTrusted) {
53
+ return {
54
+ trustStatus: "blocked_by_upstream",
55
+ trustReason: "A required upstream unit is not trusted.",
56
+ };
57
+ }
58
+ if (args.status === "completed") {
59
+ const missingOutputRefs = args.outputRefs.filter((outputRef) => args.outputHashes[outputRef] === null);
60
+ if (missingOutputRefs.length > 0) {
61
+ return {
62
+ trustStatus: "untrusted",
63
+ trustReason: `Completed unit is missing required output refs: ${missingOutputRefs.join(", ")}.`,
64
+ };
65
+ }
66
+ return {
67
+ trustStatus: "trusted",
68
+ trustReason: "Unit completed and required output refs are present.",
69
+ };
70
+ }
71
+ if (args.status === "failed") {
72
+ return {
73
+ trustStatus: "untrusted",
74
+ trustReason: args.lastFailureMessage?.trim() ||
75
+ "Unit failed before producing a trusted output.",
76
+ };
77
+ }
78
+ if (args.status === "skipped") {
79
+ return {
80
+ trustStatus: "untrusted",
81
+ trustReason: "Unit was skipped and did not produce a trusted output.",
82
+ };
83
+ }
84
+ if (args.status === "missing") {
85
+ return {
86
+ trustStatus: "untrusted",
87
+ trustReason: "Required unit output is missing.",
88
+ };
89
+ }
90
+ if (args.status === "not_reached") {
91
+ return {
92
+ trustStatus: "blocked_by_upstream",
93
+ trustReason: "Unit was not reached because upstream execution did not complete.",
94
+ };
95
+ }
96
+ return {
97
+ trustStatus: "untrusted",
98
+ trustReason: "Unit is planned but has not completed.",
99
+ };
100
+ }
@@ -1 +1,33 @@
1
- export {};
1
+ export const RECONSTRUCT_STAGE_IDS = [
2
+ "invocation_binding",
3
+ "target_material_profile",
4
+ "source_inventory",
5
+ "initial_source_frontier",
6
+ "source_observation",
7
+ "observation_directive",
8
+ "observation_directive_validation",
9
+ "lens_judgment",
10
+ "exploration_synthesis",
11
+ "source_frontier",
12
+ "source_frontier_validation",
13
+ "domain_context_selection",
14
+ "domain_context_selection_validation",
15
+ "seed_candidate",
16
+ "seed_candidate_validation",
17
+ "claim_realization",
18
+ "claim_realization_validation",
19
+ "seed_confirmation",
20
+ "seed_confirmation_validation",
21
+ "competency_questions",
22
+ "competency_questions_validation",
23
+ "competency_question_assessment",
24
+ "competency_question_assessment_validation",
25
+ "failure_classification",
26
+ "failure_classification_validation",
27
+ "revision_proposal",
28
+ "revision_proposal_validation",
29
+ "metrics",
30
+ "stop_decision",
31
+ "final_output",
32
+ "record_assembly",
33
+ ];
@@ -2,7 +2,7 @@ import crypto from "node:crypto";
2
2
  import fs from "node:fs/promises";
3
3
  import path from "node:path";
4
4
  import { stringify as stringifyYaml } from "yaml";
5
- import { detectTargetMaterialKind, } from "../target-material-kind.js";
5
+ import { aggregateTargetMaterialDetections, detectTargetMaterialRefs, } from "../target-material-kind.js";
6
6
  import { validateSourceObservationBoundary, } from "./source-observations.js";
7
7
  import { loadReconstructSourceProfiles, } from "./source-profiles.js";
8
8
  const CONCRETE_TARGET_MATERIAL_KINDS = new Set([
@@ -38,8 +38,8 @@ function supportForMaterial(args) {
38
38
  }
39
39
  if (args.targetMaterialKind === "mixed") {
40
40
  return {
41
- support_status: "partial",
42
- unsupported_reason: "mixed target material kind requires per-ref observation; unsupported members are skipped",
41
+ support_status: "partial_composite",
42
+ unsupported_reason: "mixed target material kind is observed through per-ref source profiles; unsupported members are skipped with authority impact",
43
43
  };
44
44
  }
45
45
  if (args.selectedProfiles.length === 0) {
@@ -91,15 +91,20 @@ function selectedProfileRefs(profiles, candidates) {
91
91
  async function textStats(ref) {
92
92
  try {
93
93
  const text = await fs.readFile(ref, "utf8");
94
+ const excerptLimit = 6000;
94
95
  return {
95
96
  line_count: text.length === 0 ? 0 : text.split(/\r?\n/).length,
96
97
  char_count: text.length,
98
+ content_excerpt: text.slice(0, excerptLimit),
99
+ excerpt_truncated: text.length > excerptLimit,
97
100
  };
98
101
  }
99
102
  catch {
100
103
  return {
101
104
  line_count: null,
102
105
  char_count: null,
106
+ content_excerpt: null,
107
+ excerpt_truncated: false,
103
108
  };
104
109
  }
105
110
  }
@@ -113,6 +118,8 @@ async function buildObservation(detection) {
113
118
  const stats = stat.isFile() ? await textStats(detection.ref) : {
114
119
  line_count: null,
115
120
  char_count: null,
121
+ content_excerpt: null,
122
+ excerpt_truncated: false,
116
123
  };
117
124
  const observation = {
118
125
  observation_id: stableObservationId({
@@ -132,6 +139,8 @@ async function buildObservation(detection) {
132
139
  size_bytes: stat.isFile() ? stat.size : null,
133
140
  line_count: stats.line_count,
134
141
  char_count: stats.char_count,
142
+ content_excerpt: stats.content_excerpt,
143
+ excerpt_truncated: stats.excerpt_truncated,
135
144
  },
136
145
  };
137
146
  const validation = validateSourceObservationBoundary(observation);
@@ -140,6 +149,40 @@ async function buildObservation(detection) {
140
149
  }
141
150
  return observation;
142
151
  }
152
+ function stableFrontierRefId(unit) {
153
+ const digest = crypto
154
+ .createHash("sha256")
155
+ .update(`${unit.target_material_kind}\n${path.resolve(unit.ref)}\n${unit.inventory_unit}`)
156
+ .digest("hex")
157
+ .slice(0, 16);
158
+ return `frontier_initial_${digest}`;
159
+ }
160
+ function buildInitialSourceFrontier(args) {
161
+ return {
162
+ schema_version: "1",
163
+ session_id: args.sessionId,
164
+ created_at: isoNow(),
165
+ frontier_id: "initial",
166
+ source_refs: args.inventory.inventory_units
167
+ .filter((unit) => unit.scan_status === "planned")
168
+ .map((unit) => ({
169
+ frontier_ref_id: stableFrontierRefId(unit),
170
+ source_ref: unit.ref,
171
+ target_material_kind: unit.target_material_kind,
172
+ inventory_unit: unit.inventory_unit,
173
+ profile_ref: unit.profile_ref,
174
+ rationale: "Initial source frontier derived from runtime material inventory and selected source profile.",
175
+ })),
176
+ skipped_refs: args.inventory.inventory_units
177
+ .filter((unit) => unit.scan_status === "skipped")
178
+ .map((unit) => ({
179
+ source_ref: unit.ref,
180
+ target_material_kind: unit.target_material_kind,
181
+ reason: unit.skip_reason ?? "skipped",
182
+ authority_impact: "Semantic artifacts cannot use this ref as trusted evidence until a supported material profile and observation exist.",
183
+ })),
184
+ };
185
+ }
143
186
  function buildInventoryUnits(args) {
144
187
  return args.detections.map((detection) => {
145
188
  const profileRef = profileRefForKind(args.profiles, detection.kind);
@@ -169,7 +212,8 @@ export async function materializeReconstructPreparationArtifacts(params) {
169
212
  const sessionId = path.basename(sessionRoot);
170
213
  const targetRefs = params.targetRefs.map((ref) => path.resolve(ref));
171
214
  const profiles = await loadReconstructSourceProfiles(params.profilesRoot);
172
- const detection = await detectTargetMaterialKind(targetRefs);
215
+ const perRefDetections = await detectTargetMaterialRefs(targetRefs);
216
+ const detection = aggregateTargetMaterialDetections(perRefDetections);
173
217
  const selectedProfiles = selectedProfileRefs(profiles, detection.target_material_kind === "mixed"
174
218
  ? detection.target_material_kind_candidates
175
219
  : [detection.target_material_kind]);
@@ -207,6 +251,10 @@ export async function materializeReconstructPreparationArtifacts(params) {
207
251
  source: "binding",
208
252
  },
209
253
  };
254
+ const initialSourceFrontier = buildInitialSourceFrontier({
255
+ sessionId,
256
+ inventory,
257
+ });
210
258
  const observations = [];
211
259
  const skippedRefs = [];
212
260
  for (const unit of inventory.inventory_units) {
@@ -240,11 +288,13 @@ export async function materializeReconstructPreparationArtifacts(params) {
240
288
  const refs = {
241
289
  target_material_profile: path.join(sessionRoot, "target-material-profile.yaml"),
242
290
  source_inventory: path.join(sessionRoot, "source-inventory.yaml"),
291
+ initial_source_frontier: path.join(sessionRoot, "initial-source-frontier.yaml"),
243
292
  source_observations: path.join(sessionRoot, "source-observations.yaml"),
244
293
  };
245
294
  await Promise.all([
246
295
  writeYamlDocument(refs.target_material_profile, targetMaterialProfile),
247
296
  writeYamlDocument(refs.source_inventory, inventory),
297
+ writeYamlDocument(refs.initial_source_frontier, initialSourceFrontier),
248
298
  writeYamlDocument(refs.source_observations, sourceObservations),
249
299
  ]);
250
300
  return refs;
@@ -0,0 +1,342 @@
1
+ import path from "node:path";
2
+ import { PIPELINE_EXECUTION_LEDGER_SCHEMA_VERSION, buildLedgerTrust, buildOutputHashes, isTrustedLedgerUnit, normalizeLedgerRefs, } from "../pipeline-execution-ledger.js";
3
+ const RECONSTRUCT_LEDGER_STAGE_SPECS = [
4
+ {
5
+ unitId: "target_material_profile",
6
+ unitKind: "material_profile",
7
+ owner: "runtime",
8
+ artifactKey: "target_material_profile",
9
+ upstreamUnitIds: [],
10
+ },
11
+ {
12
+ unitId: "source_inventory",
13
+ unitKind: "source_inventory",
14
+ owner: "runtime",
15
+ artifactKey: "source_inventory",
16
+ upstreamUnitIds: ["target_material_profile"],
17
+ },
18
+ {
19
+ unitId: "initial_source_frontier",
20
+ unitKind: "source_frontier_initial",
21
+ owner: "runtime",
22
+ artifactKey: "initial_source_frontier",
23
+ upstreamUnitIds: ["source_inventory"],
24
+ },
25
+ {
26
+ unitId: "source_observation",
27
+ unitKind: "source_observation",
28
+ owner: "runtime",
29
+ artifactKey: "source_observations",
30
+ upstreamUnitIds: ["initial_source_frontier"],
31
+ },
32
+ {
33
+ unitId: "observation_directive",
34
+ unitKind: "semantic_directive",
35
+ owner: "host_llm",
36
+ artifactKey: "source_observation_directive",
37
+ upstreamUnitIds: ["source_observation"],
38
+ },
39
+ {
40
+ unitId: "observation_directive_validation",
41
+ unitKind: "runtime_validation",
42
+ owner: "runtime",
43
+ artifactKey: "source_observation_directive_validation",
44
+ upstreamUnitIds: ["observation_directive"],
45
+ },
46
+ {
47
+ unitId: "domain_context_selection",
48
+ unitKind: "semantic_context_selection",
49
+ owner: "host_llm",
50
+ artifactKey: "domain_context_selection",
51
+ upstreamUnitIds: ["observation_directive_validation"],
52
+ },
53
+ {
54
+ unitId: "domain_context_selection_validation",
55
+ unitKind: "runtime_validation",
56
+ owner: "runtime",
57
+ artifactKey: "domain_context_selection_validation",
58
+ upstreamUnitIds: ["domain_context_selection"],
59
+ },
60
+ {
61
+ unitId: "lens_judgment",
62
+ unitKind: "semantic_lens_judgment",
63
+ owner: "host_llm",
64
+ artifactKey: "lens_judgment_index",
65
+ upstreamUnitIds: ["observation_directive_validation"],
66
+ },
67
+ {
68
+ unitId: "exploration_synthesis",
69
+ unitKind: "semantic_exploration_synthesis",
70
+ owner: "host_llm",
71
+ artifactKey: "exploration_synthesis",
72
+ upstreamUnitIds: ["lens_judgment"],
73
+ },
74
+ {
75
+ unitId: "source_frontier",
76
+ unitKind: "semantic_source_frontier",
77
+ owner: "host_llm",
78
+ artifactKey: "source_frontier",
79
+ upstreamUnitIds: ["exploration_synthesis"],
80
+ },
81
+ {
82
+ unitId: "source_frontier_validation",
83
+ unitKind: "runtime_validation",
84
+ owner: "runtime",
85
+ artifactKey: "source_frontier_validation",
86
+ upstreamUnitIds: ["source_frontier"],
87
+ },
88
+ {
89
+ unitId: "seed_candidate",
90
+ unitKind: "semantic_seed",
91
+ owner: "host_llm",
92
+ artifactKey: "seed_candidate",
93
+ upstreamUnitIds: ["source_frontier_validation"],
94
+ },
95
+ {
96
+ unitId: "seed_candidate_validation",
97
+ unitKind: "runtime_validation",
98
+ owner: "runtime",
99
+ artifactKey: "seed_candidate_validation",
100
+ upstreamUnitIds: ["seed_candidate"],
101
+ },
102
+ {
103
+ unitId: "claim_realization",
104
+ unitKind: "semantic_map",
105
+ owner: "host_llm",
106
+ artifactKey: "claim_realization_map",
107
+ upstreamUnitIds: ["seed_candidate_validation"],
108
+ },
109
+ {
110
+ unitId: "claim_realization_validation",
111
+ unitKind: "runtime_validation",
112
+ owner: "runtime",
113
+ artifactKey: "claim_realization_map_validation",
114
+ upstreamUnitIds: ["claim_realization"],
115
+ },
116
+ {
117
+ unitId: "seed_confirmation",
118
+ unitKind: "confirmation",
119
+ owner: "user_or_host_mediated",
120
+ artifactKey: "seed_confirmation",
121
+ upstreamUnitIds: ["seed_candidate_validation", "claim_realization_validation"],
122
+ },
123
+ {
124
+ unitId: "seed_confirmation_validation",
125
+ unitKind: "runtime_validation",
126
+ owner: "runtime",
127
+ artifactKey: "seed_confirmation_validation",
128
+ upstreamUnitIds: ["seed_confirmation"],
129
+ },
130
+ {
131
+ unitId: "competency_questions",
132
+ unitKind: "semantic_questions",
133
+ owner: "host_llm",
134
+ artifactKey: "competency_questions",
135
+ upstreamUnitIds: ["seed_confirmation_validation"],
136
+ },
137
+ {
138
+ unitId: "competency_questions_validation",
139
+ unitKind: "runtime_validation",
140
+ owner: "runtime",
141
+ artifactKey: "competency_questions_validation",
142
+ upstreamUnitIds: ["competency_questions"],
143
+ },
144
+ {
145
+ unitId: "competency_question_assessment",
146
+ unitKind: "semantic_assessment",
147
+ owner: "host_llm",
148
+ artifactKey: "competency_question_assessment",
149
+ upstreamUnitIds: ["competency_questions_validation"],
150
+ },
151
+ {
152
+ unitId: "competency_question_assessment_validation",
153
+ unitKind: "runtime_validation",
154
+ owner: "runtime",
155
+ artifactKey: "competency_question_assessment_validation",
156
+ upstreamUnitIds: ["competency_question_assessment"],
157
+ },
158
+ {
159
+ unitId: "failure_classification",
160
+ unitKind: "semantic_failure_classification",
161
+ owner: "host_llm",
162
+ artifactKey: "failure_classification",
163
+ upstreamUnitIds: ["competency_question_assessment_validation"],
164
+ },
165
+ {
166
+ unitId: "failure_classification_validation",
167
+ unitKind: "runtime_validation",
168
+ owner: "runtime",
169
+ artifactKey: "failure_classification_validation",
170
+ upstreamUnitIds: ["failure_classification"],
171
+ },
172
+ {
173
+ unitId: "revision_proposal",
174
+ unitKind: "semantic_revision",
175
+ owner: "host_llm",
176
+ artifactKey: "revision_proposal",
177
+ upstreamUnitIds: ["failure_classification_validation"],
178
+ },
179
+ {
180
+ unitId: "revision_proposal_validation",
181
+ unitKind: "runtime_validation",
182
+ owner: "runtime",
183
+ artifactKey: "revision_proposal_validation",
184
+ upstreamUnitIds: ["revision_proposal"],
185
+ },
186
+ {
187
+ unitId: "metrics",
188
+ unitKind: "runtime_metrics",
189
+ owner: "runtime",
190
+ artifactKey: "reconstruct_metrics",
191
+ upstreamUnitIds: ["revision_proposal_validation"],
192
+ },
193
+ {
194
+ unitId: "stop_decision",
195
+ unitKind: "semantic_decision",
196
+ owner: "host_llm",
197
+ artifactKey: "stop_decision",
198
+ upstreamUnitIds: ["metrics"],
199
+ },
200
+ {
201
+ unitId: "final_output",
202
+ unitKind: "final_output",
203
+ owner: "host_llm",
204
+ artifactKey: "final_output",
205
+ upstreamUnitIds: ["stop_decision"],
206
+ },
207
+ {
208
+ unitId: "record_assembly",
209
+ unitKind: "record_assembly",
210
+ owner: "runtime",
211
+ artifactKey: "reconstruct_record",
212
+ upstreamUnitIds: ["final_output"],
213
+ },
214
+ ];
215
+ const VALIDATION_GATE_BY_AUTHORED_UNIT = new Map([
216
+ ["observation_directive", "observation_directive_validation"],
217
+ ["source_frontier", "source_frontier_validation"],
218
+ ["seed_candidate", "seed_candidate_validation"],
219
+ ["claim_realization", "claim_realization_validation"],
220
+ ["seed_confirmation", "seed_confirmation_validation"],
221
+ ["competency_questions", "competency_questions_validation"],
222
+ ["competency_question_assessment", "competency_question_assessment_validation"],
223
+ ["failure_classification", "failure_classification_validation"],
224
+ ["revision_proposal", "revision_proposal_validation"],
225
+ ]);
226
+ function artifactRefForKey(args) {
227
+ if (args.key === "reconstruct_record")
228
+ return args.reconstructRecordRef ?? null;
229
+ return args.record.artifact_refs[args.key] ?? null;
230
+ }
231
+ function downstreamMap() {
232
+ const map = new Map();
233
+ for (const spec of RECONSTRUCT_LEDGER_STAGE_SPECS)
234
+ map.set(spec.unitId, []);
235
+ for (const spec of RECONSTRUCT_LEDGER_STAGE_SPECS) {
236
+ for (const upstreamUnitId of spec.upstreamUnitIds) {
237
+ map.set(upstreamUnitId, [...(map.get(upstreamUnitId) ?? []), spec.unitId]);
238
+ }
239
+ }
240
+ return map;
241
+ }
242
+ function manifestStatus(manifest, unitId) {
243
+ const status = manifest?.steps.find((step) => step.step_id === unitId)?.status;
244
+ if (status === "completed" || status === "failed" || status === "skipped") {
245
+ return status;
246
+ }
247
+ return null;
248
+ }
249
+ function trustForReconstructUnit(args) {
250
+ const validationGateUnitId = VALIDATION_GATE_BY_AUTHORED_UNIT.get(args.spec.unitId);
251
+ if (validationGateUnitId) {
252
+ const validationOutputPresent = args.artifactRefsByUnitId.get(validationGateUnitId)?.some((ref) => ref.length > 0) ?? false;
253
+ if (!validationOutputPresent) {
254
+ return {
255
+ trustStatus: "untrusted",
256
+ trustReason: "LLM or user-authored artifact exists only as a candidate until its runtime validation gate completes.",
257
+ };
258
+ }
259
+ }
260
+ return buildLedgerTrust({
261
+ status: args.status,
262
+ outputRefs: args.outputRefs,
263
+ outputHashes: args.outputHashes,
264
+ upstreamTrusted: args.upstreamTrusted,
265
+ });
266
+ }
267
+ export async function buildReconstructPipelineExecutionLedger(params) {
268
+ const downstreamUnitIds = downstreamMap();
269
+ const manifestStepByUnitId = new Map((params.reconstructRunManifest?.steps ?? []).map((step) => [step.step_id, step]));
270
+ const artifactRefsByUnitId = new Map(RECONSTRUCT_LEDGER_STAGE_SPECS.map((spec) => [
271
+ spec.unitId,
272
+ normalizeLedgerRefs([
273
+ artifactRefForKey({
274
+ key: spec.artifactKey,
275
+ record: params.reconstructRecord,
276
+ reconstructRecordRef: params.reconstructRecordRef ?? null,
277
+ }),
278
+ ]),
279
+ ]));
280
+ const trustedUnitIds = new Set();
281
+ const units = [];
282
+ for (const spec of RECONSTRUCT_LEDGER_STAGE_SPECS) {
283
+ const outputRefs = artifactRefsByUnitId.get(spec.unitId) ?? [];
284
+ const outputHashes = await buildOutputHashes(outputRefs);
285
+ const outputPresent = outputRefs.length > 0 &&
286
+ outputRefs.every((outputRef) => outputHashes[outputRef] !== null);
287
+ const upstreamTrusted = spec.upstreamUnitIds.every((unitId) => {
288
+ if (spec.unitKind === "runtime_validation") {
289
+ return (artifactRefsByUnitId.get(unitId) ?? []).length > 0;
290
+ }
291
+ return trustedUnitIds.has(unitId);
292
+ });
293
+ const status = manifestStatus(params.reconstructRunManifest, spec.unitId) ??
294
+ (outputPresent
295
+ ? "completed"
296
+ : upstreamTrusted
297
+ ? "missing"
298
+ : "not_reached");
299
+ const trust = trustForReconstructUnit({
300
+ spec,
301
+ status,
302
+ outputRefs,
303
+ outputHashes,
304
+ upstreamTrusted,
305
+ artifactRefsByUnitId,
306
+ });
307
+ const manifestStep = manifestStepByUnitId.get(spec.unitId);
308
+ const entry = {
309
+ unitId: spec.unitId,
310
+ unitKind: spec.unitKind,
311
+ owner: spec.owner,
312
+ producedArtifactRefs: outputRefs,
313
+ consumedArtifactRefs: normalizeLedgerRefs([
314
+ ...spec.upstreamUnitIds.flatMap((unitId) => artifactRefsByUnitId.get(unitId) ?? []),
315
+ ]),
316
+ outputRefs,
317
+ outputHashes,
318
+ status,
319
+ trustStatus: trust.trustStatus,
320
+ trustReason: trust.trustReason,
321
+ attemptCount: manifestStep ? 1 : 0,
322
+ lastFailureMessage: null,
323
+ upstreamUnitIds: spec.upstreamUnitIds,
324
+ downstreamUnitIds: downstreamUnitIds.get(spec.unitId) ?? [],
325
+ };
326
+ units.push(entry);
327
+ if (isTrustedLedgerUnit(entry))
328
+ trustedUnitIds.add(entry.unitId);
329
+ }
330
+ return {
331
+ schemaVersion: PIPELINE_EXECUTION_LEDGER_SCHEMA_VERSION,
332
+ pipeline: "reconstruct",
333
+ sessionId: params.reconstructRecord.session_id || path.basename(params.sessionRoot),
334
+ sourceRefs: normalizeLedgerRefs([
335
+ params.reconstructRecordRef,
336
+ params.reconstructRunManifestRef,
337
+ ...Object.values(params.reconstructRecord.artifact_refs),
338
+ ...Object.values(params.reconstructRunManifest?.artifact_refs ?? {}),
339
+ ]),
340
+ units,
341
+ };
342
+ }