onto-mcp 0.4.12 → 0.4.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (128) hide show
  1. package/.onto/authority/core-lexicon.yaml +1 -1
  2. package/.onto/authority/supported-models.yaml +137 -0
  3. package/.onto/domains/accounting-kr/competency_qs.md +153 -153
  4. package/.onto/processes/reconstruct/obligation-coverage-ledger.yaml +842 -0
  5. package/.onto/processes/reconstruct/obligation-coverage-recorded.yaml +225 -0
  6. package/.onto/processes/reconstruct/ontology-seeding-and-maturation-design.md +93 -54
  7. package/.onto/processes/reconstruct/reconstruct-contract-registry.yaml +169 -8
  8. package/.onto/processes/reconstruct/reconstruct-execution-ux-contract.md +72 -1
  9. package/.onto/processes/reconstruct/source-profiles/spreadsheet.md +35 -0
  10. package/.onto/processes/review/productized-live-path.md +9 -5
  11. package/.onto/processes/review/prompt-execution-runner-contract.md +32 -8
  12. package/.onto/processes/review/review-target-profile-contract.md +47 -4
  13. package/.onto/processes/shared/pipeline-execution-ledger-contract.md +11 -0
  14. package/.onto/processes/shared/target-material-kind-contract.md +1 -1
  15. package/README.md +64 -1
  16. package/dist/cli.js +11 -1
  17. package/dist/core-api/reconstruct-api.js +575 -24
  18. package/dist/core-api/review-api.js +70 -5
  19. package/dist/core-api/review-progress.js +7 -0
  20. package/dist/core-api/runtime-observation.js +7 -0
  21. package/dist/core-runtime/artifact-io.js +75 -3
  22. package/dist/core-runtime/cli/claude-code-review-unit-executor.js +19 -8
  23. package/dist/core-runtime/cli/materialize-review-prompt-packets.js +68 -14
  24. package/dist/core-runtime/cli/prepare-review-session.js +34 -1
  25. package/dist/core-runtime/cli/render-review-final-output.js +2 -1
  26. package/dist/core-runtime/cli/run-review-prompt-execution.js +758 -48
  27. package/dist/core-runtime/cli/run-seats.js +18 -0
  28. package/dist/core-runtime/cli/runtime-submit-context.js +10 -2
  29. package/dist/core-runtime/cli/unit-resubmit.js +269 -0
  30. package/dist/core-runtime/cli/worker-structured-output.js +80 -2
  31. package/dist/core-runtime/discovery/host-detection.js +71 -22
  32. package/dist/core-runtime/discovery/review-cert-assemble.js +208 -0
  33. package/dist/core-runtime/discovery/review-cert-record.js +442 -0
  34. package/dist/core-runtime/discovery/review-execution-units.js +22 -0
  35. package/dist/core-runtime/discovery/seat-inventory.js +167 -0
  36. package/dist/core-runtime/discovery/settings-chain.js +311 -52
  37. package/dist/core-runtime/discovery/supported-models.js +282 -29
  38. package/dist/core-runtime/discovery/synthesize-cert-assemble.js +197 -0
  39. package/dist/core-runtime/discovery/synthesize-cert-capsule.js +399 -0
  40. package/dist/core-runtime/discovery/synthesize-cert-judge.js +340 -0
  41. package/dist/core-runtime/discovery/synthesize-cert-loop.js +234 -0
  42. package/dist/core-runtime/discovery/synthesize-cert-mutation.js +117 -0
  43. package/dist/core-runtime/discovery/synthesize-cert-packet.js +182 -0
  44. package/dist/core-runtime/discovery/synthesize-cert-record.js +831 -0
  45. package/dist/core-runtime/discovery/synthesize-cert-sampler.js +336 -0
  46. package/dist/core-runtime/effort-calibration-ingest.js +135 -0
  47. package/dist/core-runtime/llm/dispatch-breaker.js +412 -0
  48. package/dist/core-runtime/llm/dispatch-fallback-adapter-capabilities.js +33 -0
  49. package/dist/core-runtime/llm/llm-caller.js +188 -47
  50. package/dist/core-runtime/llm/mock-llm-realization.js +35 -4
  51. package/dist/core-runtime/llm/model-switcher.js +1 -1
  52. package/dist/core-runtime/llm/openai-responses-incomplete-error.js +46 -0
  53. package/dist/core-runtime/llm/sealed-dispatch-capability.js +359 -0
  54. package/dist/core-runtime/llm/structured-dispatch-error.js +15 -0
  55. package/dist/core-runtime/onboard/claude-profile-scan.js +65 -0
  56. package/dist/core-runtime/onboard/cli-host.js +5 -2
  57. package/dist/core-runtime/onboard/configure-provider.js +33 -1
  58. package/dist/core-runtime/onboard/host-target.js +28 -3
  59. package/dist/core-runtime/onboard/register.js +54 -12
  60. package/dist/core-runtime/pipeline-execution-ledger.js +10 -0
  61. package/dist/core-runtime/reconstruct/artifact-types.js +35 -0
  62. package/dist/core-runtime/reconstruct/claim-projection-validation.js +17 -0
  63. package/dist/core-runtime/reconstruct/competency-projection-contract.js +63 -0
  64. package/dist/core-runtime/reconstruct/comprehension-artifact.js +321 -0
  65. package/dist/core-runtime/reconstruct/comprehension-reduce.js +367 -0
  66. package/dist/core-runtime/reconstruct/comprehension-semantic-map.js +771 -0
  67. package/dist/core-runtime/reconstruct/contract-registry.js +20 -0
  68. package/dist/core-runtime/reconstruct/dispatch-fallback-artifacts.js +640 -0
  69. package/dist/core-runtime/reconstruct/execution-telemetry.js +83 -9
  70. package/dist/core-runtime/reconstruct/final-output-sections.js +89 -0
  71. package/dist/core-runtime/reconstruct/governing-snapshot.js +36 -1
  72. package/dist/core-runtime/reconstruct/leaf-reader.js +305 -0
  73. package/dist/core-runtime/reconstruct/llm-dispatch-failure.js +270 -0
  74. package/dist/core-runtime/reconstruct/llm-touch-fingerprint.js +94 -0
  75. package/dist/core-runtime/reconstruct/material-admission-validation.js +18 -0
  76. package/dist/core-runtime/reconstruct/material-profile-validation.js +14 -0
  77. package/dist/core-runtime/reconstruct/materialize-preparation.js +304 -18
  78. package/dist/core-runtime/reconstruct/maturation-validation.js +1231 -53
  79. package/dist/core-runtime/reconstruct/mock-llm-realization.js +107 -3
  80. package/dist/core-runtime/reconstruct/obligation-assertion.js +18 -0
  81. package/dist/core-runtime/reconstruct/ontology-seed-validation.js +27 -0
  82. package/dist/core-runtime/reconstruct/output-budget.js +6 -0
  83. package/dist/core-runtime/reconstruct/pipeline-execution-ledger.js +87 -3
  84. package/dist/core-runtime/reconstruct/post-seed-validation.js +118 -5
  85. package/dist/core-runtime/reconstruct/purpose-authority-validation.js +117 -2
  86. package/dist/core-runtime/reconstruct/record.js +42 -0
  87. package/dist/core-runtime/reconstruct/registry-verification-validation.js +16 -0
  88. package/dist/core-runtime/reconstruct/run-control-validation.js +1011 -34
  89. package/dist/core-runtime/reconstruct/run.js +5198 -536
  90. package/dist/core-runtime/reconstruct/seed-authoring-readiness-validation.js +54 -3
  91. package/dist/core-runtime/reconstruct/semantic-quality-gate.js +105 -2
  92. package/dist/core-runtime/reconstruct/source-observation-delta-validation.js +41 -0
  93. package/dist/core-runtime/reconstruct/source-observations.js +166 -0
  94. package/dist/core-runtime/reconstruct/source-safety-validation.js +71 -183
  95. package/dist/core-runtime/reconstruct/source-scout-pack-validation.js +37 -1
  96. package/dist/core-runtime/reconstruct/terminal-validation.js +132 -2
  97. package/dist/core-runtime/review/continuation-plan.js +12 -5
  98. package/dist/core-runtime/review/issue-artifact-runtime.js +4 -1
  99. package/dist/core-runtime/review/materializers.js +100 -11
  100. package/dist/core-runtime/review/nesting-batch.js +6 -2
  101. package/dist/core-runtime/review/obligation-shardability.js +138 -0
  102. package/dist/core-runtime/review/pipeline-execution-ledger.js +42 -2
  103. package/dist/core-runtime/review/review-artifact-utils.js +217 -5
  104. package/dist/core-runtime/review/review-execution-steps.js +32 -8
  105. package/dist/core-runtime/review/review-progress-contract.js +8 -0
  106. package/dist/core-runtime/review/review-prompt-budget.js +97 -0
  107. package/dist/core-runtime/review/semantic-quality-gate.js +33 -7
  108. package/dist/core-runtime/review/spreadsheet-review-disposition.js +94 -0
  109. package/dist/core-runtime/route-identity.js +182 -0
  110. package/dist/core-runtime/spreadsheet-header-escalation.js +204 -0
  111. package/dist/core-runtime/spreadsheet-structure-observer.js +2689 -0
  112. package/dist/core-runtime/target-material-kind.js +58 -0
  113. package/dist/mcp/server.js +12 -5
  114. package/dist/tui/app.js +167 -0
  115. package/dist/tui/data/event-follower.js +116 -0
  116. package/dist/tui/data/node-detail.js +42 -0
  117. package/dist/tui/data/projection-poll.js +26 -0
  118. package/dist/tui/data/session-discovery.js +65 -0
  119. package/dist/tui/index.js +108 -0
  120. package/dist/tui/screens/log.js +25 -0
  121. package/dist/tui/screens/node-detail.js +17 -0
  122. package/dist/tui/screens/session-selector.js +26 -0
  123. package/dist/tui/screens/workflow-tree.js +90 -0
  124. package/dist/tui/view-model/reconstruct-adapter.js +162 -0
  125. package/dist/tui/view-model/review-adapter.js +270 -0
  126. package/dist/tui/view-model/tree-view-model.js +19 -0
  127. package/package.json +18 -3
  128. package/settings.example.json +77 -16
@@ -6,7 +6,8 @@ import { z } from "zod";
6
6
  import { normalizeLlmModelSwitcher, } from "../llm/model-switcher.js";
7
7
  import { createStructuredFailureRecord, } from "../review/failure-records.js";
8
8
  import { fileExists } from "../review/review-artifact-utils.js";
9
- import { assertSupportedModelRoutes, collectModelSelections, loadSupportedModelRegistry, SUPPORTED_MODELS_AUTHORITY_PATH, } from "./supported-models.js";
9
+ import { assertSupportedModelRoutes, collectSupportedModelDispatches, loadSupportedModelRegistry, SUPPORTED_MODELS_AUTHORITY_PATH, } from "./supported-models.js";
10
+ import { REVIEW_EXECUTION_UNIT_IDS } from "./review-execution-units.js";
10
11
  const LlmAuthModeSchema = z.enum(["api_key", "oauth", "local"]);
11
12
  const LlmProviderSchema = z.enum(["openai", "anthropic", "grok", "lmstudio"]);
12
13
  const LlmSettingsSchema = z
@@ -18,6 +19,10 @@ const LlmSettingsSchema = z
18
19
  effort: z.string().min(1).optional(),
19
20
  service_tier: z.string().min(1).optional(),
20
21
  api_key_env: z.string().min(1).optional(),
22
+ // Per-call CLI-worker timeout (ms) for the direct-call route
23
+ // (codex_cli/claude_code); absent → DEFAULT_WORKER_TIMEOUT_MS. Distinct from
24
+ // review `units[].timeout_ms` (worker-process bound). See LlmCallConfig.timeout_ms.
25
+ timeout_ms: z.number().int().min(1).optional(),
21
26
  })
22
27
  .strict();
23
28
  const FullLlmSettingsSchema = z
@@ -29,6 +34,10 @@ const FullLlmSettingsSchema = z
29
34
  effort: z.string().min(1).optional(),
30
35
  service_tier: z.string().min(1).optional(),
31
36
  api_key_env: z.string().min(1).optional(),
37
+ // Per-call CLI-worker timeout (ms) for the direct-call route
38
+ // (codex_cli/claude_code); absent → DEFAULT_WORKER_TIMEOUT_MS. Distinct from
39
+ // review `units[].timeout_ms` (worker-process bound). See LlmCallConfig.timeout_ms.
40
+ timeout_ms: z.number().int().min(1).optional(),
32
41
  })
33
42
  .strict();
34
43
  const ReviewActorLlmSettingsSchema = z
@@ -40,6 +49,10 @@ const ReviewActorLlmSettingsSchema = z
40
49
  effort: z.string().min(1).optional(),
41
50
  service_tier: z.string().min(1).optional(),
42
51
  api_key_env: z.string().min(1).optional(),
52
+ // Per-call CLI-worker timeout (ms) for the direct-call route
53
+ // (codex_cli/claude_code); absent → DEFAULT_WORKER_TIMEOUT_MS. Distinct from
54
+ // review `units[].timeout_ms` (worker-process bound). See LlmCallConfig.timeout_ms.
55
+ timeout_ms: z.number().int().min(1).optional(),
43
56
  })
44
57
  .strict();
45
58
  const LlmRefSchema = LlmSettingsSchema;
@@ -62,19 +75,10 @@ const ReviewArtifactGenerationRealizationSchema = z.enum([
62
75
  ]);
63
76
  const ReviewDeliberationSchema = z.enum(["controlled-lens-deliberation"]);
64
77
  const ReviewLensOutputFormatSchema = z.enum(["markdown", "sidecar"]);
65
- export const REVIEW_EXECUTION_UNIT_IDS = [
66
- "lens",
67
- "finding_ledger",
68
- "finding_relation_graph",
69
- "issue_ledger",
70
- "issue_stance_matrix",
71
- "deliberation_plan",
72
- "problem_framing",
73
- "issue_stance_response",
74
- "deliberation_response",
75
- "deliberation_resolution",
76
- "synthesis_response",
77
- ];
78
+ // Unit-id vocabulary lives in a leaf module (review-execution-units.ts) so the
79
+ // dispatch-role resolver (supported-models) can bound unit paths without an
80
+ // import cycle; re-exported here so existing consumers keep their import site.
81
+ export { REVIEW_EXECUTION_UNIT_IDS };
78
82
  const ReviewExecutionUnitIdSchema = z.enum(REVIEW_EXECUTION_UNIT_IDS);
79
83
  const ReviewToolModeSchema = z.enum(["auto", "native", "inline"]);
80
84
  const ReviewSubmitSalvageSettingsSchema = z
@@ -90,6 +94,46 @@ const ReviewSubmitSalvageSettingsSchema = z
90
94
  delta_completion: z.literal("unit_llm").optional(),
91
95
  })
92
96
  .strict();
97
+ const ReviewUnitResubmitSettingsSchema = z
98
+ .object({
99
+ enabled: z.boolean().optional(),
100
+ })
101
+ .strict();
102
+ /** Dispatch limit/transport circuit breaker settings shape (설계 B) — shared
103
+ * by the reconstruct semantic-map loop (`reconstruct.execution.dispatch_breaker`)
104
+ * and the review lens/stance pools (`review.execution.retry.dispatch_breaker`):
105
+ * one concept, one schema, per-pipeline wire keys. */
106
+ const DispatchBreakerSettingsSchema = z
107
+ .object({
108
+ enabled: z.boolean().optional(),
109
+ systemic_threshold: z.number().int().min(1).optional(),
110
+ per_call_max_attempts: z.number().int().min(1).optional(),
111
+ backoff_initial_ms: z.number().int().min(0).optional(),
112
+ backoff_cap_ms: z.number().int().min(0).optional(),
113
+ })
114
+ .strict();
115
+ const DispatchFallbackLlmSettingsSchema = z
116
+ .object({
117
+ provider: z.enum(["openai", "anthropic"]),
118
+ auth: z.literal("api_key"),
119
+ model: z.string().min(1),
120
+ effort: z.string().min(1),
121
+ api_key_env: z.string().min(1),
122
+ })
123
+ .strict();
124
+ const DispatchFallbackSettingsSchema = z.discriminatedUnion("enabled", [
125
+ z.object({ enabled: z.literal(false) }).strict(),
126
+ z
127
+ .object({
128
+ enabled: z.literal(true),
129
+ trigger: z.literal("rate_limit"),
130
+ max_fallback_passes: z.literal(1),
131
+ per_dispatch_max_provider_attempts: z.literal(1),
132
+ systemic_failure_threshold: z.literal(1),
133
+ llm: DispatchFallbackLlmSettingsSchema,
134
+ })
135
+ .strict(),
136
+ ]);
93
137
  const ReviewRetrySettingsSchema = z
94
138
  .object({
95
139
  lens_max_retries: z.number().int().min(0).optional(),
@@ -98,6 +142,12 @@ const ReviewRetrySettingsSchema = z
98
142
  synthesis_max_retries: z.number().int().min(0).optional(),
99
143
  retry_initial_delay_ms: z.number().int().min(0).optional(),
100
144
  salvage: ReviewSubmitSalvageSettingsSchema.optional(),
145
+ resubmit: ReviewUnitResubmitSettingsSchema.optional(),
146
+ // NOTE: this schema is shared by the V3 INPUT parse and the merged
147
+ // NormalizedSettingsSchema re-validation (via ReviewSettingsSchema), so a
148
+ // key added here survives both — the #167 dual-schema gap class cannot
149
+ // recur for retry keys.
150
+ dispatch_breaker: DispatchBreakerSettingsSchema.optional(),
101
151
  })
102
152
  .strict();
103
153
  const ReviewUnitExecutionSettingsSchema = z
@@ -136,6 +186,26 @@ const DEFAULT_REVIEW_EXECUTION = {
136
186
  synthesize: { seat: "worker" },
137
187
  deliberation: "controlled-lens-deliberation",
138
188
  };
189
+ export const DEFAULT_DISPATCH_BREAKER_SETTINGS = {
190
+ // opt-in: OFF = 현행 동작 보존 — 활성화는 settings로만.
191
+ enabled: false,
192
+ systemic_threshold: 3,
193
+ per_call_max_attempts: 3,
194
+ backoff_initial_ms: 3000,
195
+ backoff_cap_ms: 30000,
196
+ };
197
+ export function completeDispatchBreakerSettings(input) {
198
+ return {
199
+ enabled: input?.enabled ?? DEFAULT_DISPATCH_BREAKER_SETTINGS.enabled,
200
+ systemic_threshold: input?.systemic_threshold ??
201
+ DEFAULT_DISPATCH_BREAKER_SETTINGS.systemic_threshold,
202
+ per_call_max_attempts: input?.per_call_max_attempts ??
203
+ DEFAULT_DISPATCH_BREAKER_SETTINGS.per_call_max_attempts,
204
+ backoff_initial_ms: input?.backoff_initial_ms ??
205
+ DEFAULT_DISPATCH_BREAKER_SETTINGS.backoff_initial_ms,
206
+ backoff_cap_ms: input?.backoff_cap_ms ?? DEFAULT_DISPATCH_BREAKER_SETTINGS.backoff_cap_ms,
207
+ };
208
+ }
139
209
  const DEFAULT_REVIEW_RETRY_SETTINGS = {
140
210
  lens_max_retries: 2,
141
211
  issue_artifact_max_retries: 2,
@@ -144,6 +214,10 @@ const DEFAULT_REVIEW_RETRY_SETTINGS = {
144
214
  retry_initial_delay_ms: 3000,
145
215
  // opt-in: 벤치마크 재현성 보호 — 활성화는 settings로만.
146
216
  salvage: { enabled: false, delta_completion: "unit_llm" },
217
+ // opt-in: OFF = 현행 halt 동작 보존 — 활성화는 settings로만.
218
+ resubmit: { enabled: false },
219
+ // opt-in: OFF = 현행 halt/배리어 동작 보존 — 활성화는 settings로만.
220
+ dispatch_breaker: DEFAULT_DISPATCH_BREAKER_SETTINGS,
147
221
  };
148
222
  const DEFAULT_REVIEW_UNIT_TIMEOUT_MS = 240000;
149
223
  const DEFAULT_REVIEW_SHORT_LLM_UNIT_TIMEOUT_MS = 180000;
@@ -255,11 +329,76 @@ const V3ReviewActorSettingsSchema = z
255
329
  llm: ReviewActorLlmSettingsSchema,
256
330
  })
257
331
  .strict();
332
+ const ReconstructActorLlmRuntimeSettingsSchema = z
333
+ .object({
334
+ openai_responses_output_headroom_tokens: z.number().int().positive().safe(),
335
+ })
336
+ .strict();
258
337
  const V3ReconstructActorSettingsSchema = z
259
338
  .object({
260
339
  llm: FullLlmSettingsSchema,
340
+ llm_runtime: ReconstructActorLlmRuntimeSettingsSchema.optional(),
261
341
  })
262
342
  .strict();
343
+ /**
344
+ * Single source of the reconstruct actor-seat key set (INV-MODEL-1 role-aware
345
+ * design §5.1). The V3/Normalized zod shapes and the ReconstructSettings type
346
+ * are DERIVED from this constant, and the normalize/merge copy functions
347
+ * iterate it — so a new actor key added here flows through parse, normalize,
348
+ * merge, and the gate walk without touching a hand-enumerated whitelist (the
349
+ * silent-strip class F19 is closed by construction). `semantic_author` and
350
+ * `confirmation_provider` are REQUIRED seats for live direct_call execution
351
+ * (resolveReconstructActorLlmSettings); `semantic_map_synthesize` is an
352
+ * OPTIONAL per-role override seat (absent = inherit the semantic_author
353
+ * config; resolveOptionalReconstructActorLlmSettings).
354
+ */
355
+ export const RECONSTRUCT_ACTOR_KEYS = [
356
+ "semantic_author",
357
+ "confirmation_provider",
358
+ "semantic_map_synthesize",
359
+ ];
360
+ /**
361
+ * Declared reconstruct.execution-level scalar settings (design §5.1). Like the
362
+ * actor axis, the zod schemas' scalar entries are DERIVED from this constant
363
+ * (reconstructExecutionScalarsShape) and the type maps over it, so a
364
+ * schema-declared-but-constant-missing key cannot exist (no second key
365
+ * authority in either direction); the normalize/merge copy functions iterate
366
+ * this list, so a declared scalar survives the settings chain even when
367
+ * `actors` is absent. A key NOT declared here is rejected by the strict
368
+ * schemas (fail-loud), never silently dropped. Current scalars are all
369
+ * booleans; a future non-boolean scalar must extend the shape helper.
370
+ */
371
+ export const RECONSTRUCT_EXECUTION_SCALAR_KEYS = [
372
+ "semantic_map_authoring",
373
+ ];
374
+ /** zod actors shape derived from {@link RECONSTRUCT_ACTOR_KEYS} — the schema
375
+ * cannot drift from the constant (no second key authority). */
376
+ function reconstructActorsShape(actorSchema) {
377
+ return Object.fromEntries(RECONSTRUCT_ACTOR_KEYS.map((key) => [key, actorSchema.optional()]));
378
+ }
379
+ function reconstructActorsSettingsSchema() {
380
+ return z
381
+ .object(reconstructActorsShape(V3ReconstructActorSettingsSchema))
382
+ .strict()
383
+ .superRefine((actors, ctx) => {
384
+ for (const key of RECONSTRUCT_ACTOR_KEYS) {
385
+ if (key !== "semantic_author" && actors[key]?.llm_runtime !== undefined) {
386
+ ctx.addIssue({
387
+ code: "custom",
388
+ path: [key, "llm_runtime"],
389
+ message: "reconstruct actor llm_runtime is currently supported only on semantic_author.",
390
+ });
391
+ }
392
+ }
393
+ });
394
+ }
395
+ /** zod execution-scalar shape derived from
396
+ * {@link RECONSTRUCT_EXECUTION_SCALAR_KEYS} — same single-source rule as the
397
+ * actor axis, so the F19/F24 silent-strip class is closed by construction on
398
+ * BOTH axes. */
399
+ function reconstructExecutionScalarsShape() {
400
+ return Object.fromEntries(RECONSTRUCT_EXECUTION_SCALAR_KEYS.map((key) => [key, z.boolean().optional()]));
401
+ }
263
402
  const V3ReviewExecutionSettingsSchema = z
264
403
  .object({
265
404
  topology: ReviewExecutionModeSchema.optional(),
@@ -349,12 +488,13 @@ const V3ReconstructSettingsSchema = z
349
488
  execution: z
350
489
  .object({
351
490
  actors: z
352
- .object({
353
- semantic_author: V3ReconstructActorSettingsSchema.optional(),
354
- confirmation_provider: V3ReconstructActorSettingsSchema.optional(),
355
- })
356
- .strict()
491
+ .lazy(reconstructActorsSettingsSchema)
357
492
  .optional(),
493
+ // Execution-level scalars (e.g. the semantic-map authoring opt-in,
494
+ // design §5.5) — entries DERIVED from the scalar constant (§5.1).
495
+ ...reconstructExecutionScalarsShape(),
496
+ dispatch_breaker: DispatchBreakerSettingsSchema.optional(),
497
+ dispatch_fallback: DispatchFallbackSettingsSchema.optional(),
358
498
  })
359
499
  .strict()
360
500
  .optional(),
@@ -377,18 +517,11 @@ const NormalizedSettingsSchema = z
377
517
  execution: z
378
518
  .object({
379
519
  actors: z
380
- .object({
381
- semantic_author: z
382
- .object({ llm: FullLlmSettingsSchema })
383
- .strict()
384
- .optional(),
385
- confirmation_provider: z
386
- .object({ llm: FullLlmSettingsSchema })
387
- .strict()
388
- .optional(),
389
- })
390
- .strict()
520
+ .lazy(reconstructActorsSettingsSchema)
391
521
  .optional(),
522
+ ...reconstructExecutionScalarsShape(),
523
+ dispatch_breaker: DispatchBreakerSettingsSchema.optional(),
524
+ dispatch_fallback: DispatchFallbackSettingsSchema.optional(),
392
525
  })
393
526
  .strict()
394
527
  .optional(),
@@ -411,6 +544,32 @@ export function resolveReconstructActorLlmSettings(settings, actorName) {
411
544
  normalizeLlmModelSwitcher(actor.llm);
412
545
  return actor.llm;
413
546
  }
547
+ export function resolveReconstructSemanticAuthorLlmRuntimeSettings(settings) {
548
+ return settings.reconstruct?.execution?.actors?.semantic_author?.llm_runtime;
549
+ }
550
+ /**
551
+ * THE single reader of an OPTIONAL reconstruct actor seat for DISPATCH
552
+ * consumers (design §5.4) — live wiring, mock identity projection, and tests
553
+ * all consume this one post-chain projection so no second dispatch-side
554
+ * seat-reading authority can drift. (The supported-model gate walks the same
555
+ * post-chain settings object structurally via collectModelSelections; both
556
+ * therefore see one seat value.) Absent seat → undefined (the stage inherits
557
+ * the semantic_author config). Pure projection: normalizes the switcher shape
558
+ * only — no provider/auth resolution, so it is safe under mock realization.
559
+ */
560
+ export function resolveOptionalReconstructActorLlmSettings(settings, actorName) {
561
+ const actor = settings.reconstruct?.execution?.actors?.[actorName];
562
+ if (!actor)
563
+ return undefined;
564
+ normalizeLlmModelSwitcher(actor.llm);
565
+ return actor.llm;
566
+ }
567
+ /** Production opt-in for the semantic-map authoring stage (design §5.5).
568
+ * Absent/false = off: the capability pair is not attached AND the synthesize
569
+ * seat is dormant (excluded from the gate walk — U6, salvage precedent). */
570
+ export function isReconstructSemanticMapAuthoringEnabled(settings) {
571
+ return settings.reconstruct?.execution?.semantic_map_authoring === true;
572
+ }
414
573
  export const SETTINGS_FILENAME = "settings.json";
415
574
  export const RETIRED_CONFIG_FILENAMES = [
416
575
  `config.${"yml"}`,
@@ -546,6 +705,15 @@ function definedReviewRetry(retry) {
546
705
  }
547
706
  out.salvage = salvage;
548
707
  }
708
+ // 정정 2026-07-05: resubmit이 이 복사 함수에서 누락되어 설정 파일의
709
+ // opt-in(true)이 정규화 단계에서 소실됐다(#163 관찰 모드가 불활성이던
710
+ // 원인). strict 파서가 이미 미지 키를 거른 뒤라 통복사가 안전하다.
711
+ if (retry.resubmit !== undefined) {
712
+ out.resubmit = { ...retry.resubmit };
713
+ }
714
+ if (retry.dispatch_breaker !== undefined) {
715
+ out.dispatch_breaker = { ...retry.dispatch_breaker };
716
+ }
549
717
  return Object.keys(out).length > 0 ? out : undefined;
550
718
  }
551
719
  function definedReviewUnitExecutionSettings(unit) {
@@ -601,6 +769,10 @@ export function completeReviewRetrySettings(retry) {
601
769
  delta_completion: retry?.salvage?.delta_completion ??
602
770
  DEFAULT_REVIEW_RETRY_SETTINGS.salvage.delta_completion,
603
771
  },
772
+ resubmit: {
773
+ enabled: retry?.resubmit?.enabled ?? DEFAULT_REVIEW_RETRY_SETTINGS.resubmit.enabled,
774
+ },
775
+ dispatch_breaker: completeDispatchBreakerSettings(retry?.dispatch_breaker),
604
776
  };
605
777
  }
606
778
  function v3ActorSettings(actor) {
@@ -610,22 +782,47 @@ function v3ActorSettings(actor) {
610
782
  };
611
783
  }
612
784
  function v3ReconstructActorSettings(actor) {
613
- return { llm: actor.llm };
785
+ return {
786
+ llm: actor.llm,
787
+ ...(actor.llm_runtime !== undefined
788
+ ? { llm_runtime: actor.llm_runtime }
789
+ : {}),
790
+ };
614
791
  }
615
- function v3ReconstructSettings(reconstruct) {
616
- const actors = reconstruct?.execution?.actors;
617
- if (!actors)
792
+ /**
793
+ * Normalizes the parsed V3 reconstruct block into the runtime shape.
794
+ * STRUCTURE-PRESERVING by construction (design §5.1): actor copies iterate
795
+ * RECONSTRUCT_ACTOR_KEYS and execution-level scalars iterate
796
+ * RECONSTRUCT_EXECUTION_SCALAR_KEYS — no hand-enumerated whitelist, and no
797
+ * actors-absent early return (a scalar-only block survives).
798
+ * Exported for the unit-level drift-guard tests, which call the copy
799
+ * functions directly (bypassing the strict parser) so a missed copy fails.
800
+ */
801
+ export function v3ReconstructSettings(reconstruct) {
802
+ const execution = reconstruct?.execution;
803
+ if (!execution)
618
804
  return undefined;
619
805
  const normalizedActors = {};
620
- if (actors.semantic_author) {
621
- normalizedActors.semantic_author = v3ReconstructActorSettings(actors.semantic_author);
806
+ for (const key of RECONSTRUCT_ACTOR_KEYS) {
807
+ const actor = execution.actors?.[key];
808
+ if (actor)
809
+ normalizedActors[key] = v3ReconstructActorSettings(actor);
810
+ }
811
+ const out = {};
812
+ if (Object.keys(normalizedActors).length > 0)
813
+ out.actors = normalizedActors;
814
+ for (const key of RECONSTRUCT_EXECUTION_SCALAR_KEYS) {
815
+ const value = execution[key];
816
+ if (value !== undefined)
817
+ out[key] = value;
622
818
  }
623
- if (actors.confirmation_provider) {
624
- normalizedActors.confirmation_provider = v3ReconstructActorSettings(actors.confirmation_provider);
819
+ if (execution.dispatch_breaker !== undefined) {
820
+ out.dispatch_breaker = execution.dispatch_breaker;
625
821
  }
626
- return Object.keys(normalizedActors).length > 0
627
- ? { execution: { actors: normalizedActors } }
628
- : undefined;
822
+ if (execution.dispatch_fallback !== undefined) {
823
+ out.dispatch_fallback = execution.dispatch_fallback;
824
+ }
825
+ return Object.keys(out).length > 0 ? { execution: out } : undefined;
629
826
  }
630
827
  function normalizeV3Settings(settings) {
631
828
  const execution = settings.review?.execution;
@@ -814,6 +1011,25 @@ function mergeReviewRetrySettings(userRetry, projectRetry) {
814
1011
  },
815
1012
  }
816
1013
  : {}),
1014
+ // resubmit merges deep for the same reason as salvage.
1015
+ ...(userRetry?.resubmit !== undefined || projectRetry?.resubmit !== undefined
1016
+ ? {
1017
+ resubmit: {
1018
+ ...(userRetry?.resubmit ?? {}),
1019
+ ...(projectRetry?.resubmit ?? {}),
1020
+ },
1021
+ }
1022
+ : {}),
1023
+ // dispatch_breaker merges deep for the same reason as salvage/resubmit.
1024
+ ...(userRetry?.dispatch_breaker !== undefined ||
1025
+ projectRetry?.dispatch_breaker !== undefined
1026
+ ? {
1027
+ dispatch_breaker: {
1028
+ ...(userRetry?.dispatch_breaker ?? {}),
1029
+ ...(projectRetry?.dispatch_breaker ?? {}),
1030
+ },
1031
+ }
1032
+ : {}),
817
1033
  };
818
1034
  return completeReviewRetrySettings(merged);
819
1035
  }
@@ -872,15 +1088,44 @@ function mergeReviewUnits(defaultUnits, userUnits, projectUnits) {
872
1088
  function mergeReconstructActorSettings(userActor, projectActor) {
873
1089
  return projectActor ?? userActor;
874
1090
  }
875
- function mergeReconstructSettings(user, project) {
876
- const semanticAuthor = mergeReconstructActorSettings(user?.execution?.actors?.semantic_author, project?.execution?.actors?.semantic_author);
877
- const confirmationProvider = mergeReconstructActorSettings(user?.execution?.actors?.confirmation_provider, project?.execution?.actors?.confirmation_provider);
1091
+ /**
1092
+ * user+project merge of the reconstruct block. STRUCTURE-PRESERVING by
1093
+ * construction (design §5.1): per-actor merge iterates RECONSTRUCT_ACTOR_KEYS
1094
+ * (project > user per actor), execution-level scalars iterate
1095
+ * RECONSTRUCT_EXECUTION_SCALAR_KEYS (project > user) and survive even when no
1096
+ * actors are configured on either side. Exported for the unit-level
1097
+ * drift-guard tests (direct calls, strict parser bypassed).
1098
+ */
1099
+ export function mergeReconstructSettings(user, project) {
878
1100
  const actors = {};
879
- if (semanticAuthor)
880
- actors.semantic_author = semanticAuthor;
881
- if (confirmationProvider)
882
- actors.confirmation_provider = confirmationProvider;
883
- return Object.keys(actors).length > 0 ? { execution: { actors } } : undefined;
1101
+ for (const key of RECONSTRUCT_ACTOR_KEYS) {
1102
+ const merged = mergeReconstructActorSettings(user?.execution?.actors?.[key], project?.execution?.actors?.[key]);
1103
+ if (merged)
1104
+ actors[key] = merged;
1105
+ }
1106
+ const out = {};
1107
+ if (Object.keys(actors).length > 0)
1108
+ out.actors = actors;
1109
+ for (const key of RECONSTRUCT_EXECUTION_SCALAR_KEYS) {
1110
+ const value = project?.execution?.[key] ?? user?.execution?.[key];
1111
+ if (value !== undefined)
1112
+ out[key] = value;
1113
+ }
1114
+ // dispatch_breaker merges deep (salvage/resubmit precedent): a project
1115
+ // layer that partially sets it must not clobber an inherited user opt-in.
1116
+ const dispatchBreaker = user?.execution?.dispatch_breaker !== undefined ||
1117
+ project?.execution?.dispatch_breaker !== undefined
1118
+ ? {
1119
+ ...(user?.execution?.dispatch_breaker ?? {}),
1120
+ ...(project?.execution?.dispatch_breaker ?? {}),
1121
+ }
1122
+ : undefined;
1123
+ if (dispatchBreaker !== undefined)
1124
+ out.dispatch_breaker = dispatchBreaker;
1125
+ const dispatchFallback = project?.execution?.dispatch_fallback ?? user?.execution?.dispatch_fallback;
1126
+ if (dispatchFallback !== undefined)
1127
+ out.dispatch_fallback = dispatchFallback;
1128
+ return Object.keys(out).length > 0 ? { execution: out } : undefined;
884
1129
  }
885
1130
  function contextFromSettings(settings) {
886
1131
  return definedReviewContext({
@@ -969,7 +1214,7 @@ function unitDefaultActorForSettingsValidation(unitId) {
969
1214
  * the membership check fails loud on it.
970
1215
  */
971
1216
  export function collectEffectiveModelRoutes(settings) {
972
- const nodes = collectModelSelections(settings);
1217
+ const nodes = collectSupportedModelDispatches(settings);
973
1218
  const providerAtPath = new Map();
974
1219
  const modelAtPath = new Map();
975
1220
  for (const node of nodes) {
@@ -1001,12 +1246,23 @@ export function collectEffectiveModelRoutes(settings) {
1001
1246
  // is disabled the model never runs, so validating it would be a false positive
1002
1247
  // that could block G7 or a live run on an unused setting.
1003
1248
  const salvageEnabled = settings.review?.execution?.retry?.salvage?.enabled === true;
1249
+ // The synthesize seat dispatches only when semantic-map authoring is opted
1250
+ // in (the capability pair is attached solely under the opt-in). A dormant
1251
+ // seat (configured, opt-in off) never dispatches, so validating it would
1252
+ // block every live run on an unused setting — same dispatch-conditioned
1253
+ // exemption as salvage (design §5.1-7, U6 owner decision). Flipping the
1254
+ // opt-in on brings the seat into the walk and fails loud then.
1255
+ const semanticMapAuthoringEnabled = isReconstructSemanticMapAuthoringEnabled(settings);
1256
+ const SYNTHESIZE_SEAT_PATH = "reconstruct.execution.actors.semantic_map_synthesize.llm";
1004
1257
  return nodes.flatMap((node) => {
1005
1258
  if (isSalvageTranscription(node.path)) {
1006
1259
  if (!salvageEnabled)
1007
1260
  return []; // disabled salvage transcription never dispatches
1008
1261
  return [{ ...node, provider: node.provider ?? DEFAULT_TRANSCRIPTION_PROVIDER }];
1009
1262
  }
1263
+ if (node.path === SYNTHESIZE_SEAT_PATH && !semanticMapAuthoringEnabled) {
1264
+ return []; // dormant synthesize seat never dispatches (opt-in off)
1265
+ }
1010
1266
  const unitId = reviewUnitOf(node.path);
1011
1267
  if (unitId) {
1012
1268
  // A review-unit override merges over its default actor llm
@@ -1017,6 +1273,7 @@ export function collectEffectiveModelRoutes(settings) {
1017
1273
  provider: node.provider ?? providerAtPath.get(actorPath),
1018
1274
  model: node.model ?? modelAtPath.get(actorPath),
1019
1275
  path: node.path,
1276
+ requiredRole: node.requiredRole,
1020
1277
  }];
1021
1278
  }
1022
1279
  // Actors, reconstruct actors, and the top-level llm are base seats: validated
@@ -1037,9 +1294,9 @@ export function collectEffectiveModelRoutes(settings) {
1037
1294
  * disagree. Mock/test paths that resolve settings without making real calls
1038
1295
  * never invoke it.
1039
1296
  */
1040
- export function assertSettingsModelsSupported(settings) {
1297
+ export function assertSettingsModelsSupported(settings, options) {
1041
1298
  try {
1042
- assertSupportedModelRoutes(collectEffectiveModelRoutes(settings), loadSupportedModelRegistry());
1299
+ assertSupportedModelRoutes(collectEffectiveModelRoutes(settings), loadSupportedModelRegistry(), options);
1043
1300
  }
1044
1301
  catch (error) {
1045
1302
  if (error instanceof OntoSettingsValidationError)
@@ -1163,6 +1420,8 @@ export function defaultReviewRetrySettings() {
1163
1420
  return {
1164
1421
  ...DEFAULT_REVIEW_RETRY_SETTINGS,
1165
1422
  salvage: { ...DEFAULT_REVIEW_RETRY_SETTINGS.salvage },
1423
+ resubmit: { ...DEFAULT_REVIEW_RETRY_SETTINGS.resubmit },
1424
+ dispatch_breaker: { ...DEFAULT_REVIEW_RETRY_SETTINGS.dispatch_breaker },
1166
1425
  };
1167
1426
  }
1168
1427
  function validateActorLlmRefs(settings) {