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
@@ -1,8 +1,11 @@
1
1
  import fs from "node:fs/promises";
2
2
  import path from "node:path";
3
3
  import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
4
+ import { callLlm } from "../llm/llm-caller.js";
5
+ import { loadCoreLensRegistry } from "../discovery/lens-registry.js";
4
6
  import { writeSourceObservationDirectiveValidationArtifact } from "./directive-validation.js";
5
7
  import { materializeReconstructPreparationArtifacts } from "./materialize-preparation.js";
8
+ import { validateFinalOutputProvenance, writeClaimRealizationMapValidationArtifact, writeCompetencyQuestionAssessmentValidationArtifact, writeCompetencyQuestionsValidationArtifact, writeFailureClassificationValidationArtifact, writeRevisionProposalValidationArtifact, writeSeedConfirmationValidationArtifact, } from "./post-seed-validation.js";
6
9
  import { assembleReconstructRecord } from "./record.js";
7
10
  import { writeSeedCandidateValidationArtifact } from "./seed-candidate-validation.js";
8
11
  function isoNow() {
@@ -26,6 +29,74 @@ function allClaims(seedCandidate) {
26
29
  ...seedCandidate.rules,
27
30
  ];
28
31
  }
32
+ function compactStatement(statement) {
33
+ const limit = 240;
34
+ return statement.length <= limit ? statement : `${statement.slice(0, limit - 3)}...`;
35
+ }
36
+ function sourceBasename(sourceRef) {
37
+ return path.basename(sourceRef) || sourceRef;
38
+ }
39
+ function summarizeSeedClaimsForConfirmation(seedCandidate) {
40
+ const groups = [
41
+ ["purpose", [seedCandidate.purpose]],
42
+ ["non_goal", seedCandidate.non_goals],
43
+ ["entity", seedCandidate.entities],
44
+ ["relation", seedCandidate.relations],
45
+ ["action", seedCandidate.actions],
46
+ ["property", seedCandidate.properties],
47
+ ["rule", seedCandidate.rules],
48
+ ];
49
+ return groups.flatMap(([claimKind, claims]) => claims.map((claim) => ({
50
+ claim_id: claim.claim_id,
51
+ claim_kind: claimKind,
52
+ name: claim.name,
53
+ statement: compactStatement(claim.statement),
54
+ evidence_observation_ids: [
55
+ ...new Set(claim.evidence_refs.map((ref) => ref.observation_id)),
56
+ ],
57
+ evidence_source_basenames: [
58
+ ...new Set(claim.evidence_refs.map((ref) => sourceBasename(ref.source_ref))),
59
+ ],
60
+ })));
61
+ }
62
+ function countBy(values, selected) {
63
+ const counts = Object.fromEntries(values.map((value) => [value, 0]));
64
+ for (const value of selected) {
65
+ counts[value] += 1;
66
+ }
67
+ return counts;
68
+ }
69
+ const CLAIM_REALIZATION_STANCES = [
70
+ "observed_runtime_behavior",
71
+ "declared_design_intent",
72
+ "schema_or_contract_presence",
73
+ "test_or_fixture_only",
74
+ "deferred_or_non_goal",
75
+ "unknown",
76
+ ];
77
+ const ANSWER_STATUSES = [
78
+ "answered",
79
+ "partially_answered",
80
+ "not_answered",
81
+ "needs_evidence",
82
+ "out_of_scope",
83
+ ];
84
+ const FAILURE_KINDS = [
85
+ "unsupported_claim",
86
+ "unanswered_question",
87
+ "contradicted_evidence",
88
+ "insufficient_evidence",
89
+ "deferred_scope",
90
+ "out_of_scope",
91
+ ];
92
+ const REVISION_ACTIONS = [
93
+ "reuse",
94
+ "extend",
95
+ "rename",
96
+ "split",
97
+ "reject",
98
+ "defer",
99
+ ];
29
100
  function evidenceRefFromObservation(observation) {
30
101
  return {
31
102
  observation_id: observation.observation_id,
@@ -46,18 +117,34 @@ function calculateMetrics(args) {
46
117
  source_observation_directive: args.sourceObservationDirectiveValidation.validation_status,
47
118
  seed_candidate: args.seedCandidateValidation.validation_status,
48
119
  seed_confirmation: args.seedConfirmation.confirmation_status,
120
+ claim_realization: args.claimRealizationMapValidation.validation_status,
121
+ seed_confirmation_validation: args.seedConfirmationValidation.validation_status,
122
+ competency_questions: args.competencyQuestionsValidation.validation_status,
123
+ competency_question_assessment: args.competencyQuestionAssessmentValidation.validation_status,
124
+ failure_classification: args.failureClassificationValidation.validation_status,
125
+ revision_proposal: args.revisionProposalValidation.validation_status,
49
126
  };
50
- const rejectedClaimCount = args.seedConfirmation.rejected_claim_ids.length;
51
- const gateFailureCount = validationStatus.source_observation_directive === "valid" &&
52
- validationStatus.seed_candidate === "valid" &&
53
- validationStatus.seed_confirmation === "accepted"
54
- ? 0
55
- : 1;
127
+ const rejectedClaimCount = args.seedConfirmationValidation.rejected_claim_ids.length;
128
+ const partialClaimCount = args.seedConfirmationValidation.partial_claim_ids.length;
129
+ const deferredClaimCount = args.seedConfirmationValidation.deferred_claim_ids.length;
130
+ const invalidGateCount = [
131
+ validationStatus.source_observation_directive,
132
+ validationStatus.seed_candidate,
133
+ validationStatus.claim_realization,
134
+ validationStatus.seed_confirmation_validation,
135
+ validationStatus.competency_questions,
136
+ validationStatus.competency_question_assessment,
137
+ validationStatus.failure_classification,
138
+ validationStatus.revision_proposal,
139
+ ].filter((status) => status !== "valid").length;
56
140
  const unresolvedQuestionCount = rejectedClaimCount +
141
+ partialClaimCount +
142
+ args.failureClassificationValidation.material_failure_count +
57
143
  args.competencyQuestions.open_questions.length +
58
- gateFailureCount;
144
+ invalidGateCount;
59
145
  const competencyQuestionCount = args.competencyQuestions.questions.length;
60
146
  const passedQuestions = Math.max(0, competencyQuestionCount - unresolvedQuestionCount);
147
+ const answerStatusCounts = args.competencyQuestionAssessmentValidation.answer_status_counts;
61
148
  return {
62
149
  schema_version: "1",
63
150
  session_id: args.sessionId,
@@ -66,10 +153,26 @@ function calculateMetrics(args) {
66
153
  selected_observation_count: args.sourceObservationDirectiveValidation.selected_observation_count,
67
154
  semantic_claim_count: args.seedCandidateValidation.semantic_claim_count,
68
155
  evidence_ref_count: args.seedCandidateValidation.evidence_ref_count,
69
- confirmed_claim_count: args.seedConfirmation.confirmed_claim_ids.length,
156
+ confirmed_claim_count: args.seedConfirmationValidation.accepted_claim_ids.length,
70
157
  rejected_claim_count: rejectedClaimCount,
158
+ partial_claim_count: partialClaimCount,
159
+ deferred_claim_count: deferredClaimCount,
71
160
  competency_question_count: competencyQuestionCount,
161
+ competency_question_assessment_count: args.competencyQuestionAssessmentValidation.assessment_count,
72
162
  unresolved_question_count: unresolvedQuestionCount,
163
+ deferred_count: deferredClaimCount +
164
+ answerStatusCounts.out_of_scope +
165
+ args.failureClassificationValidation.failure_kind_counts.deferred_scope,
166
+ claim_realization_stance_counts: args.claimRealizationMapValidation.stance_counts,
167
+ confirmation_state_counts: {
168
+ accepted: args.seedConfirmationValidation.accepted_claim_ids.length,
169
+ rejected: rejectedClaimCount,
170
+ partial: partialClaimCount,
171
+ deferred: deferredClaimCount,
172
+ },
173
+ competency_question_answer_status_counts: answerStatusCounts,
174
+ failure_kind_counts: args.failureClassificationValidation.failure_kind_counts,
175
+ revision_proposal_action_counts: args.revisionProposalValidation.action_counts,
73
176
  pass_rate: competencyQuestionCount === 0
74
177
  ? 0
75
178
  : Number((passedQuestions / competencyQuestionCount).toFixed(4)),
@@ -80,16 +183,30 @@ function artifactRefsWithDefaults(args) {
80
183
  return {
81
184
  target_material_profile: args.refs.target_material_profile ?? null,
82
185
  source_inventory: args.refs.source_inventory ?? null,
186
+ initial_source_frontier: args.refs.initial_source_frontier ?? null,
83
187
  source_observations: args.refs.source_observations ?? null,
84
188
  source_observation_directive: args.refs.source_observation_directive ?? null,
85
189
  source_observation_directive_validation: args.refs.source_observation_directive_validation ?? null,
190
+ lens_judgment_index: args.refs.lens_judgment_index ?? null,
191
+ exploration_synthesis: args.refs.exploration_synthesis ?? null,
192
+ source_frontier: args.refs.source_frontier ?? null,
193
+ source_frontier_validation: args.refs.source_frontier_validation ?? null,
86
194
  domain_context_selection: args.refs.domain_context_selection ?? null,
195
+ domain_context_selection_validation: args.refs.domain_context_selection_validation ?? null,
87
196
  seed_candidate: args.refs.seed_candidate ?? null,
88
197
  seed_candidate_validation: args.refs.seed_candidate_validation ?? null,
198
+ claim_realization_map: args.refs.claim_realization_map ?? null,
199
+ claim_realization_map_validation: args.refs.claim_realization_map_validation ?? null,
89
200
  seed_confirmation: args.refs.seed_confirmation ?? null,
201
+ seed_confirmation_validation: args.refs.seed_confirmation_validation ?? null,
90
202
  competency_questions: args.refs.competency_questions ?? null,
203
+ competency_questions_validation: args.refs.competency_questions_validation ?? null,
204
+ competency_question_assessment: args.refs.competency_question_assessment ?? null,
205
+ competency_question_assessment_validation: args.refs.competency_question_assessment_validation ?? null,
91
206
  failure_classification: args.refs.failure_classification ?? null,
207
+ failure_classification_validation: args.refs.failure_classification_validation ?? null,
92
208
  revision_proposal: args.refs.revision_proposal ?? null,
209
+ revision_proposal_validation: args.refs.revision_proposal_validation ?? null,
93
210
  reconstruct_metrics: args.refs.reconstruct_metrics ?? null,
94
211
  stop_decision: args.refs.stop_decision ?? null,
95
212
  final_output: args.refs.final_output ?? null,
@@ -105,6 +222,17 @@ function completedStep(stepId, owner, performedBy, artifactRefs) {
105
222
  artifact_refs: artifactRefs,
106
223
  };
107
224
  }
225
+ function skippedStep(stepId, owner, performedBy, reason, authorityImpact) {
226
+ return {
227
+ step_id: stepId,
228
+ owner,
229
+ performed_by: performedBy,
230
+ status: "skipped",
231
+ artifact_refs: [],
232
+ reason,
233
+ authority_impact: authorityImpact,
234
+ };
235
+ }
108
236
  function runtimePerformer() {
109
237
  return {
110
238
  authority: "runtime",
@@ -115,14 +243,14 @@ function runtimePerformer() {
115
243
  function directiveAuthorPerformer(directiveAuthor) {
116
244
  return {
117
245
  authority: "host_llm",
118
- realization: "mock",
246
+ realization: directiveAuthor.owner === "mock" ? "mock" : "direct_call",
119
247
  actor_id: directiveAuthor.authorId,
120
248
  };
121
249
  }
122
250
  function confirmationProviderPerformer(confirmationProvider) {
123
251
  return {
124
252
  authority: "host_or_user",
125
- realization: "mock",
253
+ realization: confirmationProvider.owner === "mock" ? "mock" : "direct_call",
126
254
  actor_id: confirmationProvider.providerId,
127
255
  };
128
256
  }
@@ -136,11 +264,19 @@ function createRunManifest(args) {
136
264
  target_refs: args.targetRefs,
137
265
  intent: args.intent,
138
266
  execution_profile: {
139
- runner: "material-aware-happy-path",
140
- semantic_author_realization: "mock",
141
- confirmation_provider_realization: "mock",
267
+ profile_kind: args.semanticAuthorRealization === "mock"
268
+ ? "mock_semantic_slice"
269
+ : "full_integral_exploration",
270
+ runner: args.semanticAuthorRealization === "mock"
271
+ ? "material-aware-happy-path"
272
+ : "integral-exploration-direct-call",
273
+ semantic_author_realization: args.semanticAuthorRealization,
274
+ confirmation_provider_realization: args.confirmationProviderRealization,
142
275
  directive_author_id: args.directiveAuthor.authorId,
143
276
  confirmation_provider_id: args.confirmationProvider.providerId,
277
+ allowed_completion_claim: args.semanticAuthorRealization === "mock"
278
+ ? "Runtime exercised the post-Seed artifact flow with mock authorship; live semantic reconstruction is not claimed."
279
+ : "Runtime completed the live integral reconstruct path for the produced and explicitly skipped artifacts.",
144
280
  },
145
281
  artifact_refs: {
146
282
  ...args.artifactRefs,
@@ -150,13 +286,28 @@ function createRunManifest(args) {
150
286
  implemented_artifacts: [
151
287
  "target_material_profile",
152
288
  "source_inventory",
289
+ "initial_source_frontier",
153
290
  "source_observations",
154
291
  "source_observation_directive",
155
292
  "source_observation_directive_validation",
293
+ "lens_judgment_index",
294
+ "exploration_synthesis",
295
+ "source_frontier",
296
+ "source_frontier_validation",
156
297
  "seed_candidate",
157
298
  "seed_candidate_validation",
299
+ "claim_realization_map",
300
+ "claim_realization_map_validation",
158
301
  "seed_confirmation",
302
+ "seed_confirmation_validation",
159
303
  "competency_questions",
304
+ "competency_questions_validation",
305
+ "competency_question_assessment",
306
+ "competency_question_assessment_validation",
307
+ "failure_classification",
308
+ "failure_classification_validation",
309
+ "revision_proposal",
310
+ "revision_proposal_validation",
160
311
  "reconstruct_metrics",
161
312
  "stop_decision",
162
313
  "final_output",
@@ -165,36 +316,76 @@ function createRunManifest(args) {
165
316
  ],
166
317
  deferred_artifacts: [
167
318
  "domain_context_selection",
168
- "failure_classification",
169
- "revision_proposal",
319
+ "domain_context_selection_validation",
170
320
  ],
171
- deferred_reason: "The current runner is a bounded happy path; domain context selection, failure classification, and revision loops require additional host/user semantics.",
321
+ deferred_reason: "The current runner does not yet select domain context; downstream authority is narrowed to source-grounded reconstruction without selected domain-document alignment.",
172
322
  },
173
323
  steps: [
174
- completedStep("target_material_profiling", "runtime", runtimePerformer(), [
324
+ completedStep("invocation_binding", "runtime", runtimePerformer(), []),
325
+ completedStep("target_material_profile", "runtime", runtimePerformer(), [
175
326
  args.artifactRefs.target_material_profile,
176
327
  ].filter((ref) => ref !== null)),
177
328
  completedStep("source_inventory", "runtime", runtimePerformer(), [
178
329
  args.artifactRefs.source_inventory,
179
330
  ].filter((ref) => ref !== null)),
331
+ completedStep("initial_source_frontier", "runtime", runtimePerformer(), [
332
+ args.artifactRefs.initial_source_frontier,
333
+ ].filter((ref) => ref !== null)),
180
334
  completedStep("source_observation", "runtime", runtimePerformer(), [
181
335
  args.artifactRefs.source_observations,
182
336
  ].filter((ref) => ref !== null)),
183
- completedStep("source_observation_directive", "host_llm", directiveAuthorPerformer(args.directiveAuthor), [args.artifactRefs.source_observation_directive]
337
+ completedStep("observation_directive", "host_llm", directiveAuthorPerformer(args.directiveAuthor), [args.artifactRefs.source_observation_directive]
184
338
  .filter((ref) => ref !== null)),
185
- completedStep("source_observation_directive_validation", "runtime", runtimePerformer(), [
339
+ completedStep("observation_directive_validation", "runtime", runtimePerformer(), [
186
340
  args.artifactRefs.source_observation_directive_validation,
187
341
  ].filter((ref) => ref !== null)),
342
+ completedStep("lens_judgment", "host_llm", directiveAuthorPerformer(args.directiveAuthor), [args.artifactRefs.lens_judgment_index]
343
+ .filter((ref) => ref !== null)),
344
+ completedStep("exploration_synthesis", "host_llm", directiveAuthorPerformer(args.directiveAuthor), [args.artifactRefs.exploration_synthesis]
345
+ .filter((ref) => ref !== null)),
346
+ completedStep("source_frontier", "host_llm", directiveAuthorPerformer(args.directiveAuthor), [args.artifactRefs.source_frontier]
347
+ .filter((ref) => ref !== null)),
348
+ completedStep("source_frontier_validation", "runtime", runtimePerformer(), [
349
+ args.artifactRefs.source_frontier_validation,
350
+ ].filter((ref) => ref !== null)),
351
+ skippedStep("domain_context_selection", "host_llm", directiveAuthorPerformer(args.directiveAuthor), "domain context selection is not implemented in this direct-call runner.", "Final output cannot claim selected domain-document alignment."),
352
+ skippedStep("domain_context_selection_validation", "runtime", runtimePerformer(), "domain context selection was skipped.", "Runtime cannot validate domain snapshot identity for this run."),
188
353
  completedStep("seed_candidate", "host_llm", directiveAuthorPerformer(args.directiveAuthor), [args.artifactRefs.seed_candidate]
189
354
  .filter((ref) => ref !== null)),
190
355
  completedStep("seed_candidate_validation", "runtime", runtimePerformer(), [
191
356
  args.artifactRefs.seed_candidate_validation,
192
357
  ].filter((ref) => ref !== null)),
358
+ completedStep("claim_realization", "host_llm", directiveAuthorPerformer(args.directiveAuthor), [args.artifactRefs.claim_realization_map]
359
+ .filter((ref) => ref !== null)),
360
+ completedStep("claim_realization_validation", "runtime", runtimePerformer(), [
361
+ args.artifactRefs.claim_realization_map_validation,
362
+ ].filter((ref) => ref !== null)),
193
363
  completedStep("seed_confirmation", "host_or_user", confirmationProviderPerformer(args.confirmationProvider), [args.artifactRefs.seed_confirmation]
194
364
  .filter((ref) => ref !== null)),
365
+ completedStep("seed_confirmation_validation", "runtime", runtimePerformer(), [
366
+ args.artifactRefs.seed_confirmation_validation,
367
+ ].filter((ref) => ref !== null)),
195
368
  completedStep("competency_questions", "host_llm", directiveAuthorPerformer(args.directiveAuthor), [args.artifactRefs.competency_questions]
196
369
  .filter((ref) => ref !== null)),
197
- completedStep("reconstruct_metrics", "runtime", runtimePerformer(), [
370
+ completedStep("competency_questions_validation", "runtime", runtimePerformer(), [
371
+ args.artifactRefs.competency_questions_validation,
372
+ ].filter((ref) => ref !== null)),
373
+ completedStep("competency_question_assessment", "host_llm", directiveAuthorPerformer(args.directiveAuthor), [args.artifactRefs.competency_question_assessment]
374
+ .filter((ref) => ref !== null)),
375
+ completedStep("competency_question_assessment_validation", "runtime", runtimePerformer(), [
376
+ args.artifactRefs.competency_question_assessment_validation,
377
+ ].filter((ref) => ref !== null)),
378
+ completedStep("failure_classification", "host_llm", directiveAuthorPerformer(args.directiveAuthor), [args.artifactRefs.failure_classification]
379
+ .filter((ref) => ref !== null)),
380
+ completedStep("failure_classification_validation", "runtime", runtimePerformer(), [
381
+ args.artifactRefs.failure_classification_validation,
382
+ ].filter((ref) => ref !== null)),
383
+ completedStep("revision_proposal", "host_llm", directiveAuthorPerformer(args.directiveAuthor), [args.artifactRefs.revision_proposal]
384
+ .filter((ref) => ref !== null)),
385
+ completedStep("revision_proposal_validation", "runtime", runtimePerformer(), [
386
+ args.artifactRefs.revision_proposal_validation,
387
+ ].filter((ref) => ref !== null)),
388
+ completedStep("metrics", "runtime", runtimePerformer(), [
198
389
  args.artifactRefs.reconstruct_metrics,
199
390
  ].filter((ref) => ref !== null)),
200
391
  completedStep("stop_decision", "host_llm", directiveAuthorPerformer(args.directiveAuthor), [args.artifactRefs.stop_decision]
@@ -211,6 +402,766 @@ function createRunManifest(args) {
211
402
  },
212
403
  };
213
404
  }
405
+ function stripJsonFences(text) {
406
+ const trimmed = text.trim();
407
+ const fenced = /^```(?:json)?\s*([\s\S]*?)\s*```$/i.exec(trimmed);
408
+ return fenced?.[1]?.trim() ?? trimmed;
409
+ }
410
+ function parseLlmJsonObject(text, artifactName) {
411
+ const stripped = stripJsonFences(text);
412
+ const start = stripped.indexOf("{");
413
+ const end = stripped.lastIndexOf("}");
414
+ if (start < 0 || end < start) {
415
+ throw new Error(`${artifactName} author returned no JSON object.`);
416
+ }
417
+ try {
418
+ const parsed = JSON.parse(stripped.slice(start, end + 1));
419
+ if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
420
+ throw new Error("top-level value is not an object");
421
+ }
422
+ return parsed;
423
+ }
424
+ catch (error) {
425
+ throw new Error(`${artifactName} author returned invalid JSON: ${error instanceof Error ? error.message : String(error)}`);
426
+ }
427
+ }
428
+ function records(value, fieldName) {
429
+ if (!Array.isArray(value)) {
430
+ throw new Error(`${fieldName} must be an array.`);
431
+ }
432
+ return value.map((item, index) => {
433
+ if (item === null || typeof item !== "object" || Array.isArray(item)) {
434
+ throw new Error(`${fieldName}[${index}] must be an object.`);
435
+ }
436
+ return item;
437
+ });
438
+ }
439
+ function stringValue(value, fieldName) {
440
+ if (typeof value !== "string" || value.trim().length === 0) {
441
+ throw new Error(`${fieldName} must be a non-empty string.`);
442
+ }
443
+ return value.trim();
444
+ }
445
+ function optionalString(value) {
446
+ return typeof value === "string" && value.trim().length > 0
447
+ ? value.trim()
448
+ : null;
449
+ }
450
+ function stringArray(value, fieldName) {
451
+ if (value === undefined)
452
+ return [];
453
+ if (!Array.isArray(value))
454
+ throw new Error(`${fieldName} must be an array.`);
455
+ return value.map((item, index) => stringValue(item, `${fieldName}[${index}]`));
456
+ }
457
+ function evidenceRefByObservationId(sourceObservations) {
458
+ return new Map(sourceObservations.observations.map((observation) => [
459
+ observation.observation_id,
460
+ evidenceRefFromObservation(observation),
461
+ ]));
462
+ }
463
+ function evidenceRefsFromIds(args) {
464
+ const byId = evidenceRefByObservationId(args.sourceObservations);
465
+ const refs = args.observationIds.map((observationId) => {
466
+ const ref = byId.get(observationId);
467
+ if (!ref) {
468
+ throw new Error(`${args.fieldName} references unknown observation id: ${observationId}`);
469
+ }
470
+ return ref;
471
+ });
472
+ if (refs.length === 0) {
473
+ throw new Error(`${args.fieldName} must reference at least one observation id.`);
474
+ }
475
+ return refs;
476
+ }
477
+ function claimFromLlm(args) {
478
+ const claimId = optionalString(args.raw.claim_id) ?? args.fallbackId;
479
+ return {
480
+ claim_id: claimId,
481
+ name: stringValue(args.raw.name, `${args.fieldName}.name`),
482
+ statement: stringValue(args.raw.statement, `${args.fieldName}.statement`),
483
+ evidence_refs: evidenceRefsFromIds({
484
+ observationIds: stringArray(args.raw.evidence_observation_ids, `${args.fieldName}.evidence_observation_ids`),
485
+ sourceObservations: args.sourceObservations,
486
+ fieldName: `${args.fieldName}.evidence_observation_ids`,
487
+ }),
488
+ };
489
+ }
490
+ function claimsFromLlm(args) {
491
+ return records(args.value ?? [], args.prefix).map((raw, index) => claimFromLlm({
492
+ raw,
493
+ fallbackId: `${args.prefix}-${index + 1}`,
494
+ sourceObservations: args.sourceObservations,
495
+ fieldName: `${args.prefix}[${index}]`,
496
+ }));
497
+ }
498
+ function observationPromptPayload(sourceObservations) {
499
+ return sourceObservations.observations.map((observation) => ({
500
+ observation_id: observation.observation_id,
501
+ target_material_kind: observation.target_material_kind,
502
+ source_ref: observation.source_ref,
503
+ location: observation.location,
504
+ summary: observation.summary,
505
+ structural_data: observation.structural_data,
506
+ }));
507
+ }
508
+ function lensJudgmentPromptPayload(lensJudgments) {
509
+ return lensJudgments.map((judgment) => ({
510
+ lens_id: judgment.lens_id,
511
+ candidate_labels: judgment.candidate_labels.map((label) => ({
512
+ label_id: label.label_id,
513
+ label: label.label,
514
+ evidence_observation_ids: label.evidence_refs.map((ref) => ref.observation_id),
515
+ rationale: label.rationale,
516
+ })),
517
+ semantic_gaps: judgment.semantic_gaps.map((gap) => ({
518
+ gap_id: gap.gap_id,
519
+ description: gap.description,
520
+ evidence_observation_ids: gap.evidence_refs.map((ref) => ref.observation_id),
521
+ requested_source_refs: gap.requested_source_refs,
522
+ materiality_rationale: gap.materiality_rationale,
523
+ })),
524
+ no_next_frontier_rationale: judgment.no_next_frontier_rationale,
525
+ }));
526
+ }
527
+ async function callJsonAuthor(args) {
528
+ const result = await args.llmCall(args.systemPrompt, JSON.stringify(args.userPayload, null, 2), { ...args.llmConfig, max_tokens: args.maxTokens });
529
+ return parseLlmJsonObject(result.text, args.artifactName);
530
+ }
531
+ export function createDirectCallReconstructDirectiveAuthor(args = {}) {
532
+ const authorId = "direct-call-reconstruct-directive-author";
533
+ const llmConfig = args.llmConfig ?? {};
534
+ const llmCall = args.llmCall ?? callLlm;
535
+ const baseSystem = [
536
+ "You are authoring reconstruct semantic artifacts.",
537
+ "Return only valid JSON. Do not wrap in Markdown.",
538
+ "Use only provided observation ids as evidence. Do not invent source refs, ids, files, or facts.",
539
+ "Runtime will validate ids and refs. If evidence is insufficient, mark gaps or open questions instead of guessing.",
540
+ ].join("\n");
541
+ return {
542
+ authorId,
543
+ owner: "host_llm",
544
+ async writeSourceObservationDirective(input) {
545
+ requireFirstObservation(input.sourceObservations);
546
+ const raw = await callJsonAuthor({
547
+ llmCall,
548
+ llmConfig,
549
+ artifactName: "SourceObservationDirective",
550
+ maxTokens: 2400,
551
+ systemPrompt: [
552
+ baseSystem,
553
+ "Select observations that should become evidence candidates for the declared reconstruct purpose.",
554
+ "JSON shape: {\"selected_observations\":[{\"observation_id\":\"...\",\"selection_rationale\":\"...\"}],\"open_questions\":[\"...\"]}",
555
+ ].join("\n"),
556
+ userPayload: {
557
+ intent: input.intent,
558
+ target_material_profile: input.targetMaterialProfile,
559
+ source_observations: observationPromptPayload(input.sourceObservations),
560
+ },
561
+ });
562
+ const byId = new Map(input.sourceObservations.observations.map((observation) => [
563
+ observation.observation_id,
564
+ observation,
565
+ ]));
566
+ const selected = records(raw.selected_observations, "selected_observations").map((selection, index) => {
567
+ const observationId = stringValue(selection.observation_id, `selected_observations[${index}].observation_id`);
568
+ const observation = byId.get(observationId);
569
+ if (!observation) {
570
+ throw new Error(`SourceObservationDirective selected unknown observation id: ${observationId}`);
571
+ }
572
+ return {
573
+ ...evidenceRefFromObservation(observation),
574
+ selection_rationale: stringValue(selection.selection_rationale, `selected_observations[${index}].selection_rationale`),
575
+ };
576
+ });
577
+ return {
578
+ schema_version: "1",
579
+ session_id: input.sessionId,
580
+ created_at: isoNow(),
581
+ selected_observations: selected,
582
+ open_questions: stringArray(raw.open_questions, "open_questions"),
583
+ };
584
+ },
585
+ async writeLensJudgment(input) {
586
+ const raw = await callJsonAuthor({
587
+ llmCall,
588
+ llmConfig,
589
+ artifactName: `ReconstructLensJudgment:${input.lensId}`,
590
+ maxTokens: 3200,
591
+ systemPrompt: [
592
+ baseSystem,
593
+ `You are the ${input.lensId} reconstruct lens. Apply this perspective:`,
594
+ input.lensPrompt,
595
+ "JSON shape: {\"candidate_labels\":[{\"label_id\":\"...\",\"label\":\"...\",\"evidence_observation_ids\":[\"...\"],\"rationale\":\"...\"}],\"semantic_gaps\":[{\"gap_id\":\"...\",\"description\":\"...\",\"evidence_observation_ids\":[\"...\"],\"requested_source_refs\":[\"...\"],\"materiality_rationale\":\"...\"}],\"no_next_frontier_rationale\":\"... or null\"}",
596
+ ].join("\n"),
597
+ userPayload: {
598
+ intent: input.intent,
599
+ round_id: input.roundId,
600
+ source_observation_directive_ref: input.sourceObservationDirectiveRef,
601
+ selected_observations: input.sourceObservationDirective.selected_observations,
602
+ source_observations: observationPromptPayload(input.sourceObservations),
603
+ },
604
+ });
605
+ return {
606
+ schema_version: "1",
607
+ session_id: input.sessionId,
608
+ round_id: input.roundId,
609
+ lens_id: input.lensId,
610
+ created_at: isoNow(),
611
+ source_observation_directive_ref: input.sourceObservationDirectiveRef,
612
+ candidate_labels: records(raw.candidate_labels ?? [], "candidate_labels")
613
+ .map((label, index) => ({
614
+ label_id: optionalString(label.label_id) ?? `${input.lensId}-label-${index + 1}`,
615
+ label: stringValue(label.label, `candidate_labels[${index}].label`),
616
+ evidence_refs: evidenceRefsFromIds({
617
+ observationIds: stringArray(label.evidence_observation_ids, `candidate_labels[${index}].evidence_observation_ids`),
618
+ sourceObservations: input.sourceObservations,
619
+ fieldName: `candidate_labels[${index}].evidence_observation_ids`,
620
+ }),
621
+ rationale: stringValue(label.rationale, `candidate_labels[${index}].rationale`),
622
+ })),
623
+ semantic_gaps: records(raw.semantic_gaps ?? [], "semantic_gaps")
624
+ .map((gap, index) => ({
625
+ gap_id: optionalString(gap.gap_id) ?? `${input.lensId}-gap-${index + 1}`,
626
+ description: stringValue(gap.description, `semantic_gaps[${index}].description`),
627
+ evidence_refs: evidenceRefsFromIds({
628
+ observationIds: stringArray(gap.evidence_observation_ids, `semantic_gaps[${index}].evidence_observation_ids`),
629
+ sourceObservations: input.sourceObservations,
630
+ fieldName: `semantic_gaps[${index}].evidence_observation_ids`,
631
+ }),
632
+ requested_source_refs: stringArray(gap.requested_source_refs, `semantic_gaps[${index}].requested_source_refs`),
633
+ materiality_rationale: stringValue(gap.materiality_rationale, `semantic_gaps[${index}].materiality_rationale`),
634
+ })),
635
+ no_next_frontier_rationale: optionalString(raw.no_next_frontier_rationale),
636
+ directive_author: {
637
+ owner: "host_llm",
638
+ author_id: authorId,
639
+ },
640
+ };
641
+ },
642
+ async writeExplorationSynthesis(input) {
643
+ const raw = await callJsonAuthor({
644
+ llmCall,
645
+ llmConfig,
646
+ artifactName: "ExplorationSynthesis",
647
+ maxTokens: 3200,
648
+ systemPrompt: [
649
+ baseSystem,
650
+ "Integrate reconstruct lens judgments. Preserve disagreements and gaps. Request new source refs only when they are concrete and unjudged.",
651
+ "JSON shape: {\"accepted_gaps\":[{\"gap_id\":\"...\",\"lens_id\":\"...\",\"description\":\"...\",\"evidence_observation_ids\":[\"...\"]}],\"requested_source_refs\":[{\"source_ref\":\"...\",\"rationale\":\"...\",\"priority\":\"high|medium|low\"}],\"no_next_frontier_rationale\":\"... or null\"}",
652
+ ].join("\n"),
653
+ userPayload: {
654
+ intent: input.intent,
655
+ round_id: input.roundId,
656
+ lens_judgment_index_ref: input.lensJudgmentIndexRef,
657
+ lens_judgments: lensJudgmentPromptPayload(input.lensJudgments),
658
+ },
659
+ });
660
+ const sourceObservations = {
661
+ schema_version: "1",
662
+ session_id: input.sessionId,
663
+ created_at: isoNow(),
664
+ observations: input.lensJudgments.flatMap((judgment) => [
665
+ ...judgment.candidate_labels.flatMap((label) => label.evidence_refs),
666
+ ...judgment.semantic_gaps.flatMap((gap) => gap.evidence_refs),
667
+ ].map((ref) => ({
668
+ observation_id: ref.observation_id,
669
+ target_material_kind: ref.target_material_kind,
670
+ adapter_id: "evidence-ref-projection",
671
+ source_ref: ref.source_ref,
672
+ location: ref.location,
673
+ summary: "Projected from lens judgment evidence refs.",
674
+ structural_data: {},
675
+ }))),
676
+ skipped_refs: [],
677
+ validation_results: [],
678
+ };
679
+ return {
680
+ schema_version: "1",
681
+ session_id: input.sessionId,
682
+ round_id: input.roundId,
683
+ created_at: isoNow(),
684
+ lens_judgment_index_ref: input.lensJudgmentIndexRef,
685
+ accepted_gaps: records(raw.accepted_gaps ?? [], "accepted_gaps")
686
+ .map((gap, index) => ({
687
+ gap_id: stringValue(gap.gap_id, `accepted_gaps[${index}].gap_id`),
688
+ lens_id: stringValue(gap.lens_id, `accepted_gaps[${index}].lens_id`),
689
+ description: stringValue(gap.description, `accepted_gaps[${index}].description`),
690
+ evidence_refs: evidenceRefsFromIds({
691
+ observationIds: stringArray(gap.evidence_observation_ids, `accepted_gaps[${index}].evidence_observation_ids`),
692
+ sourceObservations,
693
+ fieldName: `accepted_gaps[${index}].evidence_observation_ids`,
694
+ }),
695
+ })),
696
+ requested_source_refs: records(raw.requested_source_refs ?? [], "requested_source_refs").map((request, index) => {
697
+ const priorityValue = stringValue(request.priority, `requested_source_refs[${index}].priority`);
698
+ if (priorityValue !== "high" && priorityValue !== "medium" && priorityValue !== "low") {
699
+ throw new Error(`requested_source_refs[${index}].priority is invalid.`);
700
+ }
701
+ const priority = priorityValue;
702
+ return {
703
+ source_ref: stringValue(request.source_ref, `requested_source_refs[${index}].source_ref`),
704
+ rationale: stringValue(request.rationale, `requested_source_refs[${index}].rationale`),
705
+ priority,
706
+ };
707
+ }),
708
+ no_next_frontier_rationale: optionalString(raw.no_next_frontier_rationale),
709
+ directive_author: {
710
+ owner: "host_llm",
711
+ author_id: authorId,
712
+ },
713
+ };
714
+ },
715
+ async writeSourceFrontier(input) {
716
+ const raw = await callJsonAuthor({
717
+ llmCall,
718
+ llmConfig,
719
+ artifactName: "SourceFrontier",
720
+ maxTokens: 2000,
721
+ systemPrompt: [
722
+ baseSystem,
723
+ "Convert exploration synthesis into a concrete source frontier. If no new source should be read, return an empty frontier_refs array and a no_next_frontier_rationale.",
724
+ "JSON shape: {\"frontier_refs\":[{\"source_ref\":\"...\",\"rationale\":\"...\",\"priority\":\"high|medium|low\"}],\"no_next_frontier_rationale\":\"... or null\"}",
725
+ ].join("\n"),
726
+ userPayload: {
727
+ intent: input.intent,
728
+ round_id: input.roundId,
729
+ exploration_synthesis_ref: input.explorationSynthesisRef,
730
+ exploration_synthesis: input.explorationSynthesis,
731
+ },
732
+ });
733
+ const frontierRefs = records(raw.frontier_refs ?? [], "frontier_refs")
734
+ .map((frontier, index) => {
735
+ const priorityValue = stringValue(frontier.priority, `frontier_refs[${index}].priority`);
736
+ if (priorityValue !== "high" && priorityValue !== "medium" && priorityValue !== "low") {
737
+ throw new Error(`frontier_refs[${index}].priority is invalid.`);
738
+ }
739
+ const priority = priorityValue;
740
+ const sourceRef = stringValue(frontier.source_ref, `frontier_refs[${index}].source_ref`);
741
+ return {
742
+ frontier_ref_id: `frontier_${index + 1}`,
743
+ source_ref: sourceRef,
744
+ rationale: stringValue(frontier.rationale, `frontier_refs[${index}].rationale`),
745
+ priority,
746
+ };
747
+ });
748
+ return {
749
+ schema_version: "1",
750
+ session_id: input.sessionId,
751
+ round_id: input.roundId,
752
+ created_at: isoNow(),
753
+ exploration_synthesis_ref: input.explorationSynthesisRef,
754
+ frontier_refs: frontierRefs,
755
+ no_next_frontier_rationale: optionalString(raw.no_next_frontier_rationale),
756
+ directive_author: {
757
+ owner: "host_llm",
758
+ author_id: authorId,
759
+ },
760
+ };
761
+ },
762
+ async writeSeedCandidate(input) {
763
+ const raw = await callJsonAuthor({
764
+ llmCall,
765
+ llmConfig,
766
+ artifactName: "SeedCandidate",
767
+ maxTokens: 4200,
768
+ systemPrompt: [
769
+ baseSystem,
770
+ "Author an ontology Seed candidate for the declared purpose. Claims must be evidence-backed by observation ids. Cover purpose, non-goals, entities, relations, actions, properties, rules, and open questions.",
771
+ "Each claim must include claim_id for artifact linkage and name for user-facing meaning. name must be a concise meaningful label such as RawIngestEvent, Usage Mart, or Dashboard Overview, not Entity 1 or a numbered placeholder.",
772
+ "Each claim shape: {\"claim_id\":\"...\",\"name\":\"...\",\"statement\":\"...\",\"evidence_observation_ids\":[\"...\"]}",
773
+ "JSON shape: {\"purpose\":claim,\"non_goals\":[claim],\"entities\":[claim],\"relations\":[claim],\"actions\":[claim],\"properties\":[claim],\"rules\":[claim],\"open_questions\":[\"...\"]}",
774
+ ].join("\n"),
775
+ userPayload: {
776
+ intent: input.intent,
777
+ selected_observations: input.sourceObservationDirective.selected_observations,
778
+ source_observations: observationPromptPayload(input.sourceObservations),
779
+ lens_judgment_index: input.lensJudgmentIndex,
780
+ exploration_synthesis: input.explorationSynthesis,
781
+ source_frontier_validation: input.sourceFrontierValidation,
782
+ },
783
+ });
784
+ const purposeRaw = raw.purpose && typeof raw.purpose === "object" && !Array.isArray(raw.purpose)
785
+ ? raw.purpose
786
+ : null;
787
+ if (!purposeRaw)
788
+ throw new Error("SeedCandidate.purpose must be an object.");
789
+ return {
790
+ schema_version: "1",
791
+ session_id: input.sessionId,
792
+ created_at: isoNow(),
793
+ purpose: claimFromLlm({
794
+ raw: purposeRaw,
795
+ fallbackId: "purpose-1",
796
+ sourceObservations: input.sourceObservations,
797
+ fieldName: "purpose",
798
+ }),
799
+ non_goals: claimsFromLlm({
800
+ value: raw.non_goals,
801
+ prefix: "non-goal",
802
+ sourceObservations: input.sourceObservations,
803
+ }),
804
+ entities: claimsFromLlm({
805
+ value: raw.entities,
806
+ prefix: "entity",
807
+ sourceObservations: input.sourceObservations,
808
+ }),
809
+ relations: claimsFromLlm({
810
+ value: raw.relations,
811
+ prefix: "relation",
812
+ sourceObservations: input.sourceObservations,
813
+ }),
814
+ actions: claimsFromLlm({
815
+ value: raw.actions,
816
+ prefix: "action",
817
+ sourceObservations: input.sourceObservations,
818
+ }),
819
+ properties: claimsFromLlm({
820
+ value: raw.properties,
821
+ prefix: "property",
822
+ sourceObservations: input.sourceObservations,
823
+ }),
824
+ rules: claimsFromLlm({
825
+ value: raw.rules,
826
+ prefix: "rule",
827
+ sourceObservations: input.sourceObservations,
828
+ }),
829
+ open_questions: stringArray(raw.open_questions, "open_questions"),
830
+ };
831
+ },
832
+ async writeClaimRealizationMap(input) {
833
+ const raw = await callJsonAuthor({
834
+ llmCall,
835
+ llmConfig,
836
+ artifactName: "ClaimRealizationMap",
837
+ maxTokens: 3000,
838
+ systemPrompt: [
839
+ baseSystem,
840
+ `Classify every Seed claim with one stance from: ${CLAIM_REALIZATION_STANCES.join(", ")}.`,
841
+ "JSON shape: {\"claim_realizations\":[{\"claim_id\":\"...\",\"stance\":\"...\",\"rationale\":\"...\"}]}",
842
+ ].join("\n"),
843
+ userPayload: {
844
+ seed_candidate_ref: input.seedCandidateRef,
845
+ seed_candidate: input.seedCandidate,
846
+ source_observations: observationPromptPayload(input.sourceObservations),
847
+ },
848
+ });
849
+ const claimById = new Map(allClaims(input.seedCandidate).map((claim) => [
850
+ claim.claim_id,
851
+ claim,
852
+ ]));
853
+ const realizations = records(raw.claim_realizations, "claim_realizations").map((realization, index) => {
854
+ const claimId = stringValue(realization.claim_id, `claim_realizations[${index}].claim_id`);
855
+ const claim = claimById.get(claimId);
856
+ if (!claim)
857
+ throw new Error(`ClaimRealizationMap references unknown claim id: ${claimId}`);
858
+ const stance = stringValue(realization.stance, `claim_realizations[${index}].stance`);
859
+ if (!CLAIM_REALIZATION_STANCES.includes(stance)) {
860
+ throw new Error(`ClaimRealizationMap stance is invalid for ${claimId}: ${stance}`);
861
+ }
862
+ return {
863
+ claim_id: claimId,
864
+ stance,
865
+ evidence_refs: claim.evidence_refs,
866
+ rationale: stringValue(realization.rationale, `claim_realizations[${index}].rationale`),
867
+ };
868
+ });
869
+ return {
870
+ schema_version: "1",
871
+ session_id: input.sessionId,
872
+ created_at: isoNow(),
873
+ seed_candidate_ref: input.seedCandidateRef,
874
+ claim_realizations: realizations,
875
+ directive_author: {
876
+ owner: "host_llm",
877
+ author_id: authorId,
878
+ },
879
+ };
880
+ },
881
+ async writeCompetencyQuestions(input) {
882
+ const raw = await callJsonAuthor({
883
+ llmCall,
884
+ llmConfig,
885
+ artifactName: "CompetencyQuestions",
886
+ maxTokens: 3200,
887
+ systemPrompt: [
888
+ baseSystem,
889
+ "Write competency questions that test accepted or CQ-eligible Seed claims for the declared purpose.",
890
+ "Every cq_eligible_claim_id must appear in at least one linked_claim_ids array. Group related claims when useful, but do not leave an eligible claim untested.",
891
+ "JSON shape: {\"questions\":[{\"question_id\":\"...\",\"question\":\"...\",\"linked_claim_ids\":[\"...\"],\"evidence_observation_ids\":[\"...\"]}],\"open_questions\":[\"...\"]}",
892
+ ].join("\n"),
893
+ userPayload: {
894
+ seed_candidate: input.seedCandidate,
895
+ seed_confirmation: input.seedConfirmation,
896
+ seed_confirmation_validation: input.seedConfirmationValidation,
897
+ claim_realization_map: input.claimRealizationMap,
898
+ },
899
+ });
900
+ const eligibleClaimIds = new Set(input.seedConfirmationValidation.cq_eligible_claim_ids);
901
+ return {
902
+ schema_version: "1",
903
+ session_id: input.sessionId,
904
+ created_at: isoNow(),
905
+ seed_confirmation_ref: input.seedConfirmationRef,
906
+ questions: records(raw.questions, "questions").map((question, index) => {
907
+ const linkedClaimIds = stringArray(question.linked_claim_ids, `questions[${index}].linked_claim_ids`);
908
+ for (const claimId of linkedClaimIds) {
909
+ if (!eligibleClaimIds.has(claimId)) {
910
+ throw new Error(`CompetencyQuestions linked non-eligible claim id: ${claimId}`);
911
+ }
912
+ }
913
+ return {
914
+ question_id: optionalString(question.question_id) ?? `cq-${index + 1}`,
915
+ question: stringValue(question.question, `questions[${index}].question`),
916
+ linked_claim_ids: linkedClaimIds,
917
+ evidence_refs: evidenceRefsFromIds({
918
+ observationIds: stringArray(question.evidence_observation_ids, `questions[${index}].evidence_observation_ids`),
919
+ sourceObservations: {
920
+ schema_version: "1",
921
+ session_id: input.sessionId,
922
+ created_at: isoNow(),
923
+ observations: allClaims(input.seedCandidate).flatMap((claim) => claim.evidence_refs.map((ref) => ({
924
+ observation_id: ref.observation_id,
925
+ target_material_kind: ref.target_material_kind,
926
+ adapter_id: "seed-evidence-ref-projection",
927
+ source_ref: ref.source_ref,
928
+ location: ref.location,
929
+ summary: "Projected from Seed evidence refs.",
930
+ structural_data: {},
931
+ }))),
932
+ skipped_refs: [],
933
+ validation_results: [],
934
+ },
935
+ fieldName: `questions[${index}].evidence_observation_ids`,
936
+ }),
937
+ };
938
+ }),
939
+ open_questions: stringArray(raw.open_questions, "open_questions"),
940
+ directive_author: {
941
+ owner: "host_llm",
942
+ author_id: authorId,
943
+ },
944
+ };
945
+ },
946
+ async writeCompetencyQuestionAssessment(input) {
947
+ const raw = await callJsonAuthor({
948
+ llmCall,
949
+ llmConfig,
950
+ artifactName: "CompetencyQuestionAssessment",
951
+ maxTokens: 3200,
952
+ systemPrompt: [
953
+ baseSystem,
954
+ `Assess every competency question exactly once. answer_status must be one of: ${ANSWER_STATUSES.join(", ")}.`,
955
+ "JSON shape: {\"assessments\":[{\"question_id\":\"...\",\"answer_status\":\"...\",\"rationale\":\"...\"}]}",
956
+ ].join("\n"),
957
+ userPayload: {
958
+ competency_questions_ref: input.competencyQuestionsRef,
959
+ competency_questions: input.competencyQuestions,
960
+ competency_questions_validation: input.competencyQuestionsValidation,
961
+ claim_realization_map: input.claimRealizationMap,
962
+ },
963
+ });
964
+ const questionById = new Map(input.competencyQuestions.questions.map((question) => [
965
+ question.question_id,
966
+ question,
967
+ ]));
968
+ return {
969
+ schema_version: "1",
970
+ session_id: input.sessionId,
971
+ created_at: isoNow(),
972
+ competency_questions_ref: input.competencyQuestionsRef,
973
+ competency_questions_validation_ref: input.competencyQuestionsValidationRef,
974
+ assessments: records(raw.assessments, "assessments").map((assessment, index) => {
975
+ const questionId = stringValue(assessment.question_id, `assessments[${index}].question_id`);
976
+ const question = questionById.get(questionId);
977
+ if (!question) {
978
+ throw new Error(`CompetencyQuestionAssessment references unknown question id: ${questionId}`);
979
+ }
980
+ const answerStatus = stringValue(assessment.answer_status, `assessments[${index}].answer_status`);
981
+ if (!ANSWER_STATUSES.includes(answerStatus)) {
982
+ throw new Error(`CompetencyQuestionAssessment answer_status is invalid: ${answerStatus}`);
983
+ }
984
+ return {
985
+ question_id: questionId,
986
+ answer_status: answerStatus,
987
+ linked_claim_ids: question.linked_claim_ids,
988
+ evidence_refs: question.evidence_refs,
989
+ rationale: stringValue(assessment.rationale, `assessments[${index}].rationale`),
990
+ };
991
+ }),
992
+ directive_author: {
993
+ owner: "host_llm",
994
+ author_id: authorId,
995
+ },
996
+ };
997
+ },
998
+ async writeFailureClassification(input) {
999
+ const raw = await callJsonAuthor({
1000
+ llmCall,
1001
+ llmConfig,
1002
+ artifactName: "FailureClassification",
1003
+ maxTokens: 2600,
1004
+ systemPrompt: [
1005
+ baseSystem,
1006
+ `Classify unsafe or incomplete assessments. failure_kind must be one of: ${FAILURE_KINDS.join(", ")}. recommended_action must be revise_seed, collect_evidence, defer, reject_claim, or ask_user.`,
1007
+ "JSON shape: {\"failures\":[{\"failure_id\":\"...\",\"failure_kind\":\"...\",\"materiality\":\"material|non_material\",\"question_id\":\"... or null\",\"claim_id\":\"... or null\",\"rationale\":\"...\",\"recommended_action\":\"...\"}]}",
1008
+ ].join("\n"),
1009
+ userPayload: {
1010
+ competency_question_assessment_ref: input.competencyQuestionAssessmentRef,
1011
+ competency_question_assessment: input.competencyQuestionAssessment,
1012
+ competency_question_assessment_validation: input.competencyQuestionAssessmentValidation,
1013
+ seed_confirmation_validation: input.seedConfirmationValidation,
1014
+ },
1015
+ });
1016
+ return {
1017
+ schema_version: "1",
1018
+ session_id: input.sessionId,
1019
+ created_at: isoNow(),
1020
+ competency_question_assessment_ref: input.competencyQuestionAssessmentRef,
1021
+ seed_confirmation_validation_ref: input.seedConfirmationValidation.seed_confirmation_ref,
1022
+ failures: records(raw.failures ?? [], "failures").map((failure, index) => {
1023
+ const failureKind = stringValue(failure.failure_kind, `failures[${index}].failure_kind`);
1024
+ if (!FAILURE_KINDS.includes(failureKind)) {
1025
+ throw new Error(`FailureClassification failure_kind is invalid: ${failureKind}`);
1026
+ }
1027
+ const materiality = stringValue(failure.materiality, `failures[${index}].materiality`);
1028
+ if (materiality !== "material" && materiality !== "non_material") {
1029
+ throw new Error(`FailureClassification materiality is invalid: ${materiality}`);
1030
+ }
1031
+ const recommendedAction = stringValue(failure.recommended_action, `failures[${index}].recommended_action`);
1032
+ if (!["revise_seed", "collect_evidence", "defer", "reject_claim", "ask_user"].includes(recommendedAction)) {
1033
+ throw new Error(`FailureClassification recommended_action is invalid: ${recommendedAction}`);
1034
+ }
1035
+ return {
1036
+ failure_id: optionalString(failure.failure_id) ?? `failure-${index + 1}`,
1037
+ failure_kind: failureKind,
1038
+ materiality,
1039
+ question_id: optionalString(failure.question_id),
1040
+ claim_id: optionalString(failure.claim_id),
1041
+ rationale: stringValue(failure.rationale, `failures[${index}].rationale`),
1042
+ recommended_action: recommendedAction,
1043
+ };
1044
+ }),
1045
+ directive_author: {
1046
+ owner: "host_llm",
1047
+ author_id: authorId,
1048
+ },
1049
+ };
1050
+ },
1051
+ async writeRevisionProposal(input) {
1052
+ const raw = await callJsonAuthor({
1053
+ llmCall,
1054
+ llmConfig,
1055
+ artifactName: "RevisionProposal",
1056
+ maxTokens: 2600,
1057
+ systemPrompt: [
1058
+ baseSystem,
1059
+ `Propose bounded ontology actions for failures. action must be one of: ${REVISION_ACTIONS.join(", ")}.`,
1060
+ "JSON shape: {\"proposals\":[{\"proposal_id\":\"...\",\"target_type\":\"claim|question|failure|seed|domain_context\",\"target_id\":\"...\",\"action\":\"...\",\"rationale\":\"...\",\"expected_effect\":\"...\"}]}",
1061
+ ].join("\n"),
1062
+ userPayload: {
1063
+ failure_classification_ref: input.failureClassificationRef,
1064
+ failure_classification: input.failureClassification,
1065
+ failure_classification_validation: input.failureClassificationValidation,
1066
+ },
1067
+ });
1068
+ return {
1069
+ schema_version: "1",
1070
+ session_id: input.sessionId,
1071
+ created_at: isoNow(),
1072
+ failure_classification_ref: input.failureClassificationRef,
1073
+ proposals: records(raw.proposals ?? [], "proposals").map((proposal, index) => {
1074
+ const action = stringValue(proposal.action, `proposals[${index}].action`);
1075
+ if (!REVISION_ACTIONS.includes(action)) {
1076
+ throw new Error(`RevisionProposal action is invalid: ${action}`);
1077
+ }
1078
+ const targetType = stringValue(proposal.target_type, `proposals[${index}].target_type`);
1079
+ if (!["claim", "question", "failure", "seed", "domain_context"].includes(targetType)) {
1080
+ throw new Error(`RevisionProposal target_type is invalid: ${targetType}`);
1081
+ }
1082
+ return {
1083
+ proposal_id: optionalString(proposal.proposal_id) ?? `proposal-${index + 1}`,
1084
+ target_type: targetType,
1085
+ target_id: stringValue(proposal.target_id, `proposals[${index}].target_id`),
1086
+ action,
1087
+ rationale: stringValue(proposal.rationale, `proposals[${index}].rationale`),
1088
+ expected_effect: stringValue(proposal.expected_effect, `proposals[${index}].expected_effect`),
1089
+ };
1090
+ }),
1091
+ directive_author: {
1092
+ owner: "host_llm",
1093
+ author_id: authorId,
1094
+ },
1095
+ };
1096
+ },
1097
+ async writeStopDecision(input) {
1098
+ const raw = await callJsonAuthor({
1099
+ llmCall,
1100
+ llmConfig,
1101
+ artifactName: "StopDecision",
1102
+ maxTokens: 1600,
1103
+ systemPrompt: [
1104
+ baseSystem,
1105
+ "Decide whether the current reconstructed result is decision-ready for the declared purpose. This is a presentation decision, not user control.",
1106
+ "JSON shape: {\"decision\":\"stop|continue|ask_user\",\"rationale\":\"...\",\"next_actions\":[\"...\"]}",
1107
+ ].join("\n"),
1108
+ userPayload: {
1109
+ intent: input.intent,
1110
+ metrics: input.metrics,
1111
+ failure_classification: input.failureClassification,
1112
+ revision_proposal: input.revisionProposal,
1113
+ },
1114
+ });
1115
+ const decision = stringValue(raw.decision, "decision");
1116
+ if (decision !== "stop" && decision !== "continue" && decision !== "ask_user") {
1117
+ throw new Error(`StopDecision decision is invalid: ${decision}`);
1118
+ }
1119
+ return {
1120
+ schema_version: "1",
1121
+ session_id: input.sessionId,
1122
+ created_at: isoNow(),
1123
+ decision,
1124
+ declared_purpose: input.intent,
1125
+ metrics_ref: input.metricsRef,
1126
+ rationale: stringValue(raw.rationale, "rationale"),
1127
+ next_actions: stringArray(raw.next_actions, "next_actions"),
1128
+ directive_author: {
1129
+ owner: "host_llm",
1130
+ author_id: authorId,
1131
+ },
1132
+ };
1133
+ },
1134
+ async writeFinalOutput(input) {
1135
+ const result = await llmCall([
1136
+ "You are writing the final reconstruct result for the user.",
1137
+ "Write concise Markdown. Ground every important statement in artifact refs or ids.",
1138
+ "Use claim.name as the user-facing label. Include claim_id only where artifact truth or traceability needs it.",
1139
+ "Include execution profile, completion scope, skipped/deferred stages, confirmed Seed content, CQ assessment, material failures, revision proposals, and artifact truth.",
1140
+ "Do not claim full domain-context alignment when domain context selection was skipped.",
1141
+ ].join("\n"), JSON.stringify({
1142
+ session_id: input.sessionId,
1143
+ intent: input.intent,
1144
+ target_material_profile: input.targetMaterialProfile,
1145
+ seed_candidate: input.seedCandidate,
1146
+ claim_realization_map: input.claimRealizationMap,
1147
+ seed_confirmation: input.seedConfirmation,
1148
+ seed_confirmation_validation: input.seedConfirmationValidation,
1149
+ competency_questions: input.competencyQuestions,
1150
+ competency_question_assessment: input.competencyQuestionAssessment,
1151
+ failure_classification: input.failureClassification,
1152
+ revision_proposal: input.revisionProposal,
1153
+ metrics: input.metrics,
1154
+ stop_decision: input.stopDecision,
1155
+ artifact_refs: input.artifactRefs,
1156
+ reconstruct_record_path: input.reconstructRecordPath,
1157
+ reconstruct_run_manifest_path: input.reconstructRunManifestPath,
1158
+ execution_profile: input.reconstructRunManifest.execution_profile,
1159
+ skipped_steps: input.reconstructRunManifest.steps.filter((step) => step.status === "skipped"),
1160
+ }, null, 2), { ...llmConfig, max_tokens: 4200 });
1161
+ return result.text;
1162
+ },
1163
+ };
1164
+ }
214
1165
  export function createMockReconstructDirectiveAuthor() {
215
1166
  const authorId = "mock-reconstruct-directive-author";
216
1167
  return {
@@ -236,6 +1187,63 @@ export function createMockReconstructDirectiveAuthor() {
236
1187
  ],
237
1188
  };
238
1189
  },
1190
+ async writeLensJudgment(input) {
1191
+ const observation = requireFirstObservation(input.sourceObservations);
1192
+ return {
1193
+ schema_version: "1",
1194
+ session_id: input.sessionId,
1195
+ round_id: input.roundId,
1196
+ lens_id: input.lensId,
1197
+ created_at: isoNow(),
1198
+ source_observation_directive_ref: input.sourceObservationDirectiveRef,
1199
+ candidate_labels: [
1200
+ {
1201
+ label_id: `${input.lensId}-label-1`,
1202
+ label: `${input.lensId} candidate label for observed material`,
1203
+ evidence_refs: [evidenceRefFromObservation(observation)],
1204
+ rationale: "Mock lens judgment preserves the stage shape for tests.",
1205
+ },
1206
+ ],
1207
+ semantic_gaps: [],
1208
+ no_next_frontier_rationale: "Mock lens does not request additional source frontier refs.",
1209
+ directive_author: {
1210
+ owner: "mock",
1211
+ author_id: authorId,
1212
+ },
1213
+ };
1214
+ },
1215
+ async writeExplorationSynthesis(input) {
1216
+ return {
1217
+ schema_version: "1",
1218
+ session_id: input.sessionId,
1219
+ round_id: input.roundId,
1220
+ created_at: isoNow(),
1221
+ lens_judgment_index_ref: input.lensJudgmentIndexRef,
1222
+ accepted_gaps: [],
1223
+ requested_source_refs: [],
1224
+ no_next_frontier_rationale: "Mock synthesis accepts no next frontier for the bounded test slice.",
1225
+ directive_author: {
1226
+ owner: "mock",
1227
+ author_id: authorId,
1228
+ },
1229
+ };
1230
+ },
1231
+ async writeSourceFrontier(input) {
1232
+ return {
1233
+ schema_version: "1",
1234
+ session_id: input.sessionId,
1235
+ round_id: input.roundId,
1236
+ created_at: isoNow(),
1237
+ exploration_synthesis_ref: input.explorationSynthesisRef,
1238
+ frontier_refs: [],
1239
+ no_next_frontier_rationale: input.explorationSynthesis.no_next_frontier_rationale ??
1240
+ "Mock frontier declares no next source refs.",
1241
+ directive_author: {
1242
+ owner: "mock",
1243
+ author_id: authorId,
1244
+ },
1245
+ };
1246
+ },
239
1247
  async writeSeedCandidate(input) {
240
1248
  const selections = input.sourceObservationDirective.selected_observations;
241
1249
  if (selections.length === 0) {
@@ -248,21 +1256,65 @@ export function createMockReconstructDirectiveAuthor() {
248
1256
  location: selection.location,
249
1257
  }));
250
1258
  const materialKinds = new Set(selections.map((selection) => selection.target_material_kind));
1259
+ const firstEvidence = evidenceRefs[0];
251
1260
  return {
252
1261
  schema_version: "1",
253
1262
  session_id: input.sessionId,
254
1263
  created_at: isoNow(),
255
1264
  purpose: {
256
1265
  claim_id: "purpose-1",
1266
+ name: "Bounded Ontology Seed Purpose",
257
1267
  statement: `Reconstruct a bounded ontology Seed for the declared purpose: ${input.intent}`,
258
1268
  evidence_refs: evidenceRefs,
259
1269
  },
260
- non_goals: [],
261
- entities: [],
262
- relations: [],
263
- actions: [],
264
- properties: [],
265
- rules: [],
1270
+ non_goals: [
1271
+ {
1272
+ claim_id: "non-goal-1",
1273
+ name: "Runtime Authorship Boundary",
1274
+ statement: "The runtime does not author ontology meaning; semantic expansion remains host-owned.",
1275
+ evidence_refs: [firstEvidence],
1276
+ },
1277
+ ],
1278
+ entities: [
1279
+ {
1280
+ claim_id: "entity-1",
1281
+ name: "Observed Source Evidence Set",
1282
+ statement: `The target material exposes ${selections.length} observed source unit(s) as ontology Seed evidence.`,
1283
+ evidence_refs: evidenceRefs,
1284
+ },
1285
+ ],
1286
+ relations: [
1287
+ {
1288
+ claim_id: "relation-1",
1289
+ name: "Observation Evidence Boundary",
1290
+ statement: "Selected source observations provide the evidence boundary for every Seed claim.",
1291
+ evidence_refs: evidenceRefs,
1292
+ },
1293
+ ],
1294
+ actions: [
1295
+ {
1296
+ claim_id: "action-1",
1297
+ name: "Competency Question Assessment",
1298
+ statement: "The reconstruct process can ask competency questions against confirmed Seed claims.",
1299
+ evidence_refs: [firstEvidence],
1300
+ },
1301
+ ],
1302
+ properties: [
1303
+ {
1304
+ claim_id: "property-1",
1305
+ name: "Target Material Kind",
1306
+ statement: `The target material kind is ${selections[0]?.target_material_kind ?? "unknown"} for at least one selected evidence unit.`,
1307
+ evidence_refs: [firstEvidence],
1308
+ },
1309
+ ],
1310
+ rules: [
1311
+ {
1312
+ claim_id: "rule-1",
1313
+ name: "Artifact Truth Rule",
1314
+ statement: "Any final output must cite artifact truth rather than becoming a second ontology authority.",
1315
+ evidence_refs: [firstEvidence],
1316
+ },
1317
+ ],
266
1318
  open_questions: [
267
1319
  ...(input.sourceObservationDirectiveValidation.validation_status === "valid"
268
1320
  ? []
@@ -275,8 +1327,32 @@ export function createMockReconstructDirectiveAuthor() {
275
1327
  ],
276
1328
  };
277
1329
  },
1330
+ async writeClaimRealizationMap(input) {
1331
+ const claims = allClaims(input.seedCandidate);
1332
+ return {
1333
+ schema_version: "1",
1334
+ session_id: input.sessionId,
1335
+ created_at: isoNow(),
1336
+ seed_candidate_ref: input.seedCandidateRef,
1337
+ claim_realizations: claims.map((claim, index) => {
1338
+ const stance = CLAIM_REALIZATION_STANCES[index % CLAIM_REALIZATION_STANCES.length] ?? "unknown";
1339
+ return {
1340
+ claim_id: claim.claim_id,
1341
+ stance,
1342
+ evidence_refs: claim.evidence_refs,
1343
+ rationale: stance === "observed_runtime_behavior"
1344
+ ? "Mock author treats this claim as directly supported by runtime observations."
1345
+ : `Mock author records ${stance} so downstream gates exercise non-happy-path evidence states.`,
1346
+ };
1347
+ }),
1348
+ directive_author: {
1349
+ owner: "mock",
1350
+ author_id: authorId,
1351
+ },
1352
+ };
1353
+ },
278
1354
  async writeCompetencyQuestions(input) {
279
- const confirmedClaims = new Set(input.seedConfirmation.confirmed_claim_ids);
1355
+ const confirmedClaims = new Set(input.seedConfirmationValidation.cq_eligible_claim_ids);
280
1356
  const questions = allClaims(input.seedCandidate)
281
1357
  .filter((claim) => confirmedClaims.has(claim.claim_id))
282
1358
  .map((claim, index) => ({
@@ -301,10 +1377,104 @@ export function createMockReconstructDirectiveAuthor() {
301
1377
  },
302
1378
  };
303
1379
  },
1380
+ async writeCompetencyQuestionAssessment(input) {
1381
+ const realizationByClaim = new Map(input.claimRealizationMap.claim_realizations.map((realization) => [
1382
+ realization.claim_id,
1383
+ realization,
1384
+ ]));
1385
+ return {
1386
+ schema_version: "1",
1387
+ session_id: input.sessionId,
1388
+ created_at: isoNow(),
1389
+ competency_questions_ref: input.competencyQuestionsRef,
1390
+ competency_questions_validation_ref: input.competencyQuestionsValidationRef,
1391
+ assessments: input.competencyQuestions.questions.map((question) => {
1392
+ const firstClaimId = question.linked_claim_ids[0] ?? null;
1393
+ const stance = firstClaimId
1394
+ ? realizationByClaim.get(firstClaimId)?.stance ?? "unknown"
1395
+ : "unknown";
1396
+ const answerStatus = stance === "observed_runtime_behavior" ||
1397
+ stance === "schema_or_contract_presence"
1398
+ ? "answered"
1399
+ : stance === "declared_design_intent"
1400
+ ? "partially_answered"
1401
+ : stance === "deferred_or_non_goal"
1402
+ ? "out_of_scope"
1403
+ : "needs_evidence";
1404
+ return {
1405
+ question_id: question.question_id,
1406
+ answer_status: answerStatus,
1407
+ linked_claim_ids: question.linked_claim_ids,
1408
+ evidence_refs: question.evidence_refs,
1409
+ rationale: `Mock assessment maps claim realization stance ${stance} to answer status ${answerStatus}.`,
1410
+ };
1411
+ }),
1412
+ directive_author: {
1413
+ owner: "mock",
1414
+ author_id: authorId,
1415
+ },
1416
+ };
1417
+ },
1418
+ async writeFailureClassification(input) {
1419
+ const failures = input.competencyQuestionAssessment.assessments
1420
+ .filter((assessment) => assessment.answer_status !== "answered")
1421
+ .map((assessment, index) => {
1422
+ const failureKind = assessment.answer_status === "out_of_scope"
1423
+ ? "deferred_scope"
1424
+ : assessment.answer_status === "partially_answered"
1425
+ ? "insufficient_evidence"
1426
+ : "unanswered_question";
1427
+ return {
1428
+ failure_id: `failure-${index + 1}`,
1429
+ failure_kind: failureKind,
1430
+ materiality: "material",
1431
+ question_id: assessment.question_id,
1432
+ claim_id: assessment.linked_claim_ids[0] ?? null,
1433
+ rationale: `Question ${assessment.question_id} is ${assessment.answer_status}, so the Seed is not fully safe to trust for that question.`,
1434
+ recommended_action: failureKind === "deferred_scope" ? "defer" : "collect_evidence",
1435
+ };
1436
+ });
1437
+ return {
1438
+ schema_version: "1",
1439
+ session_id: input.sessionId,
1440
+ created_at: isoNow(),
1441
+ competency_question_assessment_ref: input.competencyQuestionAssessmentRef,
1442
+ seed_confirmation_validation_ref: input.seedConfirmationValidation.seed_confirmation_ref,
1443
+ failures,
1444
+ directive_author: {
1445
+ owner: "mock",
1446
+ author_id: authorId,
1447
+ },
1448
+ };
1449
+ },
1450
+ async writeRevisionProposal(input) {
1451
+ return {
1452
+ schema_version: "1",
1453
+ session_id: input.sessionId,
1454
+ created_at: isoNow(),
1455
+ failure_classification_ref: input.failureClassificationRef,
1456
+ proposals: input.failureClassification.failures.map((failure, index) => ({
1457
+ proposal_id: `proposal-${index + 1}`,
1458
+ target_type: "failure",
1459
+ target_id: failure.failure_id,
1460
+ action: failure.recommended_action === "defer"
1461
+ ? "defer"
1462
+ : failure.recommended_action === "reject_claim"
1463
+ ? "reject"
1464
+ : "extend",
1465
+ rationale: `Address ${failure.failure_id} before treating the reconstructed Seed as complete.`,
1466
+ expected_effect: "Improve artifact-backed trust without making runtime author ontology meaning.",
1467
+ })),
1468
+ directive_author: {
1469
+ owner: "mock",
1470
+ author_id: authorId,
1471
+ },
1472
+ };
1473
+ },
304
1474
  async writeStopDecision(input) {
305
1475
  const shouldStop = input.metrics.validation_status.source_observation_directive === "valid" &&
306
1476
  input.metrics.validation_status.seed_candidate === "valid" &&
307
- input.metrics.validation_status.seed_confirmation === "accepted" &&
1477
+ input.metrics.validation_status.seed_confirmation_validation === "valid" &&
308
1478
  input.metrics.unresolved_question_count === 0;
309
1479
  return {
310
1480
  schema_version: "1",
@@ -326,10 +1496,20 @@ export function createMockReconstructDirectiveAuthor() {
326
1496
  };
327
1497
  },
328
1498
  async writeFinalOutput(input) {
329
- const confirmedClaims = allClaims(input.seedCandidate).filter((claim) => input.seedConfirmation.confirmed_claim_ids.includes(claim.claim_id));
1499
+ const confirmedClaims = allClaims(input.seedCandidate).filter((claim) => input.seedConfirmationValidation.accepted_claim_ids.includes(claim.claim_id));
330
1500
  const claimLines = confirmedClaims.length === 0
331
1501
  ? ["- No Seed claims were confirmed."]
332
- : confirmedClaims.map((claim) => `- ${claim.claim_id}: ${claim.statement}`);
1502
+ : confirmedClaims.map((claim) => `- ${claim.name} (${claim.claim_id}): ${claim.statement} (seed-candidate.yaml, seed-confirmation-validation.yaml)`);
1503
+ const realizationLines = input.claimRealizationMap.claim_realizations.map((realization) => `- ${realization.claim_id}: ${realization.stance} (claim-realization-map.yaml)`);
1504
+ const assessmentLines = input.competencyQuestionAssessment.assessments.length === 0
1505
+ ? ["- No competency question assessments recorded."]
1506
+ : input.competencyQuestionAssessment.assessments.map((assessment) => `- ${assessment.question_id}: ${assessment.answer_status} for ${assessment.linked_claim_ids.join(", ")} (competency-question-assessment.yaml)`);
1507
+ const failureLines = input.failureClassification.failures.length === 0
1508
+ ? ["- No material failures recorded."]
1509
+ : input.failureClassification.failures.map((failure) => `- ${failure.failure_id}: ${failure.failure_kind} on ${failure.question_id ?? failure.claim_id ?? "run"} (${failure.materiality}) (failure-classification.yaml)`);
1510
+ const revisionLines = input.revisionProposal.proposals.length === 0
1511
+ ? ["- No revision proposals recorded."]
1512
+ : input.revisionProposal.proposals.map((proposal) => `- ${proposal.proposal_id}: ${proposal.action} ${proposal.target_type} ${proposal.target_id} (revision-proposal.yaml)`);
333
1513
  const unresolvedQuestions = [
334
1514
  ...input.seedCandidate.open_questions,
335
1515
  ...input.competencyQuestions.open_questions,
@@ -361,8 +1541,27 @@ export function createMockReconstructDirectiveAuthor() {
361
1541
  `- Semantic claims: ${input.metrics.semantic_claim_count}`,
362
1542
  `- Evidence refs: ${input.metrics.evidence_ref_count}`,
363
1543
  `- Competency questions: ${input.metrics.competency_question_count}`,
1544
+ `- Competency question assessments: ${input.metrics.competency_question_assessment_count}`,
1545
+ `- Material failures: ${input.failureClassificationValidation.material_failure_count}`,
1546
+ `- Revision proposals: ${input.revisionProposalValidation.proposal_count}`,
364
1547
  `- Pass rate: ${input.metrics.pass_rate}`,
365
1548
  "",
1549
+ "## Claim Realization Summary",
1550
+ "",
1551
+ ...realizationLines,
1552
+ "",
1553
+ "## Competency Question Assessment",
1554
+ "",
1555
+ ...assessmentLines,
1556
+ "",
1557
+ "## Failure Classifications",
1558
+ "",
1559
+ ...failureLines,
1560
+ "",
1561
+ "## Revision Proposals",
1562
+ "",
1563
+ ...revisionLines,
1564
+ "",
366
1565
  "## Unresolved Material Questions",
367
1566
  "",
368
1567
  ...unresolvedLines,
@@ -377,6 +1576,12 @@ export function createMockReconstructDirectiveAuthor() {
377
1576
  "",
378
1577
  "## Artifact Truth",
379
1578
  "",
1579
+ `- Seed candidate: ${input.artifactRefs.seed_candidate}`,
1580
+ `- Claim realization map: ${input.artifactRefs.claim_realization_map}`,
1581
+ `- Seed confirmation validation: ${input.artifactRefs.seed_confirmation_validation}`,
1582
+ `- Competency question assessment: ${input.artifactRefs.competency_question_assessment}`,
1583
+ `- Failure classification: ${input.artifactRefs.failure_classification}`,
1584
+ `- Revision proposal: ${input.artifactRefs.revision_proposal}`,
380
1585
  `- Reconstruct record: ${input.reconstructRecordPath}`,
381
1586
  `- Reconstruct run manifest: ${input.reconstructRunManifestPath}`,
382
1587
  `- Record stage at final output authoring: ${input.record.record_stage}`,
@@ -387,24 +1592,32 @@ export function createMockReconstructDirectiveAuthor() {
387
1592
  };
388
1593
  }
389
1594
  export function createAutoAcceptReconstructConfirmationProvider() {
390
- const providerId = "mock-auto-accept-confirmation-provider";
1595
+ const providerId = "mock-mixed-confirmation-provider";
391
1596
  return {
392
1597
  providerId,
393
1598
  owner: "mock",
394
1599
  async confirmSeedCandidate(input) {
395
1600
  const claims = allClaims(input.seedCandidate);
396
1601
  const canAccept = input.seedCandidateValidation.validation_status === "valid";
1602
+ const acceptedClaims = canAccept ? claims.slice(0, 3) : [];
1603
+ const partialClaims = canAccept ? claims.slice(3, 4) : [];
1604
+ const deferredClaims = canAccept ? claims.slice(4, 5) : [];
1605
+ const rejectedClaims = canAccept ? claims.slice(5) : claims;
397
1606
  return {
398
1607
  schema_version: "1",
399
1608
  session_id: input.sessionId,
400
1609
  created_at: isoNow(),
401
1610
  seed_candidate_ref: input.seedCandidateRef,
402
1611
  seed_candidate_validation_ref: input.seedCandidateValidationRef,
403
- confirmation_status: canAccept ? "accepted" : "rejected",
404
- confirmed_claim_ids: canAccept ? claims.map((claim) => claim.claim_id) : [],
405
- rejected_claim_ids: canAccept ? [] : claims.map((claim) => claim.claim_id),
1612
+ confirmation_status: canAccept ? "partial" : "rejected",
1613
+ confirmed_claim_ids: acceptedClaims.map((claim) => claim.claim_id),
1614
+ rejected_claim_ids: rejectedClaims.map((claim) => claim.claim_id),
1615
+ partial_claim_ids: partialClaims.map((claim) => claim.claim_id),
1616
+ deferred_claim_ids: deferredClaims.map((claim) => claim.claim_id),
406
1617
  notes: canAccept
407
- ? []
1618
+ ? [
1619
+ "Mock confirmation intentionally emits mixed claim states to exercise downstream reconstruct gates.",
1620
+ ]
408
1621
  : ["Seed candidate validation failed; confirmation rejected by provider."],
409
1622
  confirmation_provider: {
410
1623
  owner: "mock",
@@ -414,18 +1627,151 @@ export function createAutoAcceptReconstructConfirmationProvider() {
414
1627
  },
415
1628
  };
416
1629
  }
1630
+ export function createDirectCallReconstructConfirmationProvider(args = {}) {
1631
+ const providerId = "direct-call-reconstruct-confirmation-provider";
1632
+ const llmConfig = args.llmConfig ?? {};
1633
+ const llmCall = args.llmCall ?? callLlm;
1634
+ return {
1635
+ providerId,
1636
+ owner: "host_or_user",
1637
+ async confirmSeedCandidate(input) {
1638
+ const claimSummaries = summarizeSeedClaimsForConfirmation(input.seedCandidate);
1639
+ const result = await llmCall([
1640
+ "You are mediating reconstruct Seed confirmation for a non-interactive host.",
1641
+ "Return only valid JSON. Do not wrap in Markdown.",
1642
+ "Classify every Seed claim summary into confirmed, rejected, partial, or deferred for the declared purpose.",
1643
+ "Use the claim id, claim kind, short statement, validation status, and evidence observation ids. Do not invent new claim ids.",
1644
+ "Do not re-author Seed content. This step only assigns confirmation state.",
1645
+ "JSON shape: {\"confirmation_status\":\"accepted|rejected|partial|deferred\",\"confirmed_claim_ids\":[\"...\"],\"rejected_claim_ids\":[\"...\"],\"partial_claim_ids\":[\"...\"],\"deferred_claim_ids\":[\"...\"],\"notes\":[\"...\"]}",
1646
+ ].join("\n"), JSON.stringify({
1647
+ seed_candidate_ref: input.seedCandidateRef,
1648
+ seed_candidate_validation_status: input.seedCandidateValidation.validation_status,
1649
+ seed_candidate_validation_results: input.seedCandidateValidation.validation_results,
1650
+ seed_candidate_validation_violation_count: input.seedCandidateValidation.violations.length,
1651
+ claim_summaries: claimSummaries,
1652
+ }, null, 2), { ...llmConfig, max_tokens: 2400 });
1653
+ const raw = parseLlmJsonObject(result.text, "SeedConfirmation");
1654
+ const confirmationStatus = stringValue(raw.confirmation_status, "confirmation_status");
1655
+ if (!["accepted", "rejected", "partial", "deferred"].includes(confirmationStatus)) {
1656
+ throw new Error(`SeedConfirmation confirmation_status is invalid: ${confirmationStatus}`);
1657
+ }
1658
+ return {
1659
+ schema_version: "1",
1660
+ session_id: input.sessionId,
1661
+ created_at: isoNow(),
1662
+ seed_candidate_ref: input.seedCandidateRef,
1663
+ seed_candidate_validation_ref: input.seedCandidateValidationRef,
1664
+ confirmation_status: confirmationStatus,
1665
+ confirmed_claim_ids: stringArray(raw.confirmed_claim_ids, "confirmed_claim_ids"),
1666
+ rejected_claim_ids: stringArray(raw.rejected_claim_ids, "rejected_claim_ids"),
1667
+ partial_claim_ids: stringArray(raw.partial_claim_ids, "partial_claim_ids"),
1668
+ deferred_claim_ids: stringArray(raw.deferred_claim_ids, "deferred_claim_ids"),
1669
+ notes: stringArray(raw.notes, "notes"),
1670
+ confirmation_provider: {
1671
+ owner: "host_or_user",
1672
+ provider_id: providerId,
1673
+ },
1674
+ };
1675
+ },
1676
+ };
1677
+ }
1678
+ async function readLensPrompt(args) {
1679
+ const ontoRoot = path.resolve(args.profilesRoot, "..", "..", "..");
1680
+ return fs.readFile(path.join(ontoRoot, "roles", `${args.lensId}.md`), "utf8");
1681
+ }
1682
+ function validateSourceFrontier(args) {
1683
+ const inventoryRefs = new Set(args.sourceInventory.inventory_units.map((unit) => path.resolve(unit.ref)));
1684
+ const observedRefs = new Set(args.sourceObservations.observations.map((observation) => path.resolve(observation.source_ref)));
1685
+ const accepted = [];
1686
+ const rejected = [];
1687
+ const seen = new Set();
1688
+ for (const frontier of args.sourceFrontier.frontier_refs) {
1689
+ const resolved = path.resolve(frontier.source_ref);
1690
+ if (seen.has(resolved)) {
1691
+ rejected.push({
1692
+ frontier_ref_id: frontier.frontier_ref_id,
1693
+ source_ref: frontier.source_ref,
1694
+ reason: "duplicate_frontier_ref",
1695
+ });
1696
+ continue;
1697
+ }
1698
+ seen.add(resolved);
1699
+ if (observedRefs.has(resolved)) {
1700
+ rejected.push({
1701
+ frontier_ref_id: frontier.frontier_ref_id,
1702
+ source_ref: frontier.source_ref,
1703
+ reason: "already_observed",
1704
+ });
1705
+ continue;
1706
+ }
1707
+ if (!inventoryRefs.has(resolved)) {
1708
+ rejected.push({
1709
+ frontier_ref_id: frontier.frontier_ref_id,
1710
+ source_ref: frontier.source_ref,
1711
+ reason: "not_in_source_inventory",
1712
+ });
1713
+ continue;
1714
+ }
1715
+ accepted.push(frontier.frontier_ref_id);
1716
+ }
1717
+ const noNextFrontierAccepted = args.sourceFrontier.frontier_refs.length === 0 &&
1718
+ typeof args.sourceFrontier.no_next_frontier_rationale === "string" &&
1719
+ args.sourceFrontier.no_next_frontier_rationale.length > 0;
1720
+ const valid = rejected.length === 0 &&
1721
+ (accepted.length > 0 || noNextFrontierAccepted);
1722
+ return {
1723
+ schema_version: "1",
1724
+ session_id: args.sessionId,
1725
+ round_id: args.roundId,
1726
+ created_at: isoNow(),
1727
+ source_frontier_ref: args.sourceFrontierRef,
1728
+ source_inventory_ref: args.sourceInventoryRef,
1729
+ source_observations_ref: args.sourceObservationsRef,
1730
+ validation_status: valid ? "valid" : "invalid",
1731
+ accepted_frontier_ref_ids: accepted,
1732
+ rejected_frontier_refs: rejected,
1733
+ no_next_frontier_accepted: noNextFrontierAccepted,
1734
+ validation_results: [
1735
+ ...(valid ? ["source_frontier_boundary_valid"] : []),
1736
+ ...(noNextFrontierAccepted ? ["no_next_frontier_rationale_present"] : []),
1737
+ ],
1738
+ };
1739
+ }
1740
+ function appendFinalOutputProvenanceFooter(finalOutputText, requiredFragments) {
1741
+ const missing = requiredFragments.filter((fragment) => !finalOutputText.includes(fragment));
1742
+ if (missing.length === 0)
1743
+ return finalOutputText;
1744
+ return [
1745
+ finalOutputText.trimEnd(),
1746
+ "",
1747
+ "## Runtime Artifact Truth Footer",
1748
+ "",
1749
+ ...missing.map((fragment) => `- ${fragment}`),
1750
+ "",
1751
+ ].join("\n");
1752
+ }
417
1753
  export async function runReconstruct(params) {
418
1754
  const projectRoot = path.resolve(params.projectRoot);
419
1755
  const sessionRoot = path.resolve(params.sessionRoot);
420
1756
  const sessionId = path.basename(sessionRoot);
421
1757
  const targetRefs = params.targetRefs.map((targetRef) => path.resolve(targetRef));
422
1758
  const { directiveAuthor, confirmationProvider } = params;
423
- if (params.semanticAuthorRealization !== "mock") {
1759
+ if (params.semanticAuthorRealization !== "mock" &&
1760
+ params.semanticAuthorRealization !== "direct_call") {
424
1761
  throw new Error(`Unsupported reconstruct semanticAuthorRealization: ${params.semanticAuthorRealization}`);
425
1762
  }
426
- if (params.confirmationProviderRealization !== "mock") {
1763
+ if (params.confirmationProviderRealization !== "mock" &&
1764
+ params.confirmationProviderRealization !== "direct_call") {
427
1765
  throw new Error(`Unsupported reconstruct confirmationProviderRealization: ${params.confirmationProviderRealization}`);
428
1766
  }
1767
+ if (params.semanticAuthorRealization === "direct_call" &&
1768
+ directiveAuthor.owner !== "host_llm") {
1769
+ throw new Error("direct_call semantic author realization requires a host_llm directive author.");
1770
+ }
1771
+ if (params.confirmationProviderRealization === "direct_call" &&
1772
+ confirmationProvider.owner !== "host_or_user") {
1773
+ throw new Error("direct_call confirmation provider realization requires a host_or_user provider.");
1774
+ }
429
1775
  const preparationRefs = await materializeReconstructPreparationArtifacts({
430
1776
  sessionRoot,
431
1777
  targetRefs,
@@ -435,6 +1781,7 @@ export async function runReconstruct(params) {
435
1781
  });
436
1782
  const targetMaterialProfile = await readYamlDocument(preparationRefs.target_material_profile);
437
1783
  const sourceObservations = await readYamlDocument(preparationRefs.source_observations);
1784
+ const sourceInventory = await readYamlDocument(preparationRefs.source_inventory);
438
1785
  const sourceObservationDirectivePath = path.join(sessionRoot, "source-observation-directive.yaml");
439
1786
  const sourceObservationDirective = await directiveAuthor.writeSourceObservationDirective({
440
1787
  sessionId,
@@ -449,6 +1796,78 @@ export async function runReconstruct(params) {
449
1796
  sourceObservationsPath: preparationRefs.source_observations,
450
1797
  outputPath: sourceObservationDirectiveValidationPath,
451
1798
  });
1799
+ const roundId = "round-1";
1800
+ const roundRoot = path.join(sessionRoot, "rounds", roundId);
1801
+ const roundObservationDirectivePath = path.join(roundRoot, "source-observation-directive.yaml");
1802
+ const roundObservationDirectiveValidationPath = path.join(roundRoot, "source-observation-directive-validation.yaml");
1803
+ await writeYamlDocument(roundObservationDirectivePath, sourceObservationDirective);
1804
+ await writeYamlDocument(roundObservationDirectiveValidationPath, sourceObservationDirectiveValidation);
1805
+ const lensJudgmentRoot = path.join(roundRoot, "lens-judgments");
1806
+ const lensIds = loadCoreLensRegistry().full_review_lens_ids;
1807
+ const lensJudgments = [];
1808
+ const lensJudgmentRefs = [];
1809
+ for (const lensId of lensIds) {
1810
+ const lensPrompt = await readLensPrompt({
1811
+ profilesRoot: path.resolve(params.profilesRoot),
1812
+ lensId,
1813
+ });
1814
+ const lensJudgment = await directiveAuthor.writeLensJudgment({
1815
+ sessionId,
1816
+ intent: params.intent,
1817
+ roundId,
1818
+ lensId,
1819
+ lensPrompt,
1820
+ sourceObservations,
1821
+ sourceObservationDirective,
1822
+ sourceObservationDirectiveRef: roundObservationDirectivePath,
1823
+ });
1824
+ const lensJudgmentPath = path.join(lensJudgmentRoot, `${lensId}.yaml`);
1825
+ await writeYamlDocument(lensJudgmentPath, lensJudgment);
1826
+ lensJudgments.push(lensJudgment);
1827
+ lensJudgmentRefs.push({
1828
+ lens_id: lensId,
1829
+ artifact_ref: lensJudgmentPath,
1830
+ });
1831
+ }
1832
+ const lensJudgmentIndexPath = path.join(roundRoot, "lens-judgment-index.yaml");
1833
+ const lensJudgmentIndex = {
1834
+ schema_version: "1",
1835
+ session_id: sessionId,
1836
+ round_id: roundId,
1837
+ created_at: isoNow(),
1838
+ lens_judgment_refs: lensJudgmentRefs,
1839
+ };
1840
+ await writeYamlDocument(lensJudgmentIndexPath, lensJudgmentIndex);
1841
+ const explorationSynthesisPath = path.join(roundRoot, "exploration-synthesis.yaml");
1842
+ const explorationSynthesis = await directiveAuthor.writeExplorationSynthesis({
1843
+ sessionId,
1844
+ intent: params.intent,
1845
+ roundId,
1846
+ lensJudgments,
1847
+ lensJudgmentIndexRef: lensJudgmentIndexPath,
1848
+ });
1849
+ await writeYamlDocument(explorationSynthesisPath, explorationSynthesis);
1850
+ const sourceFrontierPath = path.join(roundRoot, "source-frontier.yaml");
1851
+ const sourceFrontier = await directiveAuthor.writeSourceFrontier({
1852
+ sessionId,
1853
+ intent: params.intent,
1854
+ roundId,
1855
+ explorationSynthesis,
1856
+ explorationSynthesisRef: explorationSynthesisPath,
1857
+ });
1858
+ await writeYamlDocument(sourceFrontierPath, sourceFrontier);
1859
+ const sourceFrontierValidationPath = path.join(roundRoot, "source-frontier-validation.yaml");
1860
+ const sourceFrontierValidation = validateSourceFrontier({
1861
+ sessionId,
1862
+ roundId,
1863
+ sourceFrontier,
1864
+ sourceFrontierRef: sourceFrontierPath,
1865
+ sourceInventory,
1866
+ sourceInventoryRef: preparationRefs.source_inventory,
1867
+ sourceObservations,
1868
+ sourceObservationsRef: preparationRefs.source_observations,
1869
+ });
1870
+ await writeYamlDocument(sourceFrontierValidationPath, sourceFrontierValidation);
452
1871
  const seedCandidatePath = path.join(sessionRoot, "seed-candidate.yaml");
453
1872
  const seedCandidate = await directiveAuthor.writeSeedCandidate({
454
1873
  sessionId,
@@ -456,6 +1875,9 @@ export async function runReconstruct(params) {
456
1875
  sourceObservations,
457
1876
  sourceObservationDirective,
458
1877
  sourceObservationDirectiveValidation,
1878
+ lensJudgmentIndex,
1879
+ explorationSynthesis,
1880
+ sourceFrontierValidation,
459
1881
  });
460
1882
  await writeYamlDocument(seedCandidatePath, seedCandidate);
461
1883
  const seedCandidateValidationPath = path.join(sessionRoot, "seed-candidate-validation.yaml");
@@ -466,6 +1888,21 @@ export async function runReconstruct(params) {
466
1888
  sourceObservationDirectivePath,
467
1889
  sourceObservationDirectiveValidationPath,
468
1890
  });
1891
+ const claimRealizationMapPath = path.join(sessionRoot, "claim-realization-map.yaml");
1892
+ const claimRealizationMap = await directiveAuthor.writeClaimRealizationMap({
1893
+ sessionId,
1894
+ seedCandidate,
1895
+ seedCandidateRef: seedCandidatePath,
1896
+ sourceObservations,
1897
+ });
1898
+ await writeYamlDocument(claimRealizationMapPath, claimRealizationMap);
1899
+ const claimRealizationMapValidationPath = path.join(sessionRoot, "claim-realization-map-validation.yaml");
1900
+ const claimRealizationMapValidation = await writeClaimRealizationMapValidationArtifact({
1901
+ claimRealizationMapPath,
1902
+ seedCandidatePath,
1903
+ sourceObservationsPath: preparationRefs.source_observations,
1904
+ outputPath: claimRealizationMapValidationPath,
1905
+ });
469
1906
  const seedConfirmationPath = path.join(sessionRoot, "seed-confirmation.yaml");
470
1907
  const seedConfirmation = await confirmationProvider.confirmSeedCandidate({
471
1908
  sessionId,
@@ -475,22 +1912,91 @@ export async function runReconstruct(params) {
475
1912
  seedCandidateValidationRef: seedCandidateValidationPath,
476
1913
  });
477
1914
  await writeYamlDocument(seedConfirmationPath, seedConfirmation);
1915
+ const seedConfirmationValidationPath = path.join(sessionRoot, "seed-confirmation-validation.yaml");
1916
+ const seedConfirmationValidation = await writeSeedConfirmationValidationArtifact({
1917
+ seedConfirmationPath,
1918
+ seedCandidatePath,
1919
+ seedCandidateValidationPath,
1920
+ outputPath: seedConfirmationValidationPath,
1921
+ });
478
1922
  const competencyQuestionsPath = path.join(sessionRoot, "competency-questions.yaml");
479
1923
  const competencyQuestions = await directiveAuthor.writeCompetencyQuestions({
480
1924
  sessionId,
481
1925
  seedCandidate,
482
1926
  seedConfirmation,
483
1927
  seedConfirmationRef: seedConfirmationPath,
1928
+ seedConfirmationValidation,
1929
+ seedConfirmationValidationRef: seedConfirmationValidationPath,
1930
+ claimRealizationMap,
484
1931
  });
485
1932
  await writeYamlDocument(competencyQuestionsPath, competencyQuestions);
1933
+ const competencyQuestionsValidationPath = path.join(sessionRoot, "competency-questions-validation.yaml");
1934
+ const competencyQuestionsValidation = await writeCompetencyQuestionsValidationArtifact({
1935
+ competencyQuestionsPath,
1936
+ seedConfirmationValidationPath,
1937
+ sourceObservationsPath: preparationRefs.source_observations,
1938
+ outputPath: competencyQuestionsValidationPath,
1939
+ });
1940
+ const competencyQuestionAssessmentPath = path.join(sessionRoot, "competency-question-assessment.yaml");
1941
+ const competencyQuestionAssessment = await directiveAuthor.writeCompetencyQuestionAssessment({
1942
+ sessionId,
1943
+ competencyQuestions,
1944
+ competencyQuestionsRef: competencyQuestionsPath,
1945
+ competencyQuestionsValidation,
1946
+ competencyQuestionsValidationRef: competencyQuestionsValidationPath,
1947
+ claimRealizationMap,
1948
+ });
1949
+ await writeYamlDocument(competencyQuestionAssessmentPath, competencyQuestionAssessment);
1950
+ const competencyQuestionAssessmentValidationPath = path.join(sessionRoot, "competency-question-assessment-validation.yaml");
1951
+ const competencyQuestionAssessmentValidation = await writeCompetencyQuestionAssessmentValidationArtifact({
1952
+ competencyQuestionAssessmentPath,
1953
+ competencyQuestionsPath,
1954
+ outputPath: competencyQuestionAssessmentValidationPath,
1955
+ });
1956
+ const failureClassificationPath = path.join(sessionRoot, "failure-classification.yaml");
1957
+ const failureClassification = await directiveAuthor.writeFailureClassification({
1958
+ sessionId,
1959
+ competencyQuestionAssessment,
1960
+ competencyQuestionAssessmentRef: competencyQuestionAssessmentPath,
1961
+ competencyQuestionAssessmentValidation,
1962
+ seedConfirmationValidation,
1963
+ });
1964
+ await writeYamlDocument(failureClassificationPath, failureClassification);
1965
+ const failureClassificationValidationPath = path.join(sessionRoot, "failure-classification-validation.yaml");
1966
+ const failureClassificationValidation = await writeFailureClassificationValidationArtifact({
1967
+ failureClassificationPath,
1968
+ competencyQuestionAssessmentPath,
1969
+ seedConfirmationValidationPath,
1970
+ outputPath: failureClassificationValidationPath,
1971
+ });
1972
+ const revisionProposalPath = path.join(sessionRoot, "revision-proposal.yaml");
1973
+ const revisionProposal = await directiveAuthor.writeRevisionProposal({
1974
+ sessionId,
1975
+ failureClassification,
1976
+ failureClassificationRef: failureClassificationPath,
1977
+ failureClassificationValidation,
1978
+ });
1979
+ await writeYamlDocument(revisionProposalPath, revisionProposal);
1980
+ const revisionProposalValidationPath = path.join(sessionRoot, "revision-proposal-validation.yaml");
1981
+ const revisionProposalValidation = await writeRevisionProposalValidationArtifact({
1982
+ revisionProposalPath,
1983
+ failureClassificationPath,
1984
+ outputPath: revisionProposalValidationPath,
1985
+ });
486
1986
  const metricsPath = path.join(sessionRoot, "reconstruct-metrics.yaml");
487
1987
  const metrics = calculateMetrics({
488
1988
  sessionId,
489
1989
  sourceObservations,
490
1990
  sourceObservationDirectiveValidation,
491
1991
  seedCandidateValidation,
1992
+ claimRealizationMapValidation,
492
1993
  seedConfirmation,
1994
+ seedConfirmationValidation,
493
1995
  competencyQuestions,
1996
+ competencyQuestionsValidation,
1997
+ competencyQuestionAssessmentValidation,
1998
+ failureClassificationValidation,
1999
+ revisionProposalValidation,
494
2000
  });
495
2001
  await writeYamlDocument(metricsPath, metrics);
496
2002
  const stopDecisionPath = path.join(sessionRoot, "stop-decision.yaml");
@@ -499,6 +2005,8 @@ export async function runReconstruct(params) {
499
2005
  intent: params.intent,
500
2006
  metrics,
501
2007
  metricsRef: metricsPath,
2008
+ failureClassification,
2009
+ revisionProposal,
502
2010
  });
503
2011
  await writeYamlDocument(stopDecisionPath, stopDecision);
504
2012
  const finalOutputPath = path.join(sessionRoot, "final-output.md");
@@ -508,13 +2016,28 @@ export async function runReconstruct(params) {
508
2016
  refs: {
509
2017
  target_material_profile: preparationRefs.target_material_profile,
510
2018
  source_inventory: preparationRefs.source_inventory,
2019
+ initial_source_frontier: preparationRefs.initial_source_frontier,
511
2020
  source_observations: preparationRefs.source_observations,
512
2021
  source_observation_directive: sourceObservationDirectivePath,
513
2022
  source_observation_directive_validation: sourceObservationDirectiveValidationPath,
2023
+ lens_judgment_index: lensJudgmentIndexPath,
2024
+ exploration_synthesis: explorationSynthesisPath,
2025
+ source_frontier: sourceFrontierPath,
2026
+ source_frontier_validation: sourceFrontierValidationPath,
514
2027
  seed_candidate: seedCandidatePath,
515
2028
  seed_candidate_validation: seedCandidateValidationPath,
2029
+ claim_realization_map: claimRealizationMapPath,
2030
+ claim_realization_map_validation: claimRealizationMapValidationPath,
516
2031
  seed_confirmation: seedConfirmationPath,
2032
+ seed_confirmation_validation: seedConfirmationValidationPath,
517
2033
  competency_questions: competencyQuestionsPath,
2034
+ competency_questions_validation: competencyQuestionsValidationPath,
2035
+ competency_question_assessment: competencyQuestionAssessmentPath,
2036
+ competency_question_assessment_validation: competencyQuestionAssessmentValidationPath,
2037
+ failure_classification: failureClassificationPath,
2038
+ failure_classification_validation: failureClassificationValidationPath,
2039
+ revision_proposal: revisionProposalPath,
2040
+ revision_proposal_validation: revisionProposalValidationPath,
518
2041
  reconstruct_metrics: metricsPath,
519
2042
  stop_decision: stopDecisionPath,
520
2043
  final_output: finalOutputPath,
@@ -525,6 +2048,8 @@ export async function runReconstruct(params) {
525
2048
  sessionId,
526
2049
  targetRefs,
527
2050
  intent: params.intent,
2051
+ semanticAuthorRealization: params.semanticAuthorRealization,
2052
+ confirmationProviderRealization: params.confirmationProviderRealization,
528
2053
  directiveAuthor,
529
2054
  confirmationProvider,
530
2055
  artifactRefs,
@@ -536,13 +2061,23 @@ export async function runReconstruct(params) {
536
2061
  artifactRefs,
537
2062
  outputPath: recordPath,
538
2063
  });
539
- const finalOutputText = await directiveAuthor.writeFinalOutput({
2064
+ const authoredFinalOutputText = await directiveAuthor.writeFinalOutput({
540
2065
  sessionId,
541
2066
  intent: params.intent,
542
2067
  targetMaterialProfile,
543
2068
  seedCandidate,
2069
+ claimRealizationMap,
2070
+ claimRealizationMapValidation,
544
2071
  seedConfirmation,
2072
+ seedConfirmationValidation,
545
2073
  competencyQuestions,
2074
+ competencyQuestionsValidation,
2075
+ competencyQuestionAssessment,
2076
+ competencyQuestionAssessmentValidation,
2077
+ failureClassification,
2078
+ failureClassificationValidation,
2079
+ revisionProposal,
2080
+ revisionProposalValidation,
546
2081
  metrics,
547
2082
  stopDecision,
548
2083
  sourceObservations,
@@ -552,6 +2087,27 @@ export async function runReconstruct(params) {
552
2087
  reconstructRunManifest,
553
2088
  record: interimRecord,
554
2089
  });
2090
+ const requiredFinalOutputFragments = [
2091
+ recordPath,
2092
+ manifestPath,
2093
+ seedCandidatePath,
2094
+ claimRealizationMapPath,
2095
+ seedConfirmationValidationPath,
2096
+ competencyQuestionAssessmentPath,
2097
+ failureClassificationPath,
2098
+ revisionProposalPath,
2099
+ ...seedConfirmationValidation.accepted_claim_ids,
2100
+ ...failureClassification.failures.map((failure) => failure.failure_id),
2101
+ ...revisionProposal.proposals.map((proposal) => proposal.proposal_id),
2102
+ ];
2103
+ const finalOutputText = appendFinalOutputProvenanceFooter(authoredFinalOutputText, requiredFinalOutputFragments);
2104
+ const finalOutputViolations = validateFinalOutputProvenance({
2105
+ finalOutputText,
2106
+ requiredFragments: requiredFinalOutputFragments,
2107
+ });
2108
+ if (finalOutputViolations.length > 0) {
2109
+ throw new Error(`final-output.md failed provenance validation: ${finalOutputViolations.map((item) => item.message).join("; ")}`);
2110
+ }
555
2111
  await fs.writeFile(finalOutputPath, finalOutputText, "utf8");
556
2112
  const finalRecord = await assembleReconstructRecord({
557
2113
  sessionRoot,