onto-mcp 0.3.2 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.onto/processes/reconstruct/actionable-ontology-seed-recomposition-design.md +447 -0
- package/.onto/processes/reconstruct/foundry-style-ontology-seed-contract.md +934 -0
- package/.onto/processes/reconstruct/reconstruct-boundary-contract.md +303 -725
- package/.onto/processes/reconstruct/reconstruct-contract-registry.yaml +1645 -0
- package/.onto/processes/reconstruct/reconstruct-execution-ux-contract.md +26 -22
- package/.onto/processes/reconstruct/source-profile-contract.md +49 -23
- package/.onto/processes/reconstruct/source-profiles/code.md +6 -3
- package/.onto/processes/reconstruct/source-profiles/database.md +5 -2
- package/.onto/processes/reconstruct/source-profiles/document.md +5 -2
- package/.onto/processes/reconstruct/source-profiles/spreadsheet.md +5 -4
- package/.onto/processes/review/review-execution-ux-contract.md +40 -0
- package/.onto/processes/shared/pipeline-execution-ledger-contract.md +26 -10
- package/.onto/processes/shared/target-material-kind-contract.md +29 -16
- package/AGENTS.md +6 -4
- package/README.md +135 -76
- package/dist/cli.js +8 -8
- package/dist/core-api/reconstruct-api.js +117 -31
- package/dist/core-api/review-api.js +47 -0
- package/dist/core-runtime/cli/codex-review-unit-executor.js +39 -2
- package/dist/core-runtime/cli/complete-review-session.js +2 -2
- package/dist/core-runtime/cli/mock-review-unit-executor.js +1 -1
- package/dist/core-runtime/cli/review-invoke.js +9 -9
- package/dist/core-runtime/cli/run-review-prompt-execution.js +39 -5
- package/dist/core-runtime/cli/spawn-watcher.js +266 -47
- package/dist/core-runtime/cli/start-review-session.js +3 -3
- package/dist/core-runtime/llm/llm-caller.js +11 -0
- package/dist/core-runtime/llm/llm-tool-loop.js +2 -0
- package/dist/core-runtime/observability/runtime-stream-observation.js +118 -0
- package/dist/core-runtime/onboard/cli-host.js +149 -0
- package/dist/core-runtime/onboard/host-target.js +22 -0
- package/dist/core-runtime/onboard/json-config-host.js +122 -0
- package/dist/core-runtime/onboard/path-scan.js +26 -0
- package/dist/core-runtime/onboard/prompt.js +51 -0
- package/dist/core-runtime/onboard/register.js +207 -0
- package/dist/core-runtime/onboard/types.js +27 -0
- package/dist/core-runtime/reconstruct/actionable-seed-validation.js +1777 -0
- package/dist/core-runtime/reconstruct/artifact-types.js +10 -4
- package/dist/core-runtime/reconstruct/contract-registry.js +623 -0
- package/dist/core-runtime/reconstruct/domain-id.js +10 -0
- package/dist/core-runtime/reconstruct/governing-snapshot.js +716 -0
- package/dist/core-runtime/reconstruct/material-profile-validation.js +191 -0
- package/dist/core-runtime/reconstruct/materialize-preparation.js +49 -11
- package/dist/core-runtime/reconstruct/pipeline-execution-ledger.js +269 -79
- package/dist/core-runtime/reconstruct/post-seed-validation.js +1194 -51
- package/dist/core-runtime/reconstruct/record.js +104 -20
- package/dist/core-runtime/reconstruct/run.js +2107 -413
- package/dist/core-runtime/reconstruct/seed-claim-projections.js +268 -0
- package/dist/core-runtime/reconstruct/source-profiles.js +93 -4
- package/dist/core-runtime/reconstruct/terminal-validation.js +807 -0
- package/dist/core-runtime/review/review-invocation-runner.js +4 -4
- package/dist/mcp/server.js +110 -38
- package/dist/mcp/tool-schemas.js +20 -6
- package/package.json +8 -17
- package/scripts/onto-review-watch.sh +486 -0
- package/scripts/onto-runtime-watch.sh +122 -0
- package/scripts/postinstall-hint.js +22 -0
- package/.onto/processes/reconstruct/top-level-concept-discovery-contract.md +0 -387
- package/dist/core-runtime/cli/bootstrap-review-binding.js +0 -186
- package/dist/core-runtime/cli/codex-nested-dispatch.test.js +0 -390
- package/dist/core-runtime/cli/codex-nested-teamlead-executor.test.js +0 -335
- package/dist/core-runtime/cli/coordinator-helpers.js +0 -583
- package/dist/core-runtime/cli/coordinator-state-machine-deliberation.test.js +0 -167
- package/dist/core-runtime/cli/coordinator-state-machine.js +0 -794
- package/dist/core-runtime/cli/e2e-codex-multi-agent-fixes.test.js +0 -615
- package/dist/core-runtime/cli/e2e-start-review-session.test.js +0 -312
- package/dist/core-runtime/cli/health.js +0 -44
- package/dist/core-runtime/cli/inline-http-review-unit-executor.test.js +0 -567
- package/dist/core-runtime/cli/materialize-review-execution-preparation.js +0 -104
- package/dist/core-runtime/cli/migrate-session-roots.js +0 -118
- package/dist/core-runtime/cli/repo-layout-migration-replace.smoke.test.js +0 -106
- package/dist/core-runtime/cli/review-invoke-auto-resolution.test.js +0 -268
- package/dist/core-runtime/cli/review-invoke-coordinator-topology.test.js +0 -136
- package/dist/core-runtime/cli/review-invoke-resolver-caching.test.js +0 -201
- package/dist/core-runtime/cli/review-invoke-topology-dispatch.test.js +0 -192
- package/dist/core-runtime/cli/session-root-guard.js +0 -168
- package/dist/core-runtime/cli/spawn-watcher.test.js +0 -457
- package/dist/core-runtime/cli/strip-wrapping-code-fence.test.js +0 -79
- package/dist/core-runtime/cli/teamcreate-lens-deliberation-executor.js +0 -412
- package/dist/core-runtime/cli/teamcreate-lens-deliberation-executor.test.js +0 -351
- package/dist/core-runtime/cli/topology-executor-mapping.js +0 -139
- package/dist/core-runtime/cli/topology-executor-mapping.test.js +0 -173
- package/dist/core-runtime/cli/write-review-interpretation.js +0 -81
- package/dist/core-runtime/config/onto-config-cli.js +0 -278
- package/dist/core-runtime/config/onto-config-key-path.js +0 -288
- package/dist/core-runtime/config/onto-config-key-path.test.js +0 -195
- package/dist/core-runtime/config/onto-config-preview.js +0 -108
- package/dist/core-runtime/config/onto-config-preview.test.js +0 -132
- package/dist/core-runtime/discovery/config-chain.js +0 -118
- package/dist/core-runtime/discovery/config-chain.test.js +0 -103
- package/dist/core-runtime/discovery/config-profile.js +0 -199
- package/dist/core-runtime/discovery/config-profile.test.js +0 -233
- package/dist/core-runtime/discovery/host-detection.test.js +0 -186
- package/dist/core-runtime/discovery/installation-paths.test.js +0 -65
- package/dist/core-runtime/discovery/lens-registry.test.js +0 -81
- package/dist/core-runtime/discovery/path-normalization.test.js +0 -22
- package/dist/core-runtime/discovery/plugin-path.js +0 -72
- package/dist/core-runtime/discovery/plugin-path.test.js +0 -95
- package/dist/core-runtime/evolve/adapters/code-product/compile/compile-defense.js +0 -344
- package/dist/core-runtime/evolve/adapters/code-product/compile/compile-defense.test.js +0 -915
- package/dist/core-runtime/evolve/adapters/code-product/compile/compile.js +0 -564
- package/dist/core-runtime/evolve/adapters/code-product/compile/compile.test.js +0 -708
- package/dist/core-runtime/evolve/adapters/code-product/parsers/brief-parser.js +0 -165
- package/dist/core-runtime/evolve/adapters/code-product/parsers/brief-parser.test.js +0 -227
- package/dist/core-runtime/evolve/adapters/code-product/validators/validate.js +0 -59
- package/dist/core-runtime/evolve/adapters/code-product/validators/validate.test.js +0 -205
- package/dist/core-runtime/evolve/adapters/methodology/adapter.js +0 -16
- package/dist/core-runtime/evolve/adapters/methodology/adapter.test.js +0 -9
- package/dist/core-runtime/evolve/adapters/methodology/perspectives/authority-consistency.js +0 -298
- package/dist/core-runtime/evolve/adapters/methodology/perspectives/authority-consistency.test.js +0 -70
- package/dist/core-runtime/evolve/adapters/methodology/scope-types/process.js +0 -46
- package/dist/core-runtime/evolve/adapters/methodology/scope-types/process.test.js +0 -73
- package/dist/core-runtime/evolve/adapters/registry.js +0 -47
- package/dist/core-runtime/evolve/adapters/registry.test.js +0 -67
- package/dist/core-runtime/evolve/cli.js +0 -256
- package/dist/core-runtime/evolve/commands/align.js +0 -194
- package/dist/core-runtime/evolve/commands/align.test.js +0 -82
- package/dist/core-runtime/evolve/commands/apply.js +0 -161
- package/dist/core-runtime/evolve/commands/apply.test.js +0 -138
- package/dist/core-runtime/evolve/commands/close.js +0 -39
- package/dist/core-runtime/evolve/commands/close.test.js +0 -99
- package/dist/core-runtime/evolve/commands/defer.js +0 -40
- package/dist/core-runtime/evolve/commands/defer.test.js +0 -134
- package/dist/core-runtime/evolve/commands/draft.js +0 -323
- package/dist/core-runtime/evolve/commands/draft.test.js +0 -178
- package/dist/core-runtime/evolve/commands/e2e-evolve-full-cycle.test.js +0 -208
- package/dist/core-runtime/evolve/commands/error-messages.js +0 -125
- package/dist/core-runtime/evolve/commands/error-messages.test.js +0 -167
- package/dist/core-runtime/evolve/commands/propose-align.js +0 -222
- package/dist/core-runtime/evolve/commands/propose-align.test.js +0 -136
- package/dist/core-runtime/evolve/commands/reconstruct.js +0 -330
- package/dist/core-runtime/evolve/commands/reconstruct.test.js +0 -278
- package/dist/core-runtime/evolve/commands/shared.js +0 -22
- package/dist/core-runtime/evolve/commands/stale-check.js +0 -103
- package/dist/core-runtime/evolve/commands/stale-check.test.js +0 -84
- package/dist/core-runtime/evolve/commands/start.js +0 -887
- package/dist/core-runtime/evolve/commands/start.test.js +0 -396
- package/dist/core-runtime/evolve/config/project-config.js +0 -99
- package/dist/core-runtime/evolve/config/project-config.test.js +0 -170
- package/dist/core-runtime/evolve/renderers/align-packet.js +0 -280
- package/dist/core-runtime/evolve/renderers/align-packet.test.js +0 -332
- package/dist/core-runtime/evolve/renderers/draft-packet.js +0 -303
- package/dist/core-runtime/evolve/renderers/draft-packet.test.js +0 -377
- package/dist/core-runtime/evolve/renderers/format.js +0 -5
- package/dist/core-runtime/evolve/renderers/scope-md.js +0 -237
- package/dist/core-runtime/evolve/renderers/scope-md.test.js +0 -306
- package/dist/core-runtime/govern/cli.js +0 -369
- package/dist/core-runtime/govern/cli.test.js +0 -314
- package/dist/core-runtime/govern/drift-engine.js +0 -103
- package/dist/core-runtime/govern/drift-engine.test.js +0 -319
- package/dist/core-runtime/govern/promote-principle.js +0 -206
- package/dist/core-runtime/govern/promote-principle.test.js +0 -368
- package/dist/core-runtime/govern/queue.js +0 -81
- package/dist/core-runtime/govern/types.js +0 -16
- package/dist/core-runtime/install/cli.js +0 -530
- package/dist/core-runtime/install/detect.js +0 -128
- package/dist/core-runtime/install/detect.test.js +0 -155
- package/dist/core-runtime/install/gitignore-update.js +0 -74
- package/dist/core-runtime/install/gitignore-update.test.js +0 -64
- package/dist/core-runtime/install/install-integration.test.js +0 -373
- package/dist/core-runtime/install/prompts.js +0 -389
- package/dist/core-runtime/install/prompts.test.js +0 -293
- package/dist/core-runtime/install/types.js +0 -26
- package/dist/core-runtime/install/validation.js +0 -295
- package/dist/core-runtime/install/validation.test.js +0 -313
- package/dist/core-runtime/install/writer.js +0 -254
- package/dist/core-runtime/install/writer.test.js +0 -218
- package/dist/core-runtime/learning/extractor.js +0 -461
- package/dist/core-runtime/learning/feedback.js +0 -179
- package/dist/core-runtime/learning/health-report.js +0 -165
- package/dist/core-runtime/learning/health-report.test.js +0 -169
- package/dist/core-runtime/learning/loader.js +0 -388
- package/dist/core-runtime/learning/loader.test.js +0 -102
- package/dist/core-runtime/learning/promote/apply-state.js +0 -240
- package/dist/core-runtime/learning/promote/audit-obligation.js +0 -195
- package/dist/core-runtime/learning/promote/collector.js +0 -432
- package/dist/core-runtime/learning/promote/degraded-state.js +0 -125
- package/dist/core-runtime/learning/promote/domain-doc-proposer.js +0 -166
- package/dist/core-runtime/learning/promote/e2e-promote.test.js +0 -6385
- package/dist/core-runtime/learning/promote/health-snapshot.js +0 -150
- package/dist/core-runtime/learning/promote/insight-reclassifier.js +0 -544
- package/dist/core-runtime/learning/promote/judgment-auditor.js +0 -517
- package/dist/core-runtime/learning/promote/panel-reviewer.js +0 -1158
- package/dist/core-runtime/learning/promote/promote-executor.js +0 -1675
- package/dist/core-runtime/learning/promote/promoter.js +0 -307
- package/dist/core-runtime/learning/promote/retirement.js +0 -122
- package/dist/core-runtime/learning/promote/types.js +0 -23
- package/dist/core-runtime/learning/prompt-sections.js +0 -51
- package/dist/core-runtime/learning/shared/artifact-registry-init.js +0 -45
- package/dist/core-runtime/learning/shared/artifact-registry.js +0 -254
- package/dist/core-runtime/learning/shared/audit-obligation-kernel.js +0 -73
- package/dist/core-runtime/learning/shared/audit-state.js +0 -99
- package/dist/core-runtime/learning/shared/duplicate-check.js +0 -28
- package/dist/core-runtime/learning/shared/llm-caller.js +0 -831
- package/dist/core-runtime/learning/shared/llm-caller.test.js +0 -601
- package/dist/core-runtime/learning/shared/llm-tool-loop.js +0 -393
- package/dist/core-runtime/learning/shared/mode.js +0 -25
- package/dist/core-runtime/learning/shared/paths.js +0 -84
- package/dist/core-runtime/learning/shared/paths.test.js +0 -79
- package/dist/core-runtime/learning/shared/patterns.js +0 -37
- package/dist/core-runtime/learning/shared/recoverability.js +0 -355
- package/dist/core-runtime/learning/shared/recovery-context.js +0 -374
- package/dist/core-runtime/learning/shared/scope.js +0 -1
- package/dist/core-runtime/learning/shared/semantic-classifier.js +0 -94
- package/dist/core-runtime/learning/shared/specs/apply-execution-state-spec.js +0 -42
- package/dist/core-runtime/learning/shared/specs/audit-state-spec.js +0 -37
- package/dist/core-runtime/learning/shared/specs/backup-metadata-spec.js +0 -39
- package/dist/core-runtime/learning/shared/specs/emergency-log-spec.js +0 -41
- package/dist/core-runtime/learning/shared/specs/layout-version-spec.js +0 -38
- package/dist/core-runtime/learning/shared/specs/promote-decisions-spec.js +0 -43
- package/dist/core-runtime/learning/shared/specs/promote-report-spec.js +0 -113
- package/dist/core-runtime/learning/shared/specs/prune-log-spec.js +0 -36
- package/dist/core-runtime/learning/shared/specs/recovery-resolution-spec.js +0 -48
- package/dist/core-runtime/learning/shared/specs/restore-manifest-spec.js +0 -43
- package/dist/core-runtime/learning/shared/specs/spec-helpers.js +0 -64
- package/dist/core-runtime/learning/usage-tracker.js +0 -190
- package/dist/core-runtime/learning/usage-tracker.test.js +0 -176
- package/dist/core-runtime/onboard/detect-review-axes.js +0 -122
- package/dist/core-runtime/onboard/detect-review-axes.test.js +0 -127
- package/dist/core-runtime/onboard/write-review-block.js +0 -188
- package/dist/core-runtime/onboard/write-review-block.test.js +0 -240
- package/dist/core-runtime/readers/brownfield-builder.js +0 -150
- package/dist/core-runtime/readers/brownfield-builder.test.js +0 -136
- package/dist/core-runtime/readers/code-chunk-collector.js +0 -53
- package/dist/core-runtime/readers/code-chunk-collector.test.js +0 -136
- package/dist/core-runtime/readers/file-utils.js +0 -240
- package/dist/core-runtime/readers/file-utils.test.js +0 -146
- package/dist/core-runtime/readers/lexicon-citation-check.js +0 -93
- package/dist/core-runtime/readers/lexicon-citation-check.test.js +0 -77
- package/dist/core-runtime/readers/mcp-figma.js +0 -30
- package/dist/core-runtime/readers/mcp-figma.test.js +0 -82
- package/dist/core-runtime/readers/mcp-generic.js +0 -31
- package/dist/core-runtime/readers/mcp-generic.test.js +0 -76
- package/dist/core-runtime/readers/ontology-index.js +0 -148
- package/dist/core-runtime/readers/ontology-index.test.js +0 -245
- package/dist/core-runtime/readers/ontology-query.js +0 -168
- package/dist/core-runtime/readers/ontology-query.test.js +0 -311
- package/dist/core-runtime/readers/ontology-resolve.js +0 -48
- package/dist/core-runtime/readers/ontology-resolve.test.js +0 -48
- package/dist/core-runtime/readers/patterns/index.js +0 -7
- package/dist/core-runtime/readers/review-log.js +0 -213
- package/dist/core-runtime/readers/review-log.test.js +0 -313
- package/dist/core-runtime/readers/scan-local.js +0 -102
- package/dist/core-runtime/readers/scan-local.test.js +0 -102
- package/dist/core-runtime/readers/scan-tarball.js +0 -121
- package/dist/core-runtime/readers/scan-tarball.test.js +0 -283
- package/dist/core-runtime/readers/scan-vault.js +0 -34
- package/dist/core-runtime/readers/scan-vault.test.js +0 -81
- package/dist/core-runtime/readers/types.js +0 -42
- package/dist/core-runtime/readers/types.test.js +0 -94
- package/dist/core-runtime/readers/viewpoint-collectors.js +0 -229
- package/dist/core-runtime/reconstruct/seed-candidate-validation.js +0 -385
- package/dist/core-runtime/review/citation-audit.test.js +0 -165
- package/dist/core-runtime/review/execution-plan-resolver.js +0 -247
- package/dist/core-runtime/review/execution-plan-resolver.test.js +0 -243
- package/dist/core-runtime/review/execution-topology-resolver-axis-first.test.js +0 -246
- package/dist/core-runtime/review/execution-topology-resolver.js +0 -401
- package/dist/core-runtime/review/execution-topology-resolver.test.js +0 -315
- package/dist/core-runtime/review/inline-context-embedder.test.js +0 -154
- package/dist/core-runtime/review/legacy-mode-policy.js +0 -88
- package/dist/core-runtime/review/materializers-effort-persist.test.js +0 -79
- package/dist/core-runtime/review/ontology-path-classifier.js +0 -179
- package/dist/core-runtime/review/ontology-path-classifier.test.js +0 -216
- package/dist/core-runtime/review/packet-boundary-policy.test.js +0 -107
- package/dist/core-runtime/review/participating-lens-paths.test.js +0 -73
- package/dist/core-runtime/review/review-config-legacy-translate.js +0 -244
- package/dist/core-runtime/review/review-config-legacy-translate.test.js +0 -161
- package/dist/core-runtime/review/review-config-validator.js +0 -289
- package/dist/core-runtime/review/review-config-validator.test.js +0 -236
- package/dist/core-runtime/review/shape-pipeline-audit.test.js +0 -311
- package/dist/core-runtime/review/shape-to-topology-id.js +0 -117
- package/dist/core-runtime/review/shape-to-topology-id.test.js +0 -132
- package/dist/core-runtime/review/topology-shape-derivation.js +0 -155
- package/dist/core-runtime/review/topology-shape-derivation.test.js +0 -195
- package/dist/core-runtime/scope-runtime/constants.js +0 -12
- package/dist/core-runtime/scope-runtime/constraint-pool.js +0 -166
- package/dist/core-runtime/scope-runtime/constraint-pool.test.js +0 -674
- package/dist/core-runtime/scope-runtime/domain-validation-log.js +0 -135
- package/dist/core-runtime/scope-runtime/domain-validation-log.test.js +0 -156
- package/dist/core-runtime/scope-runtime/eval-persistence.js +0 -65
- package/dist/core-runtime/scope-runtime/eval-persistence.test.js +0 -84
- package/dist/core-runtime/scope-runtime/event-pipeline.js +0 -64
- package/dist/core-runtime/scope-runtime/event-pipeline.test.js +0 -450
- package/dist/core-runtime/scope-runtime/event-store.js +0 -39
- package/dist/core-runtime/scope-runtime/event-store.test.js +0 -95
- package/dist/core-runtime/scope-runtime/gate-guard.js +0 -348
- package/dist/core-runtime/scope-runtime/gate-guard.test.js +0 -1047
- package/dist/core-runtime/scope-runtime/hash.js +0 -4
- package/dist/core-runtime/scope-runtime/hash.test.js +0 -33
- package/dist/core-runtime/scope-runtime/id.js +0 -4
- package/dist/core-runtime/scope-runtime/id.test.js +0 -17
- package/dist/core-runtime/scope-runtime/reducer.js +0 -297
- package/dist/core-runtime/scope-runtime/reducer.test.js +0 -759
- package/dist/core-runtime/scope-runtime/scope-manager.js +0 -161
- package/dist/core-runtime/scope-runtime/state-machine.js +0 -309
- package/dist/core-runtime/scope-runtime/state-machine.test.js +0 -704
- package/dist/core-runtime/scope-runtime/types.js +0 -116
- package/dist/core-runtime/scope-runtime/types.test.js +0 -69
- package/dist/core-runtime/translate/render-for-user.js +0 -169
- package/dist/core-runtime/translate/render-for-user.test.js +0 -122
- package/dist/providers/capability-contract.js +0 -1
|
@@ -1,165 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest";
|
|
2
|
-
import { auditCitations, extractSignificantQuotes } from "./citation-audit.js";
|
|
3
|
-
describe("extractSignificantQuotes", () => {
|
|
4
|
-
it("extracts double-quoted strings above threshold", () => {
|
|
5
|
-
const text = 'The lens said "Top-level value lacks operational backing" in evidence.';
|
|
6
|
-
const quotes = extractSignificantQuotes(text);
|
|
7
|
-
expect(quotes.map((q) => q.text)).toContain("Top-level value lacks operational backing");
|
|
8
|
-
});
|
|
9
|
-
it("extracts backtick code spans above threshold", () => {
|
|
10
|
-
const text = "The evidence is at `src/core-runtime/scope-runtime/state-machine.ts` in the module.";
|
|
11
|
-
const quotes = extractSignificantQuotes(text);
|
|
12
|
-
expect(quotes.map((q) => q.text)).toContain("src/core-runtime/scope-runtime/state-machine.ts");
|
|
13
|
-
});
|
|
14
|
-
it("skips quotes below threshold (default 20)", () => {
|
|
15
|
-
const text = 'The status is "performed" for now, file `x.ts`.';
|
|
16
|
-
const quotes = extractSignificantQuotes(text).map((q) => q.text);
|
|
17
|
-
expect(quotes).not.toContain("performed");
|
|
18
|
-
expect(quotes).not.toContain("x.ts");
|
|
19
|
-
});
|
|
20
|
-
it("respects a custom threshold", () => {
|
|
21
|
-
const text = "File `x.ts` matters.";
|
|
22
|
-
const quotes = extractSignificantQuotes(text, { minQuoteLength: 3 });
|
|
23
|
-
expect(quotes.map((q) => q.text)).toContain("x.ts");
|
|
24
|
-
});
|
|
25
|
-
it("de-duplicates identical quotes", () => {
|
|
26
|
-
const text = '"Declaration without operational backing" appears here and "Declaration without operational backing" again.';
|
|
27
|
-
const quotes = extractSignificantQuotes(text);
|
|
28
|
-
expect(quotes.filter((q) => q.text === "Declaration without operational backing").length).toBe(1);
|
|
29
|
-
});
|
|
30
|
-
it("handles multi-line quote bodies (single newline tolerated)", () => {
|
|
31
|
-
const text = '"Line one of a long citation\nthat wraps across one linebreak"';
|
|
32
|
-
const quotes = extractSignificantQuotes(text);
|
|
33
|
-
expect(quotes[0]?.text.includes("Line one")).toBe(true);
|
|
34
|
-
});
|
|
35
|
-
it("does not extract from triple-backtick code fences as code spans", () => {
|
|
36
|
-
const text = "Before\n```ts\nconst x = 1;\n```\nAfter";
|
|
37
|
-
const quotes = extractSignificantQuotes(text);
|
|
38
|
-
expect(quotes.length).toBe(0);
|
|
39
|
-
});
|
|
40
|
-
it("returns empty array when no quoted strings present", () => {
|
|
41
|
-
const text = "Pure prose with no quotations or backtick spans above threshold.";
|
|
42
|
-
expect(extractSignificantQuotes(text)).toEqual([]);
|
|
43
|
-
});
|
|
44
|
-
});
|
|
45
|
-
describe("attribution classification", () => {
|
|
46
|
-
it("classifies `<lens>:` colon attribution", () => {
|
|
47
|
-
const text = 'axiology: "Declaration without operational grounding detected"';
|
|
48
|
-
const [q] = extractSignificantQuotes(text);
|
|
49
|
-
expect(q?.is_attribution).toBe(true);
|
|
50
|
-
expect(q?.attributed_lens).toBe("axiology");
|
|
51
|
-
});
|
|
52
|
-
it("classifies `**<lens>**:` bold attribution", () => {
|
|
53
|
-
const text = '**logic**: "Circular dependency across module boundary"';
|
|
54
|
-
const [q] = extractSignificantQuotes(text);
|
|
55
|
-
expect(q?.is_attribution).toBe(true);
|
|
56
|
-
expect(q?.attributed_lens).toBe("logic");
|
|
57
|
-
});
|
|
58
|
-
it("classifies verb attribution (said / reports / claims)", () => {
|
|
59
|
-
const texts = [
|
|
60
|
-
'Axiology said "purpose-value misalignment in executor"',
|
|
61
|
-
"Logic reports \"tier-level equivalence cannot be decided\"",
|
|
62
|
-
'Semantics claims "synonymous terms not consolidated"',
|
|
63
|
-
];
|
|
64
|
-
for (const text of texts) {
|
|
65
|
-
const [q] = extractSignificantQuotes(text);
|
|
66
|
-
expect(q?.is_attribution).toBe(true);
|
|
67
|
-
}
|
|
68
|
-
});
|
|
69
|
-
it("classifies `<Lens> lens said` phrasing", () => {
|
|
70
|
-
const text = 'Coverage lens notes "sub-area missing from scope"';
|
|
71
|
-
const [q] = extractSignificantQuotes(text);
|
|
72
|
-
expect(q?.is_attribution).toBe(true);
|
|
73
|
-
expect(q?.attributed_lens).toBe("coverage");
|
|
74
|
-
});
|
|
75
|
-
it("classifies `per <lens>` / `from <lens>` / `according to <lens>` prefix", () => {
|
|
76
|
-
const texts = [
|
|
77
|
-
'per logic, "tier-equivalence can be demonstrated structurally"',
|
|
78
|
-
'from structure: "isolated node in dependency graph found"',
|
|
79
|
-
'according to pragmatics: "usage context narrows applicability"',
|
|
80
|
-
];
|
|
81
|
-
for (const text of texts) {
|
|
82
|
-
const [q] = extractSignificantQuotes(text);
|
|
83
|
-
expect(q?.is_attribution).toBe(true);
|
|
84
|
-
}
|
|
85
|
-
});
|
|
86
|
-
it("does NOT classify a bare quote without a nearby lens reference", () => {
|
|
87
|
-
const text = 'The synthesis includes a "conditional consensus" section per the canonical taxonomy.';
|
|
88
|
-
const [q] = extractSignificantQuotes(text);
|
|
89
|
-
expect(q?.is_attribution).toBe(false);
|
|
90
|
-
expect(q?.attributed_lens).toBeUndefined();
|
|
91
|
-
});
|
|
92
|
-
it("does NOT leak attribution across sentence boundaries", () => {
|
|
93
|
-
// Lens reference is in the previous sentence; quote here is a bare concept
|
|
94
|
-
// label, not an attributed citation.
|
|
95
|
-
const text = 'Axiology covered the purpose axis. Elsewhere the report discusses "conditional consensus" as a taxonomy label.';
|
|
96
|
-
const quotes = extractSignificantQuotes(text);
|
|
97
|
-
const taxonomy = quotes.find((q) => q.text === "conditional consensus");
|
|
98
|
-
expect(taxonomy?.is_attribution).toBe(false);
|
|
99
|
-
});
|
|
100
|
-
});
|
|
101
|
-
describe("auditCitations", () => {
|
|
102
|
-
it("flags an attribution-style fabrication in quotes_unmatched (the A3 case)", () => {
|
|
103
|
-
const synthesize = "### Disagreement\n" +
|
|
104
|
-
'- Axiology said "Naming alignment between value, activity, and concept is not yet resolved".';
|
|
105
|
-
const axiologyLens = "Findings: Top-level value 시간/비용 최소화 lacks operational backing in executor.";
|
|
106
|
-
const depLens = "Authority chain integrity circular between core-lexicon.yaml and .onto/principles/.";
|
|
107
|
-
const structLens = "Naming alignment resolved as of W-A-77.";
|
|
108
|
-
const result = auditCitations(synthesize, [axiologyLens, depLens, structLens]);
|
|
109
|
-
expect(result.quotes_checked).toBe(1);
|
|
110
|
-
expect(result.attribution_count).toBe(1);
|
|
111
|
-
expect(result.quotes_unmatched).toContain("Naming alignment between value, activity, and concept is not yet resolved");
|
|
112
|
-
expect(result.quotes_unmatched_meta).toEqual([]);
|
|
113
|
-
});
|
|
114
|
-
it("routes non-attribution unmatched quotes to quotes_unmatched_meta (taxonomy labels)", () => {
|
|
115
|
-
const synthesize = 'The section is titled "conditional consensus" per the canonical taxonomy, and separately "cross-process deliberation" appears as a meta-concept.';
|
|
116
|
-
const lensA = "Irrelevant lens body without any taxonomy phrase.";
|
|
117
|
-
const result = auditCitations(synthesize, [lensA]);
|
|
118
|
-
// Both taxonomy labels are above threshold 20 and unmatched.
|
|
119
|
-
expect(result.quotes_unmatched).toEqual([]);
|
|
120
|
-
expect(result.quotes_unmatched_meta.length).toBeGreaterThanOrEqual(1);
|
|
121
|
-
// Neither was an attribution citation.
|
|
122
|
-
expect(result.attribution_count).toBe(0);
|
|
123
|
-
});
|
|
124
|
-
it("passes an attribution-style quote that substring-matches a lens", () => {
|
|
125
|
-
const synthesize = 'axiology: "Top-level value 시간/비용 최소화 lacks operational backing"';
|
|
126
|
-
const lensContent = "Findings: Top-level value 시간/비용 최소화 lacks operational backing in executor selection.";
|
|
127
|
-
const result = auditCitations(synthesize, [lensContent]);
|
|
128
|
-
expect(result.quotes_checked).toBe(1);
|
|
129
|
-
expect(result.attribution_count).toBe(1);
|
|
130
|
-
expect(result.quotes_unmatched).toEqual([]);
|
|
131
|
-
expect(result.quotes_unmatched_meta).toEqual([]);
|
|
132
|
-
});
|
|
133
|
-
it("matches against any lens (disjunction)", () => {
|
|
134
|
-
const synthesize = 'logic: "unique phrase of sufficient length here"';
|
|
135
|
-
const lensA = "not related content at all";
|
|
136
|
-
const lensB = "some prose containing unique phrase of sufficient length here inside it";
|
|
137
|
-
const result = auditCitations(synthesize, [lensA, lensB]);
|
|
138
|
-
expect(result.quotes_unmatched).toEqual([]);
|
|
139
|
-
expect(result.quotes_unmatched_meta).toEqual([]);
|
|
140
|
-
});
|
|
141
|
-
it("reports empty unmatched lists when no quotes present", () => {
|
|
142
|
-
const result = auditCitations("Pure synthesis prose.", ["some lens content"]);
|
|
143
|
-
expect(result.quotes_checked).toBe(0);
|
|
144
|
-
expect(result.quotes_unmatched).toEqual([]);
|
|
145
|
-
expect(result.quotes_unmatched_meta).toEqual([]);
|
|
146
|
-
expect(result.attribution_count).toBe(0);
|
|
147
|
-
});
|
|
148
|
-
it("includes min_quote_length in result for traceability", () => {
|
|
149
|
-
const result = auditCitations("", [], { minQuoteLength: 30 });
|
|
150
|
-
expect(result.min_quote_length).toBe(30);
|
|
151
|
-
});
|
|
152
|
-
it("uses default threshold 20 when options omitted", () => {
|
|
153
|
-
const result = auditCitations("", []);
|
|
154
|
-
expect(result.min_quote_length).toBe(20);
|
|
155
|
-
});
|
|
156
|
-
it("empty lens pool: attribution unmatched → strict, non-attribution → meta", () => {
|
|
157
|
-
const synthesize = 'logic: "attribution style phrase of sufficient length for threshold"\n' +
|
|
158
|
-
'Separately, "non-attributed taxonomy label phrase here" appears.';
|
|
159
|
-
const result = auditCitations(synthesize, []);
|
|
160
|
-
expect(result.quotes_checked).toBe(2);
|
|
161
|
-
expect(result.attribution_count).toBe(1);
|
|
162
|
-
expect(result.quotes_unmatched).toContain("attribution style phrase of sufficient length for threshold");
|
|
163
|
-
expect(result.quotes_unmatched_meta).toContain("non-attributed taxonomy label phrase here");
|
|
164
|
-
});
|
|
165
|
-
});
|
|
@@ -1,247 +0,0 @@
|
|
|
1
|
-
/** Resolves the concrete review execution plan from host signals and llm config. */
|
|
2
|
-
import fs from "node:fs";
|
|
3
|
-
import os from "node:os";
|
|
4
|
-
import path from "node:path";
|
|
5
|
-
import { detectCodexBinaryAvailable } from "../discovery/host-detection.js";
|
|
6
|
-
import { normalizeLlmModelSwitcher, } from "../llm/model-switcher.js";
|
|
7
|
-
// ---------------------------------------------------------------------------
|
|
8
|
-
// Constants
|
|
9
|
-
// ---------------------------------------------------------------------------
|
|
10
|
-
const DEFAULT_TIMEOUT_MS = Number(process.env.ONTO_LLM_TIMEOUT_MS) || 120_000;
|
|
11
|
-
const DEFAULT_MAX_ATTEMPTS = 2;
|
|
12
|
-
// ---------------------------------------------------------------------------
|
|
13
|
-
// Observability
|
|
14
|
-
// ---------------------------------------------------------------------------
|
|
15
|
-
/**
|
|
16
|
-
* Plan-level STDERR log. Mirrors `[provider-ladder]` and `[model-call]`
|
|
17
|
-
* patterns (PR #91 / PR #93) so operators can reconstruct the full decision
|
|
18
|
-
* sequence from a single STDERR capture.
|
|
19
|
-
*
|
|
20
|
-
* No suppressor env var: decision rationale is load-bearing for review
|
|
21
|
-
* reproducibility. Tests capture via vi.spyOn(process.stderr, "write").
|
|
22
|
-
*/
|
|
23
|
-
function emitPlanLog(line) {
|
|
24
|
-
process.stderr.write(`[plan] ${line}\n`);
|
|
25
|
-
}
|
|
26
|
-
function readCodexAuthState() {
|
|
27
|
-
const codexAuthPath = path.join(os.homedir(), ".codex", "auth.json");
|
|
28
|
-
if (!fs.existsSync(codexAuthPath)) {
|
|
29
|
-
return { chatgptOAuth: false, openaiApiKey: null };
|
|
30
|
-
}
|
|
31
|
-
try {
|
|
32
|
-
const auth = JSON.parse(fs.readFileSync(codexAuthPath, "utf8"));
|
|
33
|
-
const oauth = auth.auth_mode === "chatgpt" ||
|
|
34
|
-
(auth.tokens && typeof auth.tokens.access_token === "string");
|
|
35
|
-
const openaiKey = typeof auth.OPENAI_API_KEY === "string" && auth.OPENAI_API_KEY.length > 0
|
|
36
|
-
? auth.OPENAI_API_KEY
|
|
37
|
-
: null;
|
|
38
|
-
return { chatgptOAuth: Boolean(oauth), openaiApiKey: openaiKey };
|
|
39
|
-
}
|
|
40
|
-
catch {
|
|
41
|
-
return { chatgptOAuth: false, openaiApiKey: null };
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
// ---------------------------------------------------------------------------
|
|
45
|
-
// Ladder resolution
|
|
46
|
-
// ---------------------------------------------------------------------------
|
|
47
|
-
/**
|
|
48
|
-
* Compute the ExecutionPlan by walking the context-separation ladder.
|
|
49
|
-
*
|
|
50
|
-
* Priority (higher rank = preferred, matches sketch §3.2):
|
|
51
|
-
* P0 Mock (ONTO_LLM_MOCK=1) — test only
|
|
52
|
-
* P1a Explicit --codex CLI flag — subprocess (S0)
|
|
53
|
-
* P1f env ONTO_HOST_RUNTIME override — any rank
|
|
54
|
-
* P2 Auto-detected Claude Code host (CLAUDECODE=1) — host nested spawn (S2)
|
|
55
|
-
* P3 Auto-detected codex binary + auth.json — subprocess (S0)
|
|
56
|
-
* P4 llm auth/provider switcher — external HTTP (S1) or codex OAuth
|
|
57
|
-
* Fail-fast when nothing is viable.
|
|
58
|
-
*
|
|
59
|
-
* The `review:` axis block owns topology. The `llm` switcher owns provider
|
|
60
|
-
* and auth selection. Missing viable execution input returns `no_host`.
|
|
61
|
-
*/
|
|
62
|
-
export function resolveExecutionPlan(args) {
|
|
63
|
-
const env = args.env ?? process.env;
|
|
64
|
-
const claudeHost = args.claudeHost ?? env.CLAUDECODE === "1";
|
|
65
|
-
const codexAvailable = args.codexAvailable ?? detectCodexBinaryAvailable();
|
|
66
|
-
const trace = [];
|
|
67
|
-
const log = (line) => {
|
|
68
|
-
emitPlanLog(line);
|
|
69
|
-
trace.push(line);
|
|
70
|
-
};
|
|
71
|
-
const retry_policy = {
|
|
72
|
-
timeout_ms: DEFAULT_TIMEOUT_MS,
|
|
73
|
-
max_attempts: DEFAULT_MAX_ATTEMPTS,
|
|
74
|
-
backoff: "exponential",
|
|
75
|
-
};
|
|
76
|
-
// P0: Mock — test envelope.
|
|
77
|
-
if (env.ONTO_LLM_MOCK === "1") {
|
|
78
|
-
log("P0 mock: matched (ONTO_LLM_MOCK=1)");
|
|
79
|
-
return {
|
|
80
|
-
type: "resolved",
|
|
81
|
-
plan: {
|
|
82
|
-
separation_rank: "S3",
|
|
83
|
-
execution_realization: "mock",
|
|
84
|
-
host_runtime: "mock",
|
|
85
|
-
provider_identity: "mock",
|
|
86
|
-
retry_policy,
|
|
87
|
-
plan_trace: trace,
|
|
88
|
-
},
|
|
89
|
-
};
|
|
90
|
-
}
|
|
91
|
-
// P1a: Explicit --codex flag. Overrides all other inputs.
|
|
92
|
-
if (args.explicitCodex) {
|
|
93
|
-
log("P1 explicit-codex: matched (--codex flag)");
|
|
94
|
-
return resolveCodexPlan(log, trace, retry_policy, args.ontoConfig);
|
|
95
|
-
}
|
|
96
|
-
// P1f: env ONTO_HOST_RUNTIME override.
|
|
97
|
-
const envHost = env.ONTO_HOST_RUNTIME?.trim().toLowerCase();
|
|
98
|
-
if (envHost === "codex") {
|
|
99
|
-
log("P1 env-override: ONTO_HOST_RUNTIME=codex");
|
|
100
|
-
return resolveCodexPlan(log, trace, retry_policy, args.ontoConfig);
|
|
101
|
-
}
|
|
102
|
-
if (envHost === "claude") {
|
|
103
|
-
log("P1 env-override: ONTO_HOST_RUNTIME=claude → agent-teams");
|
|
104
|
-
return {
|
|
105
|
-
type: "resolved",
|
|
106
|
-
plan: {
|
|
107
|
-
separation_rank: "S2",
|
|
108
|
-
execution_realization: "agent-teams",
|
|
109
|
-
host_runtime: "claude",
|
|
110
|
-
provider_identity: "claude-code",
|
|
111
|
-
retry_policy,
|
|
112
|
-
plan_trace: trace,
|
|
113
|
-
},
|
|
114
|
-
};
|
|
115
|
-
}
|
|
116
|
-
if (envHost === "anthropic" || envHost === "openai" || envHost === "grok" || envHost === "lmstudio") {
|
|
117
|
-
log(`P1 env-override: ONTO_HOST_RUNTIME=${envHost} → ts_inline_http`);
|
|
118
|
-
return resolveExternalHttpPlan(log, trace, retry_policy, args.ontoConfig, { forcedProvider: envHost });
|
|
119
|
-
}
|
|
120
|
-
if (envHost === "standalone") {
|
|
121
|
-
log("P1 env-override: ONTO_HOST_RUNTIME=standalone → ts_inline_http");
|
|
122
|
-
return resolveExternalHttpPlan(log, trace, retry_policy, args.ontoConfig);
|
|
123
|
-
}
|
|
124
|
-
// P2: Auto-detected Claude Code host (stay-in-host).
|
|
125
|
-
if (claudeHost) {
|
|
126
|
-
log("P2 auto: claudeHost=true → agent-teams / host-nested-spawn");
|
|
127
|
-
return {
|
|
128
|
-
type: "resolved",
|
|
129
|
-
plan: {
|
|
130
|
-
separation_rank: "S2",
|
|
131
|
-
execution_realization: "agent-teams",
|
|
132
|
-
host_runtime: "claude",
|
|
133
|
-
provider_identity: "claude-code",
|
|
134
|
-
retry_policy,
|
|
135
|
-
plan_trace: trace,
|
|
136
|
-
},
|
|
137
|
-
};
|
|
138
|
-
}
|
|
139
|
-
// P3: Auto-detected codex. Auth content is validated at invocation time.
|
|
140
|
-
if (codexAvailable) {
|
|
141
|
-
log("P3 auto: codex binary + auth.json present → subprocess");
|
|
142
|
-
return resolveCodexPlan(log, trace, retry_policy, args.ontoConfig);
|
|
143
|
-
}
|
|
144
|
-
log("P3 auto: codex binary unavailable → skip");
|
|
145
|
-
// P4: canonical llm switcher.
|
|
146
|
-
const llmSelection = normalizeLlmModelSwitcher(args.ontoConfig.llm);
|
|
147
|
-
if (llmSelection) {
|
|
148
|
-
if (llmSelection.provider === "codex") {
|
|
149
|
-
log("P4 llm switcher: auth=oauth provider=openai → codex subprocess");
|
|
150
|
-
return resolveCodexPlan(log, trace, retry_policy, args.ontoConfig);
|
|
151
|
-
}
|
|
152
|
-
log(`P4 llm switcher: auth=${llmSelection.auth} provider=${llmSelection.provider} → ts_inline_http`);
|
|
153
|
-
return resolveExternalHttpPlan(log, trace, retry_policy, args.ontoConfig);
|
|
154
|
-
}
|
|
155
|
-
log("final: no viable path → no_host");
|
|
156
|
-
return {
|
|
157
|
-
type: "no_host",
|
|
158
|
-
plan_trace: trace,
|
|
159
|
-
reason: buildNoHostReason(),
|
|
160
|
-
};
|
|
161
|
-
}
|
|
162
|
-
// ---------------------------------------------------------------------------
|
|
163
|
-
// Sub-resolvers
|
|
164
|
-
// ---------------------------------------------------------------------------
|
|
165
|
-
function resolveCodexPlan(log, trace, retry_policy, config) {
|
|
166
|
-
const selection = normalizeLlmModelSwitcher(config.llm);
|
|
167
|
-
const modelId = selection?.provider === "codex" ? selection.model_id : undefined;
|
|
168
|
-
log(`codex plan: separation_rank=S0 executor=subprocess model_id=${modelId ?? "(codex default)"}`);
|
|
169
|
-
return {
|
|
170
|
-
type: "resolved",
|
|
171
|
-
plan: {
|
|
172
|
-
separation_rank: "S0",
|
|
173
|
-
execution_realization: "subagent",
|
|
174
|
-
host_runtime: "codex",
|
|
175
|
-
provider_identity: "codex",
|
|
176
|
-
...(modelId ? { model_id: modelId } : {}),
|
|
177
|
-
retry_policy,
|
|
178
|
-
plan_trace: trace,
|
|
179
|
-
},
|
|
180
|
-
};
|
|
181
|
-
}
|
|
182
|
-
function resolveExternalHttpPlan(log, trace, retry_policy, config, opts) {
|
|
183
|
-
const providerField = pickExternalProviderField(config, opts);
|
|
184
|
-
if (!providerField.provider) {
|
|
185
|
-
log("external-http: no provider identified (llm.provider unset)");
|
|
186
|
-
return {
|
|
187
|
-
type: "no_host",
|
|
188
|
-
plan_trace: trace,
|
|
189
|
-
reason: buildMissingExternalProviderReason(),
|
|
190
|
-
};
|
|
191
|
-
}
|
|
192
|
-
const provider = providerField.provider;
|
|
193
|
-
const selection = normalizeLlmModelSwitcher(config.llm);
|
|
194
|
-
const modelId = selection?.model_id;
|
|
195
|
-
const base_url = selection?.base_url;
|
|
196
|
-
log(`external-http plan: provider=${provider} source=${providerField.source} model_id=${modelId ?? "(unresolved)"} base_url=${base_url ?? "(default)"}`);
|
|
197
|
-
return {
|
|
198
|
-
type: "resolved",
|
|
199
|
-
plan: {
|
|
200
|
-
separation_rank: "S1",
|
|
201
|
-
execution_realization: "ts_inline_http",
|
|
202
|
-
host_runtime: provider,
|
|
203
|
-
provider_identity: provider,
|
|
204
|
-
...(modelId ? { model_id: modelId } : {}),
|
|
205
|
-
...(base_url ? { base_url } : {}),
|
|
206
|
-
retry_policy,
|
|
207
|
-
plan_trace: trace,
|
|
208
|
-
},
|
|
209
|
-
};
|
|
210
|
-
}
|
|
211
|
-
function pickExternalProviderField(config, opts) {
|
|
212
|
-
if (opts?.forcedProvider) {
|
|
213
|
-
return { provider: opts.forcedProvider, source: "forced (env ONTO_HOST_RUNTIME)" };
|
|
214
|
-
}
|
|
215
|
-
const selection = normalizeLlmModelSwitcher(config.llm);
|
|
216
|
-
if (selection && selection.provider !== "codex") {
|
|
217
|
-
return { provider: selection.provider, source: "llm" };
|
|
218
|
-
}
|
|
219
|
-
return { provider: null, source: "(none)" };
|
|
220
|
-
}
|
|
221
|
-
// ---------------------------------------------------------------------------
|
|
222
|
-
// Error messages
|
|
223
|
-
// ---------------------------------------------------------------------------
|
|
224
|
-
function buildNoHostReason() {
|
|
225
|
-
return [
|
|
226
|
-
"Review execution plan을 해소할 수 없습니다.",
|
|
227
|
-
"현재 감지 결과: Claude Code 세션 아님(CLAUDECODE unset), codex 바이너리 또는 ~/.codex/auth.json 부재, llm switcher 미설정.",
|
|
228
|
-
"",
|
|
229
|
-
"다음 중 한 가지로 해결하세요:",
|
|
230
|
-
" 1. Claude Code 세션에서 `onto review` 재실행",
|
|
231
|
-
" 2. codex CLI 설치 + `codex login` 후 재실행",
|
|
232
|
-
" 3. `--codex` 플래그로 codex subprocess 강제",
|
|
233
|
-
" 4. `.onto/config.yml` 에 `review:` block 추가 (`.onto/processes/configuration.md` 참고)",
|
|
234
|
-
" 5. `.onto/config.yml` 에 llm: { auth, provider, model } 설정",
|
|
235
|
-
" 6. local 실행은 llm.auth=local + llm.provider=lmstudio 로 설정",
|
|
236
|
-
].join("\n");
|
|
237
|
-
}
|
|
238
|
-
function buildMissingExternalProviderReason() {
|
|
239
|
-
return [
|
|
240
|
-
"LLM switcher 미지정.",
|
|
241
|
-
"`.onto/config.yml` 에 llm block 을 추가하세요:",
|
|
242
|
-
" llm:",
|
|
243
|
-
" auth: api_key",
|
|
244
|
-
" provider: anthropic # 또는 openai | grok | lmstudio",
|
|
245
|
-
" model: <model-id>",
|
|
246
|
-
].join("\n");
|
|
247
|
-
}
|
|
@@ -1,243 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
|
|
2
|
-
import { resolveExecutionPlan, } from "./execution-plan-resolver.js";
|
|
3
|
-
// ---------------------------------------------------------------------------
|
|
4
|
-
// These tests assert two invariants of the unified ExecutionPlan resolver:
|
|
5
|
-
//
|
|
6
|
-
// (1) Every decision point writes a trace line, so operators can reconstruct
|
|
7
|
-
// the ladder walk from STDERR + plan_trace alone. "Silent divergence" —
|
|
8
|
-
// the failure mode that made review fail 5 times on 2026-04-17~18 — is
|
|
9
|
-
// structurally impossible when every branch logs.
|
|
10
|
-
//
|
|
11
|
-
// (2) The returned ExecutionPlan shape is stable: separation_rank,
|
|
12
|
-
// execution_realization, host_runtime, and provider_identity are always
|
|
13
|
-
// populated together; callers can rely on this quadruple without
|
|
14
|
-
// defensive undefined checks.
|
|
15
|
-
// ---------------------------------------------------------------------------
|
|
16
|
-
const BASE_CONFIG = {};
|
|
17
|
-
function expectResolved(resolution) {
|
|
18
|
-
if (resolution.type !== "resolved") {
|
|
19
|
-
throw new Error(`expected resolved, got no_host: ${resolution.reason.slice(0, 80)}`);
|
|
20
|
-
}
|
|
21
|
-
return resolution;
|
|
22
|
-
}
|
|
23
|
-
function expectNoHost(resolution) {
|
|
24
|
-
if (resolution.type !== "no_host") {
|
|
25
|
-
throw new Error(`expected no_host, got resolved plan with rank=${resolution.plan.separation_rank}`);
|
|
26
|
-
}
|
|
27
|
-
return resolution;
|
|
28
|
-
}
|
|
29
|
-
describe("resolveExecutionPlan — P0 mock", () => {
|
|
30
|
-
it("P0 mock: ONTO_LLM_MOCK=1 short-circuits the ladder at S3", () => {
|
|
31
|
-
const res = resolveExecutionPlan({
|
|
32
|
-
explicitCodex: false,
|
|
33
|
-
ontoConfig: BASE_CONFIG,
|
|
34
|
-
env: { ONTO_LLM_MOCK: "1" },
|
|
35
|
-
claudeHost: false,
|
|
36
|
-
codexAvailable: false,
|
|
37
|
-
});
|
|
38
|
-
const resolved = expectResolved(res);
|
|
39
|
-
expect(resolved.plan.separation_rank).toBe("S3");
|
|
40
|
-
expect(resolved.plan.execution_realization).toBe("mock");
|
|
41
|
-
expect(resolved.plan.provider_identity).toBe("mock");
|
|
42
|
-
expect(resolved.plan.plan_trace[0]).toContain("P0 mock: matched");
|
|
43
|
-
});
|
|
44
|
-
});
|
|
45
|
-
describe("resolveExecutionPlan — P1 explicit", () => {
|
|
46
|
-
it("P1a --codex flag wins over claudeHost auto-detection", () => {
|
|
47
|
-
const res = resolveExecutionPlan({
|
|
48
|
-
explicitCodex: true,
|
|
49
|
-
ontoConfig: {},
|
|
50
|
-
env: {},
|
|
51
|
-
claudeHost: true, // would otherwise match P2
|
|
52
|
-
codexAvailable: false,
|
|
53
|
-
});
|
|
54
|
-
const resolved = expectResolved(res);
|
|
55
|
-
expect(resolved.plan.separation_rank).toBe("S0");
|
|
56
|
-
expect(resolved.plan.host_runtime).toBe("codex");
|
|
57
|
-
expect(resolved.plan.provider_identity).toBe("codex");
|
|
58
|
-
expect(resolved.plan.plan_trace[0]).toContain("P1 explicit-codex");
|
|
59
|
-
});
|
|
60
|
-
it("P1f env ONTO_HOST_RUNTIME=litellm forces ts_inline_http with matching provider", () => {
|
|
61
|
-
const res = resolveExecutionPlan({
|
|
62
|
-
explicitCodex: false,
|
|
63
|
-
ontoConfig: {
|
|
64
|
-
external_http_provider: "litellm",
|
|
65
|
-
llm_base_url: "http://proxy.local",
|
|
66
|
-
litellm: { model: "llama-8b" },
|
|
67
|
-
},
|
|
68
|
-
env: { ONTO_HOST_RUNTIME: "litellm" },
|
|
69
|
-
claudeHost: false,
|
|
70
|
-
codexAvailable: false,
|
|
71
|
-
});
|
|
72
|
-
const resolved = expectResolved(res);
|
|
73
|
-
expect(resolved.plan.separation_rank).toBe("S1");
|
|
74
|
-
expect(resolved.plan.execution_realization).toBe("ts_inline_http");
|
|
75
|
-
expect(resolved.plan.provider_identity).toBe("litellm");
|
|
76
|
-
expect(resolved.plan.base_url).toBe("http://proxy.local");
|
|
77
|
-
});
|
|
78
|
-
});
|
|
79
|
-
describe("resolveExecutionPlan — P2/P3 auto-detection", () => {
|
|
80
|
-
it("P2 claudeHost=true wins over codexAvailable (stay-in-host)", () => {
|
|
81
|
-
const res = resolveExecutionPlan({
|
|
82
|
-
explicitCodex: false,
|
|
83
|
-
ontoConfig: {},
|
|
84
|
-
env: {},
|
|
85
|
-
claudeHost: true,
|
|
86
|
-
codexAvailable: true, // would match P3 if P2 didn't exist
|
|
87
|
-
});
|
|
88
|
-
const resolved = expectResolved(res);
|
|
89
|
-
expect(resolved.plan.host_runtime).toBe("claude");
|
|
90
|
-
expect(resolved.plan.separation_rank).toBe("S2");
|
|
91
|
-
});
|
|
92
|
-
it("P3 codexAvailable=true → S0 subprocess", () => {
|
|
93
|
-
const res = resolveExecutionPlan({
|
|
94
|
-
explicitCodex: false,
|
|
95
|
-
ontoConfig: {},
|
|
96
|
-
env: {},
|
|
97
|
-
claudeHost: false,
|
|
98
|
-
codexAvailable: true,
|
|
99
|
-
});
|
|
100
|
-
const resolved = expectResolved(res);
|
|
101
|
-
expect(resolved.plan.separation_rank).toBe("S0");
|
|
102
|
-
expect(resolved.plan.provider_identity).toBe("codex");
|
|
103
|
-
});
|
|
104
|
-
it("P4 external_http_provider config → ts_inline_http when no host/codex", () => {
|
|
105
|
-
const res = resolveExecutionPlan({
|
|
106
|
-
explicitCodex: false,
|
|
107
|
-
ontoConfig: {
|
|
108
|
-
external_http_provider: "anthropic",
|
|
109
|
-
anthropic: { model: "claude-sonnet-4" },
|
|
110
|
-
},
|
|
111
|
-
env: {},
|
|
112
|
-
claudeHost: false,
|
|
113
|
-
codexAvailable: false,
|
|
114
|
-
});
|
|
115
|
-
const resolved = expectResolved(res);
|
|
116
|
-
expect(resolved.plan.separation_rank).toBe("S1");
|
|
117
|
-
expect(resolved.plan.provider_identity).toBe("anthropic");
|
|
118
|
-
expect(resolved.plan.model_id).toBe("claude-sonnet-4");
|
|
119
|
-
});
|
|
120
|
-
it("no viable path → no_host with reason", () => {
|
|
121
|
-
const res = resolveExecutionPlan({
|
|
122
|
-
explicitCodex: false,
|
|
123
|
-
ontoConfig: {},
|
|
124
|
-
env: {},
|
|
125
|
-
claudeHost: false,
|
|
126
|
-
codexAvailable: false,
|
|
127
|
-
});
|
|
128
|
-
const nohost = expectNoHost(res);
|
|
129
|
-
expect(nohost.reason).toContain("Review execution plan");
|
|
130
|
-
expect(nohost.plan_trace.length).toBeGreaterThan(0);
|
|
131
|
-
expect(nohost.plan_trace.at(-1)).toContain("no viable path");
|
|
132
|
-
});
|
|
133
|
-
});
|
|
134
|
-
describe("resolveExecutionPlan — env override", () => {
|
|
135
|
-
it("ONTO_HOST_RUNTIME=standalone → ts_inline_http + provider lookup", () => {
|
|
136
|
-
const res = resolveExecutionPlan({
|
|
137
|
-
explicitCodex: false,
|
|
138
|
-
ontoConfig: { external_http_provider: "openai", openai: { model: "gpt-5" } },
|
|
139
|
-
env: { ONTO_HOST_RUNTIME: "standalone" },
|
|
140
|
-
claudeHost: true, // env should override claudeHost auto-detection
|
|
141
|
-
codexAvailable: true,
|
|
142
|
-
});
|
|
143
|
-
const resolved = expectResolved(res);
|
|
144
|
-
expect(resolved.plan.execution_realization).toBe("ts_inline_http");
|
|
145
|
-
expect(resolved.plan.provider_identity).toBe("openai");
|
|
146
|
-
expect(resolved.plan.model_id).toBe("gpt-5");
|
|
147
|
-
});
|
|
148
|
-
it("ONTO_HOST_RUNTIME=codex → subprocess even without explicit flag", () => {
|
|
149
|
-
const res = resolveExecutionPlan({
|
|
150
|
-
explicitCodex: false,
|
|
151
|
-
ontoConfig: {},
|
|
152
|
-
env: { ONTO_HOST_RUNTIME: "codex" },
|
|
153
|
-
claudeHost: false,
|
|
154
|
-
codexAvailable: false, // env override ignores auto-detection
|
|
155
|
-
});
|
|
156
|
-
const resolved = expectResolved(res);
|
|
157
|
-
expect(resolved.plan.host_runtime).toBe("codex");
|
|
158
|
-
});
|
|
159
|
-
});
|
|
160
|
-
describe("resolveExecutionPlan — observability", () => {
|
|
161
|
-
let stderrSpy;
|
|
162
|
-
beforeEach(() => {
|
|
163
|
-
stderrSpy = vi.spyOn(process.stderr, "write").mockImplementation(() => true);
|
|
164
|
-
});
|
|
165
|
-
afterEach(() => {
|
|
166
|
-
stderrSpy.mockRestore();
|
|
167
|
-
});
|
|
168
|
-
it("emits [plan] prefix for every decision line", () => {
|
|
169
|
-
resolveExecutionPlan({
|
|
170
|
-
explicitCodex: false,
|
|
171
|
-
ontoConfig: {},
|
|
172
|
-
env: {},
|
|
173
|
-
claudeHost: true,
|
|
174
|
-
codexAvailable: false,
|
|
175
|
-
});
|
|
176
|
-
const calls = stderrSpy.mock.calls.map((c) => String(c[0]));
|
|
177
|
-
const planLines = calls.filter((l) => l.startsWith("[plan]"));
|
|
178
|
-
expect(planLines.length).toBeGreaterThan(0);
|
|
179
|
-
for (const line of planLines) {
|
|
180
|
-
expect(line).toMatch(/^\[plan\] /);
|
|
181
|
-
}
|
|
182
|
-
});
|
|
183
|
-
it("plan_trace matches what was emitted to stderr (single source of truth)", () => {
|
|
184
|
-
const res = resolveExecutionPlan({
|
|
185
|
-
explicitCodex: true,
|
|
186
|
-
ontoConfig: {},
|
|
187
|
-
env: {},
|
|
188
|
-
claudeHost: false,
|
|
189
|
-
codexAvailable: false,
|
|
190
|
-
});
|
|
191
|
-
const resolved = expectResolved(res);
|
|
192
|
-
const stderrLines = stderrSpy.mock.calls
|
|
193
|
-
.map((c) => String(c[0]))
|
|
194
|
-
.filter((l) => l.startsWith("[plan] "))
|
|
195
|
-
.map((l) => l.replace(/^\[plan\] /, "").replace(/\n$/, ""));
|
|
196
|
-
expect(resolved.plan.plan_trace).toEqual(stderrLines);
|
|
197
|
-
});
|
|
198
|
-
it("no_host resolution still surfaces plan_trace for debugging", () => {
|
|
199
|
-
const res = resolveExecutionPlan({
|
|
200
|
-
explicitCodex: false,
|
|
201
|
-
ontoConfig: {},
|
|
202
|
-
env: {},
|
|
203
|
-
claudeHost: false,
|
|
204
|
-
codexAvailable: false,
|
|
205
|
-
});
|
|
206
|
-
const nohost = expectNoHost(res);
|
|
207
|
-
expect(nohost.plan_trace).toBeDefined();
|
|
208
|
-
expect(nohost.plan_trace.length).toBeGreaterThan(0);
|
|
209
|
-
});
|
|
210
|
-
});
|
|
211
|
-
describe("resolveExecutionPlan — provider resolution priority", () => {
|
|
212
|
-
it("external_http_provider > subagent_llm.provider", () => {
|
|
213
|
-
const res = resolveExecutionPlan({
|
|
214
|
-
explicitCodex: false,
|
|
215
|
-
ontoConfig: {
|
|
216
|
-
external_http_provider: "anthropic",
|
|
217
|
-
subagent_llm: { provider: "litellm" }, // loser
|
|
218
|
-
anthropic: { model: "claude-x" },
|
|
219
|
-
},
|
|
220
|
-
env: {},
|
|
221
|
-
claudeHost: false,
|
|
222
|
-
codexAvailable: false,
|
|
223
|
-
});
|
|
224
|
-
const resolved = expectResolved(res);
|
|
225
|
-
expect(resolved.plan.provider_identity).toBe("anthropic");
|
|
226
|
-
});
|
|
227
|
-
});
|
|
228
|
-
describe("resolveExecutionPlan — retry policy", () => {
|
|
229
|
-
it("retry_policy is populated on every resolved plan", () => {
|
|
230
|
-
const res = resolveExecutionPlan({
|
|
231
|
-
explicitCodex: true,
|
|
232
|
-
ontoConfig: {},
|
|
233
|
-
env: {},
|
|
234
|
-
claudeHost: false,
|
|
235
|
-
codexAvailable: false,
|
|
236
|
-
});
|
|
237
|
-
const resolved = expectResolved(res);
|
|
238
|
-
expect(resolved.plan.retry_policy).toBeDefined();
|
|
239
|
-
expect(resolved.plan.retry_policy.timeout_ms).toBeGreaterThan(0);
|
|
240
|
-
expect(resolved.plan.retry_policy.max_attempts).toBeGreaterThanOrEqual(1);
|
|
241
|
-
expect(["exponential", "linear", "none"]).toContain(resolved.plan.retry_policy.backoff);
|
|
242
|
-
});
|
|
243
|
-
});
|