onto-mcp 0.3.0 → 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/.onto/authority/core-lexicon.yaml +12 -0
  2. package/.onto/domains/software-engineering/competency_qs.md +192 -63
  3. package/.onto/domains/software-engineering/concepts.md +67 -5
  4. package/.onto/domains/software-engineering/conciseness_rules.md +22 -2
  5. package/.onto/domains/software-engineering/dependency_rules.md +78 -8
  6. package/.onto/domains/software-engineering/domain_scope.md +181 -150
  7. package/.onto/domains/software-engineering/extension_cases.md +318 -542
  8. package/.onto/domains/software-engineering/logic_rules.md +75 -3
  9. package/.onto/domains/software-engineering/problem_framing_profile.md +29 -2
  10. package/.onto/domains/software-engineering/prompt_interface.md +122 -0
  11. package/.onto/domains/software-engineering/structure_spec.md +53 -4
  12. package/.onto/principles/llm-native-development-guideline.md +20 -0
  13. package/.onto/principles/productization-charter.md +6 -0
  14. package/.onto/processes/evolve/material-kind-adapter-contract.md +6 -0
  15. package/.onto/processes/reconstruct/reconstruct-boundary-contract.md +468 -81
  16. package/.onto/processes/reconstruct/reconstruct-execution-ux-contract.md +177 -0
  17. package/.onto/processes/reconstruct/source-profile-contract.md +39 -6
  18. package/.onto/processes/reconstruct/top-level-concept-discovery-contract.md +387 -0
  19. package/.onto/processes/review/binding-contract.md +8 -0
  20. package/.onto/processes/review/lens-registry.md +16 -0
  21. package/.onto/processes/review/pre-dispatch-contracts.md +34 -13
  22. package/.onto/processes/review/productized-live-path.md +3 -1
  23. package/.onto/processes/shared/pipeline-execution-ledger-contract.md +185 -0
  24. package/.onto/processes/shared/target-material-kind-contract.md +24 -2
  25. package/.onto/roles/axiology.md +7 -2
  26. package/AGENTS.md +4 -2
  27. package/README.md +52 -29
  28. package/dist/core-api/reconstruct-api.js +92 -5
  29. package/dist/core-api/review-api.js +1744 -371
  30. package/dist/core-runtime/cli/mock-review-unit-executor.js +17 -0
  31. package/dist/core-runtime/cli/render-review-final-output.js +9 -0
  32. package/dist/core-runtime/cli/review-invoke.js +387 -55
  33. package/dist/core-runtime/cli/run-review-prompt-execution.js +361 -90
  34. package/dist/core-runtime/path-boundary.js +58 -0
  35. package/dist/core-runtime/pipeline-execution-ledger.js +100 -0
  36. package/dist/core-runtime/reconstruct/artifact-types.js +33 -1
  37. package/dist/core-runtime/reconstruct/materialize-preparation.js +54 -4
  38. package/dist/core-runtime/reconstruct/pipeline-execution-ledger.js +342 -0
  39. package/dist/core-runtime/reconstruct/post-seed-validation.js +630 -0
  40. package/dist/core-runtime/reconstruct/record.js +105 -1
  41. package/dist/core-runtime/reconstruct/run.js +1594 -38
  42. package/dist/core-runtime/reconstruct/seed-candidate-validation.js +29 -0
  43. package/dist/core-runtime/review/continuation-plan.js +160 -0
  44. package/dist/core-runtime/review/execution-plan-boundary.js +123 -0
  45. package/dist/core-runtime/review/materializers.js +8 -3
  46. package/dist/core-runtime/review/pipeline-execution-ledger.js +250 -0
  47. package/dist/core-runtime/review/review-artifact-utils.js +15 -2
  48. package/dist/core-runtime/review/review-invocation-runner.js +604 -0
  49. package/dist/core-runtime/target-material-kind.js +43 -5
  50. package/dist/mcp/server.js +289 -59
  51. package/dist/mcp/tool-schemas.js +28 -2
  52. package/package.json +4 -2
  53. package/.onto/domains/llm-native-development/competency_qs.md +0 -430
  54. package/.onto/domains/llm-native-development/concepts.md +0 -242
  55. package/.onto/domains/llm-native-development/conciseness_rules.md +0 -163
  56. package/.onto/domains/llm-native-development/dependency_rules.md +0 -216
  57. package/.onto/domains/llm-native-development/domain_scope.md +0 -197
  58. package/.onto/domains/llm-native-development/extension_cases.md +0 -474
  59. package/.onto/domains/llm-native-development/logic_rules.md +0 -123
  60. package/.onto/domains/llm-native-development/prompt_interface.md +0 -49
  61. package/.onto/domains/llm-native-development/structure_spec.md +0 -245
@@ -0,0 +1,604 @@
1
+ import path from "node:path";
2
+ import { completeReviewSession } from "../cli/complete-review-session.js";
3
+ import { ensureProviderRouteReadyForDispatch, inferExecutorRealization, readOptionalReviewSummary, readReviewResultClosureSummary, readReviewResultExplanationSummary, resolveExecutorConfig, resolveReviewInvokeSetup, } from "../cli/review-invoke.js";
4
+ import { executeReviewPromptExecution } from "../cli/run-review-prompt-execution.js";
5
+ import { startReviewSession } from "../cli/start-review-session.js";
6
+ import { buildReviewExecutionRoute } from "./review-execution-route.js";
7
+ import { fileExists, normalizeDomainValue, readSingleOptionValueFromArgv, } from "./review-artifact-utils.js";
8
+ export function appendReviewInvocationRequestArgs(args, request, options) {
9
+ if (request.domain && request.noDomain) {
10
+ throw new Error("Use either domain or noDomain, not both.");
11
+ }
12
+ const result = [
13
+ ...args,
14
+ request.target,
15
+ request.intent,
16
+ "--project-root",
17
+ path.resolve(request.projectRoot),
18
+ "--onto-home",
19
+ options.ontoHome,
20
+ ];
21
+ if (request.domain) {
22
+ result.push("--domain", normalizeDomainValue(request.domain));
23
+ result.push("--requested-domain-token", request.domain);
24
+ }
25
+ if (request.noDomain) {
26
+ result.push("--no-domain");
27
+ }
28
+ if (request.reviewMode) {
29
+ result.push("--review-mode", request.reviewMode);
30
+ }
31
+ if (request.targetScopeKind) {
32
+ result.push("--target-scope-kind", request.targetScopeKind);
33
+ }
34
+ if (request.primaryRef) {
35
+ result.push("--primary-ref", request.primaryRef);
36
+ }
37
+ for (const memberRef of request.memberRefs ?? []) {
38
+ result.push("--member-ref", memberRef);
39
+ }
40
+ if (request.bundleKind) {
41
+ result.push("--bundle-kind", request.bundleKind);
42
+ }
43
+ if (request.diffRange) {
44
+ result.push("--diff-range", request.diffRange);
45
+ }
46
+ if (request.executorRealization) {
47
+ result.push("--executor-realization", request.executorRealization);
48
+ }
49
+ for (const lensId of request.lensIds ?? []) {
50
+ result.push("--lens-id", lensId);
51
+ }
52
+ if (request.confirmValueAlignment) {
53
+ result.push("--confirm-value-alignment");
54
+ }
55
+ return result;
56
+ }
57
+ export async function prepareReviewInvocationRequest(request, options) {
58
+ const argv = appendReviewInvocationRequestArgs([], request, {
59
+ ontoHome: options.ontoHome,
60
+ });
61
+ try {
62
+ const { result } = await withCapturedInvocationConsole(() => prepareReviewInvocationArgv(argv, options.progressObserver));
63
+ return result;
64
+ }
65
+ catch (error) {
66
+ await emitFailureProgress(options.progressObserver, "prepare", null, error);
67
+ throw error;
68
+ }
69
+ }
70
+ export async function runReviewInvocation(request, options) {
71
+ const argv = appendReviewInvocationRequestArgs(options.noWatch === false ? [] : ["--no-watch"], request, { ontoHome: options.ontoHome });
72
+ try {
73
+ const captured = await withCapturedInvocationConsole(() => runReviewInvocationArgv(argv, options.progressObserver));
74
+ return {
75
+ output: captured.result,
76
+ stdout: captured.stdout,
77
+ stderr: captured.stderr,
78
+ };
79
+ }
80
+ catch (error) {
81
+ await emitFailureProgress(options.progressObserver, "execute", null, error);
82
+ throw error;
83
+ }
84
+ }
85
+ export async function collectReviewInvocationArtifactRefs(sessionRoot) {
86
+ const candidates = {
87
+ session_metadata: path.join(sessionRoot, "session-metadata.yaml"),
88
+ interpretation: path.join(sessionRoot, "interpretation.yaml"),
89
+ binding: path.join(sessionRoot, "binding.yaml"),
90
+ execution_plan: path.join(sessionRoot, "execution-plan.yaml"),
91
+ execution_result: path.join(sessionRoot, "execution-result.yaml"),
92
+ actor_invocation_profiles: path.join(sessionRoot, "execution-preparation", "actor-invocation-profiles.yaml"),
93
+ actor_consumer_bindings: path.join(sessionRoot, "execution-preparation", "actor-consumer-bindings.yaml"),
94
+ domain_binding: path.join(sessionRoot, "execution-preparation", "domain-binding.yaml"),
95
+ review_value_alignment_criteria: path.join(sessionRoot, "execution-preparation", "review-value-alignment-criteria.yaml"),
96
+ review_target_profile: path.join(sessionRoot, "execution-preparation", "review-target-profile.yaml"),
97
+ review_context_manifest: path.join(sessionRoot, "execution-preparation", "review-context-manifest.yaml"),
98
+ lens_completion_barrier: path.join(sessionRoot, "lens-completion-barrier.yaml"),
99
+ finding_ledger: path.join(sessionRoot, "finding-ledger.yaml"),
100
+ finding_relation_graph: path.join(sessionRoot, "finding-relation-graph.yaml"),
101
+ issue_ledger: path.join(sessionRoot, "issue-ledger.yaml"),
102
+ issue_stance_matrix: path.join(sessionRoot, "issue-stance-matrix.yaml"),
103
+ deliberation_plan: path.join(sessionRoot, "deliberation-plan.yaml"),
104
+ problem_framing: path.join(sessionRoot, "problem-framing.yaml"),
105
+ deliberation_output: path.join(sessionRoot, "deliberation.md"),
106
+ synthesis_output: path.join(sessionRoot, "synthesis.md"),
107
+ review_run_manifest: path.join(sessionRoot, "review-run-manifest.yaml"),
108
+ degradation_summary: path.join(sessionRoot, "degradation-summary.yaml"),
109
+ active_review_attempt: path.join(sessionRoot, "active-review-attempt.yaml"),
110
+ review_cancel_request: path.join(sessionRoot, "review-cancel-request.yaml"),
111
+ environment_warnings: path.join(sessionRoot, "environment-warnings.yaml"),
112
+ error_log: path.join(sessionRoot, "error-log.md"),
113
+ final_output: path.join(sessionRoot, "final-output.md"),
114
+ review_record: path.join(sessionRoot, "review-record.yaml"),
115
+ };
116
+ const entries = [];
117
+ for (const [key, filePath] of Object.entries(candidates)) {
118
+ if (await fileExists(filePath)) {
119
+ entries.push([key, filePath]);
120
+ }
121
+ }
122
+ return Object.fromEntries(entries);
123
+ }
124
+ export function parseLegacyReviewInvocationOutput(stdout) {
125
+ for (const line of [...stdout].reverse()) {
126
+ const trimmed = line.trim();
127
+ if (!trimmed.startsWith("{"))
128
+ continue;
129
+ try {
130
+ const parsed = JSON.parse(trimmed);
131
+ if (isLegacyReviewInvocationOutput(parsed))
132
+ return parsed;
133
+ }
134
+ catch {
135
+ // Keep looking: progress messages may contain braces or partial JSON.
136
+ }
137
+ }
138
+ throw new Error("review invocation completed without a structured JSON result.");
139
+ }
140
+ export function projectReviewInvocationEquivalence(output) {
141
+ const entrypointPlan = isRecord(output.entrypoint_plan)
142
+ ? output.entrypoint_plan
143
+ : {};
144
+ const artifacts = isRecord(output.artifacts) ? output.artifacts : {};
145
+ return {
146
+ recordStatus: output.review_result.record_status,
147
+ deliberationStatus: output.review_result.deliberation_status ?? null,
148
+ domainFinalValue: typeof entrypointPlan.domain_final_value === "string"
149
+ ? entrypointPlan.domain_final_value
150
+ : null,
151
+ domainSelectionMode: typeof entrypointPlan.domain_selection_mode === "string"
152
+ ? entrypointPlan.domain_selection_mode
153
+ : null,
154
+ reviewMode: typeof entrypointPlan.review_mode === "string"
155
+ ? entrypointPlan.review_mode
156
+ : null,
157
+ routeSummary: output.route_summary ?? null,
158
+ boundedInvokeSteps: output.bounded_invoke_steps ?? [],
159
+ artifactKeys: Object.keys(artifacts).sort(),
160
+ participatingLensIds: output.review_result.participating_lens_ids ?? [],
161
+ degradedLensIds: output.review_result.degraded_lens_ids ?? [],
162
+ };
163
+ }
164
+ function isLegacyReviewInvocationOutput(value) {
165
+ if (value === null || typeof value !== "object")
166
+ return false;
167
+ const reviewResult = value.review_result;
168
+ return reviewResult !== null && typeof reviewResult === "object";
169
+ }
170
+ function isRecord(value) {
171
+ return value !== null && typeof value === "object" && !Array.isArray(value);
172
+ }
173
+ function stringifyConsoleArgs(args) {
174
+ return args
175
+ .map((arg) => typeof arg === "string"
176
+ ? arg
177
+ : arg instanceof Error
178
+ ? arg.stack ?? arg.message
179
+ : JSON.stringify(arg))
180
+ .join(" ");
181
+ }
182
+ async function withCapturedInvocationConsole(action) {
183
+ const originalLog = console.log;
184
+ const originalWarn = console.warn;
185
+ const originalError = console.error;
186
+ const stdout = [];
187
+ const stderr = [];
188
+ console.log = (...args) => {
189
+ stdout.push(stringifyConsoleArgs(args));
190
+ };
191
+ console.warn = (...args) => {
192
+ stderr.push(stringifyConsoleArgs(args));
193
+ };
194
+ console.error = (...args) => {
195
+ stderr.push(stringifyConsoleArgs(args));
196
+ };
197
+ try {
198
+ const result = await action();
199
+ return { result, stdout, stderr };
200
+ }
201
+ finally {
202
+ console.log = originalLog;
203
+ console.warn = originalWarn;
204
+ console.error = originalError;
205
+ }
206
+ }
207
+ async function emitProgress(observer, event) {
208
+ if (!observer)
209
+ return;
210
+ try {
211
+ await observer(event);
212
+ }
213
+ catch {
214
+ // Progress observers are transport-only and must not affect execution.
215
+ }
216
+ }
217
+ async function emitFailureProgress(observer, phase, sessionRoot, error) {
218
+ await emitProgress(observer, {
219
+ version: 1,
220
+ eventKind: "failure",
221
+ phase,
222
+ status: "failed",
223
+ sessionId: sessionIdFromRoot(sessionRoot),
224
+ sessionRoot,
225
+ message: error instanceof Error ? error.message : String(error),
226
+ failure: {
227
+ message: error instanceof Error ? error.message : String(error),
228
+ retrySafety: "unknown",
229
+ },
230
+ });
231
+ }
232
+ function sessionIdFromRoot(sessionRoot) {
233
+ return sessionRoot ? path.basename(path.resolve(sessionRoot)) : null;
234
+ }
235
+ function normalizeLegacyRecordStatus(status) {
236
+ if (status === "completed" ||
237
+ status === "completed_with_degradation" ||
238
+ status === "halted_partial") {
239
+ return status;
240
+ }
241
+ if (status === "degraded")
242
+ return "completed_with_degradation";
243
+ if (status === "failed" || status === "halted")
244
+ return "halted_partial";
245
+ return null;
246
+ }
247
+ export async function prepareReviewInvocationArgv(argv, observer) {
248
+ await emitProgress(observer, {
249
+ version: 1,
250
+ eventKind: "phase",
251
+ phase: "resolve",
252
+ status: "started",
253
+ sessionId: null,
254
+ sessionRoot: null,
255
+ message: "Resolving review invocation.",
256
+ });
257
+ const setup = await resolveReviewInvokeSetup(argv);
258
+ await emitProgress(observer, {
259
+ version: 1,
260
+ eventKind: "phase",
261
+ phase: "resolve",
262
+ status: "completed",
263
+ sessionId: null,
264
+ sessionRoot: null,
265
+ message: "Resolved review invocation.",
266
+ });
267
+ await emitProgress(observer, {
268
+ version: 1,
269
+ eventKind: "phase",
270
+ phase: "prepare",
271
+ status: "started",
272
+ sessionId: null,
273
+ sessionRoot: null,
274
+ message: "Preparing review session.",
275
+ });
276
+ const startResult = await startReviewSession(setup.startArgv);
277
+ const sessionRoot = path.resolve(startResult.session_root);
278
+ await emitProgress(observer, {
279
+ version: 1,
280
+ eventKind: "artifact",
281
+ phase: "prepare",
282
+ status: "completed",
283
+ sessionId: sessionIdFromRoot(sessionRoot),
284
+ sessionRoot,
285
+ artifactRef: path.join(sessionRoot, "execution-plan.yaml"),
286
+ message: "Prepared review session.",
287
+ });
288
+ return {
289
+ prepare_only: true,
290
+ session_root: sessionRoot,
291
+ request_text: setup.resolvedInvokeInputs.requestText,
292
+ execution_realization: setup.executionProfile.execution_realization,
293
+ host_runtime: setup.executionProfile.host_runtime,
294
+ review_mode: setup.resolvedInvokeInputs.reviewMode,
295
+ };
296
+ }
297
+ export async function runReviewInvocationArgv(argv, observer) {
298
+ const setup = await resolveReviewInvokeSetup(argv);
299
+ const resolvedProjectRoot = path.resolve(readSingleOptionValueFromArgv(setup.startArgv, "project-root") ?? ".");
300
+ const hasExplicitExecutorOverride = readSingleOptionValueFromArgv(argv, "executor-realization") !== undefined ||
301
+ readSingleOptionValueFromArgv(argv, "executor-bin") !== undefined ||
302
+ readSingleOptionValueFromArgv(argv, "synthesize-executor-realization") !== undefined ||
303
+ readSingleOptionValueFromArgv(argv, "synthesize-executor-bin") !== undefined;
304
+ const effectiveReviewExecutionProfile = setup.executionProfile.review_execution_profile;
305
+ await emitProgress(observer, {
306
+ version: 1,
307
+ eventKind: "phase",
308
+ phase: "prepare",
309
+ status: "started",
310
+ sessionId: null,
311
+ sessionRoot: null,
312
+ message: "Starting review session preparation.",
313
+ });
314
+ const startResult = await startReviewSession(setup.startArgv);
315
+ const sessionRoot = path.resolve(startResult.session_root);
316
+ await emitProgress(observer, {
317
+ version: 1,
318
+ eventKind: "artifact",
319
+ phase: "prepare",
320
+ status: "completed",
321
+ sessionId: sessionIdFromRoot(sessionRoot),
322
+ sessionRoot,
323
+ artifactRef: path.join(sessionRoot, "execution-plan.yaml"),
324
+ message: "Review session prepared.",
325
+ });
326
+ await ensureProviderRouteReadyForDispatch({
327
+ sessionRoot,
328
+ executionPlanPath: path.join(sessionRoot, "execution-plan.yaml"),
329
+ reviewExecutionProfile: effectiveReviewExecutionProfile,
330
+ });
331
+ const defaultExecutorConfig = resolveExecutorConfig(argv, "", setup.ontoConfig, setup.ontoHome, hasExplicitExecutorOverride
332
+ ? undefined
333
+ : setup.executionProfile.review_execution_profile, "lens");
334
+ const teamleadExecutorConfig = resolveExecutorConfig(argv, "", setup.ontoConfig, setup.ontoHome, hasExplicitExecutorOverride
335
+ ? undefined
336
+ : setup.executionProfile.review_execution_profile, "teamlead");
337
+ const synthesizeExecutorConfig = resolveExecutorConfig(argv, "synthesize-", setup.ontoConfig, setup.ontoHome, hasExplicitExecutorOverride
338
+ ? undefined
339
+ : setup.executionProfile.review_execution_profile, "synthesize");
340
+ await emitProgress(observer, {
341
+ version: 1,
342
+ eventKind: "phase",
343
+ phase: "execute",
344
+ status: "started",
345
+ sessionId: sessionIdFromRoot(sessionRoot),
346
+ sessionRoot,
347
+ message: "Starting review prompt execution.",
348
+ });
349
+ const promptExecutionResult = await executeReviewPromptExecution({
350
+ projectRoot: resolvedProjectRoot,
351
+ sessionRoot,
352
+ defaultExecutorConfig,
353
+ ...(teamleadExecutorConfig.bin === defaultExecutorConfig.bin &&
354
+ JSON.stringify(teamleadExecutorConfig.args) ===
355
+ JSON.stringify(defaultExecutorConfig.args)
356
+ ? {}
357
+ : { teamleadExecutorConfig }),
358
+ ...(synthesizeExecutorConfig.bin === defaultExecutorConfig.bin &&
359
+ JSON.stringify(synthesizeExecutorConfig.args) ===
360
+ JSON.stringify(defaultExecutorConfig.args)
361
+ ? {}
362
+ : { synthesizeExecutorConfig }),
363
+ reviewExecutionProfile: effectiveReviewExecutionProfile,
364
+ ontoConfig: setup.ontoConfig,
365
+ });
366
+ await emitProgress(observer, {
367
+ version: 1,
368
+ eventKind: "artifact",
369
+ phase: "execute",
370
+ status: "completed",
371
+ sessionId: sessionIdFromRoot(sessionRoot),
372
+ sessionRoot,
373
+ artifactRef: path.join(sessionRoot, "execution-result.yaml"),
374
+ message: "Review prompt execution completed.",
375
+ });
376
+ await emitProgress(observer, {
377
+ version: 1,
378
+ eventKind: "phase",
379
+ phase: "complete",
380
+ status: "started",
381
+ sessionId: sessionIdFromRoot(sessionRoot),
382
+ sessionRoot,
383
+ message: "Assembling review record.",
384
+ });
385
+ await completeReviewSession([
386
+ "--project-root",
387
+ resolvedProjectRoot,
388
+ "--session-root",
389
+ sessionRoot,
390
+ "--request-text",
391
+ setup.resolvedInvokeInputs.requestText,
392
+ ]);
393
+ await emitProgress(observer, {
394
+ version: 1,
395
+ eventKind: "artifact",
396
+ phase: "complete",
397
+ status: "completed",
398
+ sessionId: sessionIdFromRoot(sessionRoot),
399
+ sessionRoot,
400
+ artifactRef: path.join(sessionRoot, "review-record.yaml"),
401
+ message: "Review record assembled.",
402
+ });
403
+ const output = await projectLegacyReviewInvocationOutput({
404
+ setup,
405
+ sessionRoot,
406
+ resolvedProjectRoot,
407
+ promptExecutionResult,
408
+ defaultExecutorConfig,
409
+ });
410
+ await emitProgress(observer, {
411
+ version: 1,
412
+ eventKind: "artifact",
413
+ phase: "project",
414
+ status: "completed",
415
+ sessionId: sessionIdFromRoot(sessionRoot),
416
+ sessionRoot,
417
+ artifactRef: output.review_result.final_output_path,
418
+ message: "Review invocation result projected.",
419
+ });
420
+ return output;
421
+ }
422
+ async function projectLegacyReviewInvocationOutput(args) {
423
+ const reviewSummary = await readOptionalReviewSummary(args.sessionRoot);
424
+ const boundedInvokeSteps = [
425
+ "review:start-session",
426
+ "review:run-prompt-execution",
427
+ "review:complete-session",
428
+ ];
429
+ const effectiveReviewExecutionProfile = args.setup.executionProfile.review_execution_profile;
430
+ const finalRoute = buildReviewExecutionRoute(effectiveReviewExecutionProfile);
431
+ const routeProfile = {
432
+ ...args.setup.executionProfile,
433
+ execution_realization: finalRoute.execution_realization,
434
+ host_runtime: finalRoute.artifact_host_runtime,
435
+ review_execution_profile: effectiveReviewExecutionProfile,
436
+ };
437
+ const routeSummary = {
438
+ combined_entrypoint: "review:invoke",
439
+ bounded_invoke_steps: [...boundedInvokeSteps],
440
+ execution_realization: routeProfile.execution_realization,
441
+ host_runtime: routeProfile.host_runtime,
442
+ review_execution_profile: {
443
+ mode: routeProfile.review_execution_profile.mode,
444
+ teamlead_seat: routeProfile.review_execution_profile.teamlead.seat,
445
+ lens_seat: routeProfile.review_execution_profile.lens.seat,
446
+ synthesize_seat: routeProfile.review_execution_profile.synthesize.seat,
447
+ worker_executor: routeProfile.review_execution_profile.worker_executor,
448
+ deliberation: routeProfile.review_execution_profile.deliberation,
449
+ runtime_route: {
450
+ execution_realization: finalRoute.execution_realization,
451
+ host_runtime: finalRoute.artifact_host_runtime,
452
+ worker_executor: finalRoute.executor,
453
+ runtime_provider: finalRoute.resolved_provider,
454
+ auth_mode: finalRoute.auth_mode,
455
+ },
456
+ ...(routeProfile.review_execution_profile.model
457
+ ? { model: routeProfile.review_execution_profile.model }
458
+ : {}),
459
+ ...(routeProfile.review_execution_profile.effort
460
+ ? { effort: routeProfile.review_execution_profile.effort }
461
+ : {}),
462
+ ...(routeProfile.review_execution_profile.service_tier
463
+ ? { service_tier: routeProfile.review_execution_profile.service_tier }
464
+ : {}),
465
+ },
466
+ review_mode: args.setup.resolvedInvokeInputs.reviewMode,
467
+ max_concurrent_lenses: args.setup.maxConcurrentLenses,
468
+ concurrency_strategy: "all_lenses_parallel",
469
+ synthesize_waits_for_all_lenses: true,
470
+ };
471
+ const finalOutputPath = reviewSummary.binding?.final_output_path ??
472
+ path.join(args.sessionRoot, "final-output.md");
473
+ const reviewRecordPath = reviewSummary.binding?.review_record_path ??
474
+ path.join(args.sessionRoot, "review-record.yaml");
475
+ const executionResultPath = reviewSummary.binding?.execution_result_path ??
476
+ path.join(args.sessionRoot, "execution-result.yaml");
477
+ const reviewRunManifestPath = path.join(args.sessionRoot, "review-run-manifest.yaml");
478
+ const participatingLensIds = reviewSummary.reviewRecord?.participating_lens_ids ??
479
+ args.promptExecutionResult.participating_lens_ids;
480
+ const degradedLensIds = reviewSummary.reviewRecord?.degraded_lens_ids ??
481
+ args.promptExecutionResult.degraded_lens_ids;
482
+ const recordStatus = normalizeLegacyRecordStatus(reviewSummary.reviewRecord?.record_status ??
483
+ reviewSummary.executionResult?.execution_status ??
484
+ null);
485
+ const deliberationStatus = reviewSummary.reviewRecord?.deliberation_status ??
486
+ reviewSummary.executionResult?.deliberation_status ??
487
+ null;
488
+ const haltSummary = reviewSummary.executionResult?.halt_reason || args.promptExecutionResult.halt_reason
489
+ ? {
490
+ reason: reviewSummary.executionResult?.halt_reason ??
491
+ args.promptExecutionResult.halt_reason ??
492
+ null,
493
+ phase: reviewSummary.executionResult?.halt_phase ??
494
+ args.promptExecutionResult.halt_phase ??
495
+ null,
496
+ unit_id: reviewSummary.executionResult?.halt_unit_id ??
497
+ args.promptExecutionResult.halt_unit_id ??
498
+ null,
499
+ unit_kind: reviewSummary.executionResult?.halt_unit_kind ??
500
+ args.promptExecutionResult.halt_unit_kind ??
501
+ null,
502
+ lens_id: reviewSummary.executionResult?.halt_lens_id ??
503
+ args.promptExecutionResult.halt_lens_id ??
504
+ null,
505
+ }
506
+ : null;
507
+ const executionSummary = {
508
+ status: recordStatus,
509
+ deliberation_status: deliberationStatus,
510
+ halt: haltSummary,
511
+ review_mode: args.setup.resolvedInvokeInputs.reviewMode,
512
+ lens: {
513
+ participating_count: participatingLensIds.length,
514
+ degraded_count: degradedLensIds.length,
515
+ participating_lens_ids: participatingLensIds,
516
+ degraded_lens_ids: degradedLensIds,
517
+ },
518
+ executor: {
519
+ max_concurrent_lenses: args.setup.maxConcurrentLenses,
520
+ concurrency_strategy: "all_lenses_parallel",
521
+ realization: inferExecutorRealization(args.defaultExecutorConfig),
522
+ profile: routeSummary.review_execution_profile,
523
+ },
524
+ };
525
+ const artifactRefs = {
526
+ session_root: args.sessionRoot,
527
+ final_output: finalOutputPath,
528
+ review_record: reviewRecordPath,
529
+ execution_result: executionResultPath,
530
+ review_run_manifest: reviewRunManifestPath,
531
+ };
532
+ const closureSummary = await readReviewResultClosureSummary(args.sessionRoot);
533
+ const explanationSummary = await readReviewResultExplanationSummary(finalOutputPath);
534
+ const resultOverview = {
535
+ outcome: {
536
+ status: recordStatus,
537
+ deliberation_status: deliberationStatus,
538
+ halt: haltSummary,
539
+ review_mode: args.setup.resolvedInvokeInputs.reviewMode,
540
+ },
541
+ scope: {
542
+ target: args.setup.resolvedInvokeInputs.requestedTarget,
543
+ target_scope_kind: args.setup.resolvedInvokeInputs.targetScopeKind,
544
+ domain: args.setup.resolvedInvokeInputs.domainFinalValue,
545
+ domain_selection_reason: args.setup.resolvedInvokeInputs.domainSelectionReason,
546
+ },
547
+ coverage: {
548
+ planned_lens_count: args.setup.resolvedInvokeInputs.resolvedLensIds.length,
549
+ participating_lens_count: participatingLensIds.length,
550
+ degraded_lens_count: degradedLensIds.length,
551
+ participating_lens_ids: participatingLensIds,
552
+ degraded_lens_ids: degradedLensIds,
553
+ },
554
+ explanation: {
555
+ final_review_result: explanationSummary.final_review_result,
556
+ },
557
+ issues: closureSummary,
558
+ artifacts: artifactRefs,
559
+ };
560
+ return {
561
+ summary: executionSummary,
562
+ result_overview: resultOverview,
563
+ entrypoint_plan: {
564
+ entrypoint: "review",
565
+ target: args.setup.resolvedInvokeInputs.requestedTarget,
566
+ target_scope_kind: args.setup.resolvedInvokeInputs.targetScopeKind,
567
+ resolved_target_refs: args.setup.resolvedInvokeInputs.resolvedTargetRefs,
568
+ request_text: args.setup.resolvedInvokeInputs.requestText,
569
+ requested_domain_token: args.setup.resolvedInvokeInputs.requestedDomainToken.length > 0
570
+ ? args.setup.resolvedInvokeInputs.requestedDomainToken
571
+ : null,
572
+ domain_selection_required: args.setup.resolvedInvokeInputs.domainSelectionRequired,
573
+ domain_selection_mode: args.setup.resolvedInvokeInputs.domainSelectionMode,
574
+ domain_selection_reason: args.setup.resolvedInvokeInputs.domainSelectionReason,
575
+ domain_final_value: args.setup.resolvedInvokeInputs.domainFinalValue,
576
+ review_mode: args.setup.resolvedInvokeInputs.reviewMode,
577
+ },
578
+ route_summary: routeSummary,
579
+ artifacts: artifactRefs,
580
+ review_result: {
581
+ session_root: args.sessionRoot,
582
+ final_output_path: finalOutputPath,
583
+ review_record_path: reviewRecordPath,
584
+ execution_result_path: executionResultPath,
585
+ review_run_manifest_path: reviewRunManifestPath,
586
+ record_status: recordStatus,
587
+ deliberation_status: deliberationStatus,
588
+ halt_reason: haltSummary?.reason ?? null,
589
+ halt_phase: haltSummary?.phase ?? null,
590
+ halt_unit_id: haltSummary?.unit_id ?? null,
591
+ halt_unit_kind: haltSummary?.unit_kind ?? null,
592
+ halt_lens_id: haltSummary?.lens_id ?? null,
593
+ participating_lens_ids: participatingLensIds,
594
+ degraded_lens_ids: degradedLensIds,
595
+ summary: executionSummary,
596
+ },
597
+ bounded_invoke_steps: [...boundedInvokeSteps],
598
+ completion: {
599
+ status: recordStatus,
600
+ final_output_path: finalOutputPath,
601
+ review_record_path: reviewRecordPath,
602
+ },
603
+ };
604
+ }
@@ -153,7 +153,7 @@ async function classifyRef(ref) {
153
153
  maxEntries: 200,
154
154
  maxDepth: 3,
155
155
  });
156
- const aggregated = aggregateDetections(childDetections);
156
+ const aggregated = aggregateTargetMaterialDetections(childDetections);
157
157
  return {
158
158
  ref: resolved,
159
159
  exists: true,
@@ -184,7 +184,7 @@ async function classifyRef(ref) {
184
184
  };
185
185
  }
186
186
  }
187
- function aggregateDetections(detections) {
187
+ export function aggregateTargetMaterialDetections(detections) {
188
188
  if (detections.length === 0) {
189
189
  return {
190
190
  target_material_kind: "unknown",
@@ -234,9 +234,47 @@ function aggregateDetections(detections) {
234
234
  }
235
235
  export async function detectTargetMaterialKind(refs) {
236
236
  const detections = await Promise.all(refs.map(classifyRef));
237
- return aggregateDetections(detections);
237
+ return aggregateTargetMaterialDetections(detections);
238
+ }
239
+ export async function detectTargetMaterialRefs(refs) {
240
+ const detections = [];
241
+ for (const ref of refs) {
242
+ const resolved = path.resolve(ref);
243
+ try {
244
+ const stat = await fs.stat(resolved);
245
+ if (stat.isDirectory()) {
246
+ const childDetections = await collectDirectoryMaterialDetections({
247
+ root: resolved,
248
+ maxEntries: 200,
249
+ maxDepth: 3,
250
+ });
251
+ detections.push(...childDetections);
252
+ if (childDetections.length === 0) {
253
+ detections.push({
254
+ ref: resolved,
255
+ exists: true,
256
+ kind: "unknown",
257
+ confidence: 0.1,
258
+ confidence_basis: "empty or unreadable directory inventory",
259
+ });
260
+ }
261
+ continue;
262
+ }
263
+ }
264
+ catch {
265
+ // classifyRef preserves fail-loud existence metadata for missing refs.
266
+ }
267
+ detections.push(await classifyRef(resolved));
268
+ }
269
+ return detections;
238
270
  }
239
271
  export function reviewMaterialSupportStatus(kind) {
272
+ if (kind === "code") {
273
+ return {
274
+ status: "supported",
275
+ reason: null,
276
+ };
277
+ }
240
278
  if (kind === "unknown") {
241
279
  return {
242
280
  status: "unknown",
@@ -245,8 +283,8 @@ export function reviewMaterialSupportStatus(kind) {
245
283
  }
246
284
  if (kind === "mixed") {
247
285
  return {
248
- status: "partial",
249
- reason: "review records mixed material kind, but per-member material-specific validation is not implemented yet",
286
+ status: "partial_composite",
287
+ reason: "review records per-member material kinds for a mixed target, but cross-material validation remains partial",
250
288
  };
251
289
  }
252
290
  return {