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,176 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
|
2
|
-
import { mkdirSync, writeFileSync, rmSync } from "node:fs";
|
|
3
|
-
import { join } from "node:path";
|
|
4
|
-
import { tmpdir } from "node:os";
|
|
5
|
-
import { stringify } from "yaml";
|
|
6
|
-
import { collectLearningUsage, computeAggregate } from "./usage-tracker.js";
|
|
7
|
-
// ─── Fixture helpers ───
|
|
8
|
-
function makeManifest(overrides = {}) {
|
|
9
|
-
return {
|
|
10
|
-
session_domain: "none",
|
|
11
|
-
agents_loaded: 2,
|
|
12
|
-
total_items_loaded: 90,
|
|
13
|
-
total_items_parsed: 200,
|
|
14
|
-
total_items_skipped: 10,
|
|
15
|
-
degraded: false,
|
|
16
|
-
degradation_reason: null,
|
|
17
|
-
per_agent: [
|
|
18
|
-
{
|
|
19
|
-
agent_id: "logic",
|
|
20
|
-
loaded: 46,
|
|
21
|
-
parsed: 120,
|
|
22
|
-
skipped: 4,
|
|
23
|
-
truncated: 74,
|
|
24
|
-
role_distribution: { guardrail: 15, foundation: 21, convention: 1, unclassified: 9 },
|
|
25
|
-
tier_distribution: { "1": 15, "2": 11, "3": 19, "4": 1 },
|
|
26
|
-
cross_domain_included: 0,
|
|
27
|
-
cross_domain_excluded: 0,
|
|
28
|
-
tokens_used: 6259,
|
|
29
|
-
tokens_budget: 4000,
|
|
30
|
-
budget_truncated_count: 48,
|
|
31
|
-
},
|
|
32
|
-
{
|
|
33
|
-
agent_id: "structure",
|
|
34
|
-
loaded: 44,
|
|
35
|
-
parsed: 80,
|
|
36
|
-
skipped: 6,
|
|
37
|
-
truncated: 36,
|
|
38
|
-
role_distribution: { guardrail: 12, foundation: 4, convention: 0, unclassified: 28 },
|
|
39
|
-
tier_distribution: { "1": 12, "2": 16, "3": 4, "4": 12 },
|
|
40
|
-
cross_domain_included: 0,
|
|
41
|
-
cross_domain_excluded: 0,
|
|
42
|
-
tokens_used: 5666,
|
|
43
|
-
tokens_budget: 4000,
|
|
44
|
-
budget_truncated_count: 32,
|
|
45
|
-
},
|
|
46
|
-
],
|
|
47
|
-
learning_file_paths: [],
|
|
48
|
-
...overrides,
|
|
49
|
-
};
|
|
50
|
-
}
|
|
51
|
-
let testDir;
|
|
52
|
-
function createSessionManifest(reviewRoot, sessionId, manifestOverrides = {}) {
|
|
53
|
-
const prepDir = join(reviewRoot, sessionId, "execution-preparation");
|
|
54
|
-
mkdirSync(prepDir, { recursive: true });
|
|
55
|
-
writeFileSync(join(prepDir, "learning-manifest.yaml"), stringify(makeManifest(manifestOverrides)));
|
|
56
|
-
}
|
|
57
|
-
// ─── Tests ───
|
|
58
|
-
describe("usage-tracker", () => {
|
|
59
|
-
beforeEach(() => {
|
|
60
|
-
testDir = join(tmpdir(), `usage-tracker-test-${Date.now()}-${Math.random().toString(36).slice(2)}`);
|
|
61
|
-
mkdirSync(testDir, { recursive: true });
|
|
62
|
-
});
|
|
63
|
-
afterEach(() => {
|
|
64
|
-
rmSync(testDir, { recursive: true, force: true });
|
|
65
|
-
});
|
|
66
|
-
describe("collectLearningUsage", () => {
|
|
67
|
-
it("빈 디렉터리에서 빈 summary 를 반환한다", () => {
|
|
68
|
-
const result = collectLearningUsage(testDir);
|
|
69
|
-
expect(result.total_sessions).toBe(0);
|
|
70
|
-
expect(result.entries).toHaveLength(0);
|
|
71
|
-
});
|
|
72
|
-
it("존재하지 않는 경로에서 빈 summary 를 반환한다", () => {
|
|
73
|
-
const result = collectLearningUsage("/nonexistent/path");
|
|
74
|
-
expect(result.total_sessions).toBe(0);
|
|
75
|
-
});
|
|
76
|
-
it("단일 세션의 learning manifest 를 수집한다", () => {
|
|
77
|
-
createSessionManifest(testDir, "20260413-aaa00001");
|
|
78
|
-
const result = collectLearningUsage(testDir);
|
|
79
|
-
expect(result.total_sessions).toBe(1);
|
|
80
|
-
const entry = result.entries[0];
|
|
81
|
-
expect(entry.session_id).toBe("20260413-aaa00001");
|
|
82
|
-
expect(entry.total_items_loaded).toBe(90);
|
|
83
|
-
expect(entry.total_items_parsed).toBe(200);
|
|
84
|
-
expect(entry.agents_loaded).toBe(2);
|
|
85
|
-
});
|
|
86
|
-
it("per-agent tier/role 분포를 합산한다", () => {
|
|
87
|
-
createSessionManifest(testDir, "20260413-aaa00001");
|
|
88
|
-
const result = collectLearningUsage(testDir);
|
|
89
|
-
const entry = result.entries[0];
|
|
90
|
-
// logic t1:15 + structure t1:12 = 27
|
|
91
|
-
expect(entry.tier_distribution.t1).toBe(27);
|
|
92
|
-
// logic guardrail:15 + structure guardrail:12 = 27
|
|
93
|
-
expect(entry.role_distribution.guardrail).toBe(27);
|
|
94
|
-
});
|
|
95
|
-
it("tokens_used 와 budget_truncated 를 합산한다", () => {
|
|
96
|
-
createSessionManifest(testDir, "20260413-aaa00001");
|
|
97
|
-
const result = collectLearningUsage(testDir);
|
|
98
|
-
const entry = result.entries[0];
|
|
99
|
-
expect(entry.total_tokens_used).toBe(6259 + 5666);
|
|
100
|
-
expect(entry.total_budget_truncated).toBe(48 + 32);
|
|
101
|
-
});
|
|
102
|
-
it("learning-manifest.yaml 없는 세션은 건너뛴다", () => {
|
|
103
|
-
const sessionDir = join(testDir, "20260413-bbb00002");
|
|
104
|
-
mkdirSync(sessionDir, { recursive: true });
|
|
105
|
-
const result = collectLearningUsage(testDir);
|
|
106
|
-
expect(result.total_sessions).toBe(0);
|
|
107
|
-
});
|
|
108
|
-
it("여러 세션을 session_id 순 정렬한다", () => {
|
|
109
|
-
createSessionManifest(testDir, "20260413-bbb00002");
|
|
110
|
-
createSessionManifest(testDir, "20260413-aaa00001");
|
|
111
|
-
const result = collectLearningUsage(testDir);
|
|
112
|
-
expect(result.entries[0].session_id).toBe("20260413-aaa00001");
|
|
113
|
-
expect(result.entries[1].session_id).toBe("20260413-bbb00002");
|
|
114
|
-
});
|
|
115
|
-
});
|
|
116
|
-
describe("computeAggregate", () => {
|
|
117
|
-
it("빈 entries 에서 zero aggregate 를 반환한다", () => {
|
|
118
|
-
const agg = computeAggregate([]);
|
|
119
|
-
expect(agg.avg_items_loaded).toBe(0);
|
|
120
|
-
expect(agg.supply_ratio).toBe(0);
|
|
121
|
-
});
|
|
122
|
-
it("단일 세션의 supply_ratio 를 정확히 산출한다", () => {
|
|
123
|
-
const entries = [
|
|
124
|
-
makeUsageEntry("s1", { loaded: 90, parsed: 200 }),
|
|
125
|
-
];
|
|
126
|
-
const agg = computeAggregate(entries);
|
|
127
|
-
expect(agg.supply_ratio).toBe(0.45); // 90/200
|
|
128
|
-
expect(agg.avg_items_loaded).toBe(90);
|
|
129
|
-
});
|
|
130
|
-
it("여러 세션의 평균을 산출한다", () => {
|
|
131
|
-
const entries = [
|
|
132
|
-
makeUsageEntry("s1", { loaded: 100, parsed: 200, tokens: 10000 }),
|
|
133
|
-
makeUsageEntry("s2", { loaded: 200, parsed: 400, tokens: 20000 }),
|
|
134
|
-
];
|
|
135
|
-
const agg = computeAggregate(entries);
|
|
136
|
-
expect(agg.avg_items_loaded).toBe(150);
|
|
137
|
-
expect(agg.avg_items_parsed).toBe(300);
|
|
138
|
-
expect(agg.supply_ratio).toBe(0.5); // 300/600
|
|
139
|
-
expect(agg.avg_tokens_used).toBe(15000);
|
|
140
|
-
});
|
|
141
|
-
it("degradation_ratio 를 정확히 산출한다", () => {
|
|
142
|
-
const entries = [
|
|
143
|
-
makeUsageEntry("s1", { degraded: true }),
|
|
144
|
-
makeUsageEntry("s2", { degraded: false }),
|
|
145
|
-
makeUsageEntry("s3", { degraded: false }),
|
|
146
|
-
];
|
|
147
|
-
const agg = computeAggregate(entries);
|
|
148
|
-
expect(agg.degradation_ratio).toBe(0.333);
|
|
149
|
-
});
|
|
150
|
-
it("tier/role 분포를 합산한다", () => {
|
|
151
|
-
const entries = [
|
|
152
|
-
makeUsageEntry("s1", { tier: { t1: 10, t2: 5, t3: 3, t4: 2 } }),
|
|
153
|
-
makeUsageEntry("s2", { tier: { t1: 8, t2: 4, t3: 2, t4: 1 } }),
|
|
154
|
-
];
|
|
155
|
-
const agg = computeAggregate(entries);
|
|
156
|
-
expect(agg.total_tier_distribution.t1).toBe(18);
|
|
157
|
-
expect(agg.total_tier_distribution.t2).toBe(9);
|
|
158
|
-
});
|
|
159
|
-
});
|
|
160
|
-
});
|
|
161
|
-
// ─── Test helper ───
|
|
162
|
-
function makeUsageEntry(session_id, overrides = {}) {
|
|
163
|
-
return {
|
|
164
|
-
session_id,
|
|
165
|
-
session_domain: "none",
|
|
166
|
-
agents_loaded: 9,
|
|
167
|
-
total_items_loaded: overrides.loaded ?? 100,
|
|
168
|
-
total_items_parsed: overrides.parsed ?? 200,
|
|
169
|
-
total_items_skipped: 10,
|
|
170
|
-
total_tokens_used: overrides.tokens ?? 10000,
|
|
171
|
-
total_budget_truncated: 5,
|
|
172
|
-
degraded: overrides.degraded ?? false,
|
|
173
|
-
tier_distribution: overrides.tier ?? { t1: 10, t2: 5, t3: 3, t4: 2 },
|
|
174
|
-
role_distribution: { guardrail: 10, foundation: 5, convention: 2, unclassified: 3 },
|
|
175
|
-
};
|
|
176
|
-
}
|
|
@@ -1,122 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Review UX Redesign P4 — onboard-time detection of review axes.
|
|
3
|
-
*
|
|
4
|
-
* # What this module is
|
|
5
|
-
*
|
|
6
|
-
* A small pure function that aggregates the environmental signals relevant
|
|
7
|
-
* to the review execution configuration (design doc §5.2, stages 1–4) into
|
|
8
|
-
* a single `DetectedReviewAxes` result. The onboard prose flow
|
|
9
|
-
* (`.onto/processes/onboard.md`) invokes this via `npm run onboard:detect-review-axes`
|
|
10
|
-
* and uses the printed JSON as the input to the subsequent interactive
|
|
11
|
-
* questions (stages 5–7).
|
|
12
|
-
*
|
|
13
|
-
* # Why it exists
|
|
14
|
-
*
|
|
15
|
-
* Onboard is prose-driven: the LLM session reads `.onto/processes/onboard.md` and
|
|
16
|
-
* executes each stage textually. Stages 1–4 are purely automatic environment
|
|
17
|
-
* probes, so pulling them into a single deterministic TS entry point keeps
|
|
18
|
-
* the prose stage short ("run this script and read the JSON") and avoids
|
|
19
|
-
* duplicating env-var + binary checks in natural-language instructions. The
|
|
20
|
-
* detection logic itself is reused from
|
|
21
|
-
* `src/core-runtime/discovery/host-detection.ts` — this module is a thin
|
|
22
|
-
* projection into the P4 axis vocabulary (host, agent_teams_available,
|
|
23
|
-
* codex_available).
|
|
24
|
-
*
|
|
25
|
-
* # How it relates
|
|
26
|
-
*
|
|
27
|
-
* - Inputs: process.env (read via the detection helpers from discovery/).
|
|
28
|
-
* - Output: pure data (`DetectedReviewAxes`) — onboard prose renders it and
|
|
29
|
-
* asks the user axis-by-axis questions.
|
|
30
|
-
* - Write-back seat: `write-review-block.ts` (sibling module).
|
|
31
|
-
* - Consumed by: `.onto/processes/onboard.md` §§ 3.7/3.8 (added in P4).
|
|
32
|
-
*/
|
|
33
|
-
import { detectClaudeCodeEnvSignal, detectCodexBinaryAvailable, detectCodexEnvSignal, } from "../discovery/host-detection.js";
|
|
34
|
-
// ---------------------------------------------------------------------------
|
|
35
|
-
// Core detection
|
|
36
|
-
// ---------------------------------------------------------------------------
|
|
37
|
-
const ENV_AGENT_TEAMS = "CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS";
|
|
38
|
-
/**
|
|
39
|
-
* Map the discovery-layer env signals into the onboard host vocabulary.
|
|
40
|
-
*
|
|
41
|
-
* Priority: Claude Code > Codex CLI > plain terminal. Same ordering as
|
|
42
|
-
* `detectHostRuntime` but projected into the simpler three-value domain.
|
|
43
|
-
* Claude Code takes precedence because an inner Codex process inside a
|
|
44
|
-
* Claude Code session still reports CLAUDECODE=1 — in that case the onboard
|
|
45
|
-
* context is the Claude Code session, and codex availability is surfaced
|
|
46
|
-
* separately as a subagent option.
|
|
47
|
-
*/
|
|
48
|
-
function detectHost() {
|
|
49
|
-
if (detectClaudeCodeEnvSignal())
|
|
50
|
-
return "claude-code";
|
|
51
|
-
if (detectCodexEnvSignal())
|
|
52
|
-
return "codex-cli";
|
|
53
|
-
return "plain-terminal";
|
|
54
|
-
}
|
|
55
|
-
/**
|
|
56
|
-
* Detect the four review axes observable from environment alone.
|
|
57
|
-
*
|
|
58
|
-
* Pure w.r.t. process.env — no file writes, no network. Deterministic given
|
|
59
|
-
* the same env snapshot + filesystem state (codex binary + auth.json).
|
|
60
|
-
*/
|
|
61
|
-
export function detectReviewAxes() {
|
|
62
|
-
const host = detectHost();
|
|
63
|
-
// Exact-match against `"1"` to stay consistent with the resolver's strict
|
|
64
|
-
// check (`execution-topology-resolver.ts:506`). A loose `Boolean(...)` here
|
|
65
|
-
// would disagree with the resolver when the user sets the variable to
|
|
66
|
-
// `"0"` / `"false"` / `""`. Both layers must read the same signal the
|
|
67
|
-
// same way so the onboard UX promise survives the runtime.
|
|
68
|
-
const agent_teams_available = process.env[ENV_AGENT_TEAMS] === "1";
|
|
69
|
-
const codex_available = detectCodexBinaryAvailable();
|
|
70
|
-
return {
|
|
71
|
-
detected: {
|
|
72
|
-
host,
|
|
73
|
-
agent_teams_available,
|
|
74
|
-
codex_available,
|
|
75
|
-
},
|
|
76
|
-
};
|
|
77
|
-
}
|
|
78
|
-
// ---------------------------------------------------------------------------
|
|
79
|
-
// CLI entry — `npm run onboard:detect-review-axes`
|
|
80
|
-
// ---------------------------------------------------------------------------
|
|
81
|
-
function isMainModule() {
|
|
82
|
-
// tsx executes this module directly; the ESM equivalent of
|
|
83
|
-
// `require.main === module` is comparing import.meta.url to argv[1].
|
|
84
|
-
const entry = process.argv[1];
|
|
85
|
-
if (typeof entry !== "string" || entry.length === 0)
|
|
86
|
-
return false;
|
|
87
|
-
try {
|
|
88
|
-
const entryUrl = new URL(`file://${entry}`).href;
|
|
89
|
-
return import.meta.url === entryUrl;
|
|
90
|
-
}
|
|
91
|
-
catch {
|
|
92
|
-
return false;
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
function printHelp() {
|
|
96
|
-
const lines = [
|
|
97
|
-
"onboard:detect-review-axes",
|
|
98
|
-
"",
|
|
99
|
-
"Detects the environmental axes consumed by the onboard prose flow:",
|
|
100
|
-
" - host (claude-code | codex-cli | plain-terminal)",
|
|
101
|
-
" - agent_teams (CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1)",
|
|
102
|
-
" - codex_available (codex on PATH + ~/.codex/auth.json)",
|
|
103
|
-
"",
|
|
104
|
-
"Usage:",
|
|
105
|
-
" npm run onboard:detect-review-axes",
|
|
106
|
-
" npm run onboard:detect-review-axes -- --help",
|
|
107
|
-
"",
|
|
108
|
-
"Output: single-line JSON { detected: { ... } } on stdout.",
|
|
109
|
-
];
|
|
110
|
-
console.log(lines.join("\n"));
|
|
111
|
-
}
|
|
112
|
-
if (isMainModule()) {
|
|
113
|
-
const argv = process.argv.slice(2);
|
|
114
|
-
if (argv.includes("--help") || argv.includes("-h")) {
|
|
115
|
-
printHelp();
|
|
116
|
-
process.exit(0);
|
|
117
|
-
}
|
|
118
|
-
const result = detectReviewAxes();
|
|
119
|
-
// Compact single-line JSON so the prose caller can forward it as-is into
|
|
120
|
-
// the LLM context without re-parsing.
|
|
121
|
-
console.log(JSON.stringify(result));
|
|
122
|
-
}
|
|
@@ -1,127 +0,0 @@
|
|
|
1
|
-
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
|
-
import { detectReviewAxes } from "./detect-review-axes.js";
|
|
3
|
-
// ---------------------------------------------------------------------------
|
|
4
|
-
// detectReviewAxes — Review UX Redesign P4 (2026-04-21)
|
|
5
|
-
// ---------------------------------------------------------------------------
|
|
6
|
-
//
|
|
7
|
-
// These tests exercise the projection of env signals into the onboard
|
|
8
|
-
// axis vocabulary. The underlying detection helpers (from discovery/) are
|
|
9
|
-
// covered by their own suite; here we only verify:
|
|
10
|
-
//
|
|
11
|
-
// (1) Host priority ordering — Claude Code > Codex CLI > plain.
|
|
12
|
-
// (2) agent_teams_available reads CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS.
|
|
13
|
-
// (3) litellm_endpoint returns the URL string (not just a boolean).
|
|
14
|
-
// (4) codex_available is sourced from the shared discovery helper.
|
|
15
|
-
//
|
|
16
|
-
// The codex binary / auth.json probe is filesystem-backed, so we mock
|
|
17
|
-
// `detectCodexBinaryAvailable` from the discovery module to keep the tests
|
|
18
|
-
// hermetic.
|
|
19
|
-
// ---------------------------------------------------------------------------
|
|
20
|
-
vi.mock("../discovery/host-detection.js", async (importOriginal) => {
|
|
21
|
-
const actual = (await importOriginal());
|
|
22
|
-
return {
|
|
23
|
-
...actual,
|
|
24
|
-
// Override only the filesystem-dependent probe. The env-signal helpers
|
|
25
|
-
// remain real so we can control behaviour via process.env.
|
|
26
|
-
detectCodexBinaryAvailable: vi.fn(() => false),
|
|
27
|
-
};
|
|
28
|
-
});
|
|
29
|
-
const savedEnv = {};
|
|
30
|
-
const VOLATILE_ENV_KEYS = [
|
|
31
|
-
"CLAUDECODE",
|
|
32
|
-
"CLAUDE_PROJECT_DIR",
|
|
33
|
-
"CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS",
|
|
34
|
-
"CODEX_THREAD_ID",
|
|
35
|
-
"CODEX_CI",
|
|
36
|
-
"LITELLM_BASE_URL",
|
|
37
|
-
];
|
|
38
|
-
beforeEach(() => {
|
|
39
|
-
for (const key of VOLATILE_ENV_KEYS) {
|
|
40
|
-
savedEnv[key] = process.env[key];
|
|
41
|
-
delete process.env[key];
|
|
42
|
-
}
|
|
43
|
-
});
|
|
44
|
-
afterEach(() => {
|
|
45
|
-
for (const key of VOLATILE_ENV_KEYS) {
|
|
46
|
-
const original = savedEnv[key];
|
|
47
|
-
if (original === undefined) {
|
|
48
|
-
delete process.env[key];
|
|
49
|
-
}
|
|
50
|
-
else {
|
|
51
|
-
process.env[key] = original;
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
vi.resetAllMocks();
|
|
55
|
-
});
|
|
56
|
-
describe("detectReviewAxes — host category", () => {
|
|
57
|
-
it("returns plain-terminal when no host signal is present", () => {
|
|
58
|
-
const result = detectReviewAxes();
|
|
59
|
-
expect(result.detected.host).toBe("plain-terminal");
|
|
60
|
-
});
|
|
61
|
-
it("returns claude-code when CLAUDECODE=1", () => {
|
|
62
|
-
process.env.CLAUDECODE = "1";
|
|
63
|
-
expect(detectReviewAxes().detected.host).toBe("claude-code");
|
|
64
|
-
});
|
|
65
|
-
it("returns claude-code when CLAUDE_PROJECT_DIR is set", () => {
|
|
66
|
-
process.env.CLAUDE_PROJECT_DIR = "/tmp/proj";
|
|
67
|
-
expect(detectReviewAxes().detected.host).toBe("claude-code");
|
|
68
|
-
});
|
|
69
|
-
it("returns codex-cli when CODEX_THREAD_ID is set", () => {
|
|
70
|
-
process.env.CODEX_THREAD_ID = "thr-123";
|
|
71
|
-
expect(detectReviewAxes().detected.host).toBe("codex-cli");
|
|
72
|
-
});
|
|
73
|
-
it("prefers claude-code over codex-cli when both signals are present", () => {
|
|
74
|
-
// An inner Codex exec inside a Claude Code session — onboard context
|
|
75
|
-
// is Claude Code; codex availability is surfaced separately.
|
|
76
|
-
process.env.CLAUDECODE = "1";
|
|
77
|
-
process.env.CODEX_THREAD_ID = "thr-123";
|
|
78
|
-
expect(detectReviewAxes().detected.host).toBe("claude-code");
|
|
79
|
-
});
|
|
80
|
-
});
|
|
81
|
-
describe("detectReviewAxes — agent_teams_available (strict =1)", () => {
|
|
82
|
-
// The resolver uses `env.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS === "1"`.
|
|
83
|
-
// Onboard MUST use the same check so the two layers never disagree on
|
|
84
|
-
// whether teams are available — otherwise onboard writes a config the
|
|
85
|
-
// runtime silently rejects (P3 would degrade it, violating the UX
|
|
86
|
-
// promise that "what onboard accepts is what runs").
|
|
87
|
-
it("is false when env var is absent", () => {
|
|
88
|
-
expect(detectReviewAxes().detected.agent_teams_available).toBe(false);
|
|
89
|
-
});
|
|
90
|
-
it('is true only when CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS === "1"', () => {
|
|
91
|
-
process.env.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS = "1";
|
|
92
|
-
expect(detectReviewAxes().detected.agent_teams_available).toBe(true);
|
|
93
|
-
});
|
|
94
|
-
it('is false for truthy-looking strings that are not "1"', () => {
|
|
95
|
-
// These would pass a naive Boolean() check but must NOT activate teams.
|
|
96
|
-
// The resolver rejects them; onboard must match.
|
|
97
|
-
for (const value of ["true", "yes", "on", "enabled", "TRUE", "2"]) {
|
|
98
|
-
process.env.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS = value;
|
|
99
|
-
expect(detectReviewAxes().detected.agent_teams_available).toBe(false);
|
|
100
|
-
}
|
|
101
|
-
});
|
|
102
|
-
it('is false for falsy-looking strings ("0", "false", empty)', () => {
|
|
103
|
-
for (const value of ["0", "false", ""]) {
|
|
104
|
-
process.env.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS = value;
|
|
105
|
-
expect(detectReviewAxes().detected.agent_teams_available).toBe(false);
|
|
106
|
-
}
|
|
107
|
-
});
|
|
108
|
-
});
|
|
109
|
-
describe("detectReviewAxes — litellm_endpoint", () => {
|
|
110
|
-
it("is null when LITELLM_BASE_URL is unset", () => {
|
|
111
|
-
expect(detectReviewAxes().detected.litellm_endpoint).toBeNull();
|
|
112
|
-
});
|
|
113
|
-
it("returns the URL string when LITELLM_BASE_URL is set", () => {
|
|
114
|
-
process.env.LITELLM_BASE_URL = "http://localhost:4000";
|
|
115
|
-
expect(detectReviewAxes().detected.litellm_endpoint).toBe("http://localhost:4000");
|
|
116
|
-
});
|
|
117
|
-
});
|
|
118
|
-
describe("detectReviewAxes — codex_available", () => {
|
|
119
|
-
it("surfaces the discovery helper verdict (mocked false by default)", async () => {
|
|
120
|
-
const mod = await import("../discovery/host-detection.js");
|
|
121
|
-
const spy = vi.mocked(mod.detectCodexBinaryAvailable);
|
|
122
|
-
spy.mockReturnValue(false);
|
|
123
|
-
expect(detectReviewAxes().detected.codex_available).toBe(false);
|
|
124
|
-
spy.mockReturnValue(true);
|
|
125
|
-
expect(detectReviewAxes().detected.codex_available).toBe(true);
|
|
126
|
-
});
|
|
127
|
-
});
|
|
@@ -1,188 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Review UX Redesign P4 — write/update the `review:` block in `.onto/config.yml`.
|
|
3
|
-
*
|
|
4
|
-
* # What this module is
|
|
5
|
-
*
|
|
6
|
-
* A small helper that takes a validated `OntoReviewConfig` plus a path to
|
|
7
|
-
* an `.onto/config.yml` file and either (a) adds a `review:` block to the
|
|
8
|
-
* file or (b) replaces an existing one.
|
|
9
|
-
*
|
|
10
|
-
* # Why it exists
|
|
11
|
-
*
|
|
12
|
-
* Onboard stage 7 (design doc §5.2) writes the user's chosen review axes
|
|
13
|
-
* into `.onto/config.yml`. Without a dedicated seat this would require the
|
|
14
|
-
* prose to embed YAML serialization instructions, which is error-prone — a
|
|
15
|
-
* stray indent produces a silently invalid config. Centralizing the write
|
|
16
|
-
* path means the onboard prose can describe the intent ("record the user's
|
|
17
|
-
* answers in the review block") and delegate formatting to this helper.
|
|
18
|
-
*
|
|
19
|
-
* We use `yaml`'s `parseDocument` / `Document` API (not `parse` +
|
|
20
|
-
* `stringify`) because the document round-trip preserves comments and
|
|
21
|
-
* formatting where the existing file has them. That matters for configs
|
|
22
|
-
* authored by humans — they often annotate fields with rationale.
|
|
23
|
-
*
|
|
24
|
-
* # How it relates
|
|
25
|
-
*
|
|
26
|
-
* - Consumes: `OntoReviewConfig` type from `discovery/config-chain.ts`.
|
|
27
|
-
* - Validates with: `validateReviewConfig` from
|
|
28
|
-
* `review/review-config-validator.ts` — we re-validate to catch caller
|
|
29
|
-
* errors early (the onboard prose is a weak integrator).
|
|
30
|
-
* - Exposed via: `npm run onboard:write-review-block -- <path> <json>`.
|
|
31
|
-
*
|
|
32
|
-
* # Guarantees
|
|
33
|
-
*
|
|
34
|
-
* 1. Existing top-level fields are preserved in order.
|
|
35
|
-
* 2. Comments attached to preserved fields survive the round-trip.
|
|
36
|
-
* 3. If the file does not exist, it is created with a minimal document
|
|
37
|
-
* containing only the `review:` block.
|
|
38
|
-
* 4. If the input is not a valid `OntoReviewConfig`, the function returns a
|
|
39
|
-
* result object with `ok: false` and no write is performed.
|
|
40
|
-
*/
|
|
41
|
-
import fs from "node:fs";
|
|
42
|
-
import path from "node:path";
|
|
43
|
-
import { Document, parseDocument } from "yaml";
|
|
44
|
-
import { validateReviewConfig, } from "../review/review-config-validator.js";
|
|
45
|
-
/**
|
|
46
|
-
* Write (or update) the `review:` block in the given config file.
|
|
47
|
-
*
|
|
48
|
-
* The function re-validates `review` with `validateReviewConfig` before
|
|
49
|
-
* touching the file — if validation fails, nothing is written and the
|
|
50
|
-
* caller receives the structured error list.
|
|
51
|
-
*/
|
|
52
|
-
export function writeReviewBlock(configPath, review) {
|
|
53
|
-
// Re-validate. The type system alone cannot catch callers who constructed
|
|
54
|
-
// the object manually from JSON input (the most common P4 path — the
|
|
55
|
-
// onboard prose builds the object from interactive answers).
|
|
56
|
-
const validation = validateReviewConfig(review);
|
|
57
|
-
if (!validation.ok) {
|
|
58
|
-
return { ok: false, errors: validation.errors };
|
|
59
|
-
}
|
|
60
|
-
const absolute = path.resolve(configPath);
|
|
61
|
-
const exists = fs.existsSync(absolute);
|
|
62
|
-
// Load the existing document (or a fresh empty one). `parseDocument`
|
|
63
|
-
// returns a `Document` node the YAML library can mutate and re-serialize
|
|
64
|
-
// while preserving source formatting where practical.
|
|
65
|
-
let doc;
|
|
66
|
-
if (exists) {
|
|
67
|
-
const raw = fs.readFileSync(absolute, "utf8");
|
|
68
|
-
doc = parseDocument(raw, { keepSourceTokens: false });
|
|
69
|
-
}
|
|
70
|
-
else {
|
|
71
|
-
// Ensure the parent directory exists — `.onto/` may not be present on
|
|
72
|
-
// a brand-new project until onboard creates it.
|
|
73
|
-
fs.mkdirSync(path.dirname(absolute), { recursive: true });
|
|
74
|
-
doc = new Document({});
|
|
75
|
-
}
|
|
76
|
-
// Detect the prior state of the document so we can report it back to the
|
|
77
|
-
// onboard prose (for the user-facing summary).
|
|
78
|
-
const replacedExistingBlock = exists && doc.has("review");
|
|
79
|
-
// The yaml library accepts a plain object for `set` — it converts that
|
|
80
|
-
// into the internal Node graph. We filter out undefined leaves so the
|
|
81
|
-
// output does not carry `key: null` artifacts for absent axes.
|
|
82
|
-
const serializable = pruneUndefined(review);
|
|
83
|
-
doc.set("review", serializable);
|
|
84
|
-
const serialized = doc.toString();
|
|
85
|
-
fs.writeFileSync(absolute, serialized, "utf8");
|
|
86
|
-
return {
|
|
87
|
-
ok: true,
|
|
88
|
-
path: absolute,
|
|
89
|
-
created: !exists,
|
|
90
|
-
replacedExistingBlock,
|
|
91
|
-
};
|
|
92
|
-
}
|
|
93
|
-
// ---------------------------------------------------------------------------
|
|
94
|
-
// Internal helpers
|
|
95
|
-
// ---------------------------------------------------------------------------
|
|
96
|
-
/**
|
|
97
|
-
* Strip `undefined` leaves from a nested object. The `yaml` library would
|
|
98
|
-
* otherwise serialize them as `null`, which is semantically different from
|
|
99
|
-
* "absent" in the review block (an absent key = use review defaults).
|
|
100
|
-
*/
|
|
101
|
-
function pruneUndefined(input) {
|
|
102
|
-
if (input === null || input === undefined)
|
|
103
|
-
return input;
|
|
104
|
-
if (Array.isArray(input)) {
|
|
105
|
-
return input.map((v) => pruneUndefined(v));
|
|
106
|
-
}
|
|
107
|
-
if (typeof input !== "object")
|
|
108
|
-
return input;
|
|
109
|
-
const out = {};
|
|
110
|
-
for (const [key, value] of Object.entries(input)) {
|
|
111
|
-
if (value === undefined)
|
|
112
|
-
continue;
|
|
113
|
-
out[key] = pruneUndefined(value);
|
|
114
|
-
}
|
|
115
|
-
return out;
|
|
116
|
-
}
|
|
117
|
-
// ---------------------------------------------------------------------------
|
|
118
|
-
// CLI entry — `npm run onboard:write-review-block -- <path> <json>`
|
|
119
|
-
// ---------------------------------------------------------------------------
|
|
120
|
-
function printHelp() {
|
|
121
|
-
const lines = [
|
|
122
|
-
"onboard:write-review-block",
|
|
123
|
-
"",
|
|
124
|
-
"Write or update the `review:` block in a .onto/config.yml file.",
|
|
125
|
-
"",
|
|
126
|
-
"Usage:",
|
|
127
|
-
" npm run onboard:write-review-block -- <config-path> <review-json>",
|
|
128
|
-
" npm run onboard:write-review-block -- --help",
|
|
129
|
-
"",
|
|
130
|
-
"Arguments:",
|
|
131
|
-
" <config-path> Path to .onto/config.yml (created if missing).",
|
|
132
|
-
" <review-json> JSON string representing an OntoReviewConfig.",
|
|
133
|
-
"",
|
|
134
|
-
"Example:",
|
|
135
|
-
" npm run onboard:write-review-block -- .onto/config.yml \\",
|
|
136
|
-
" '{\"teamlead\":{\"model\":\"main\"},\"subagent\":{\"provider\":\"main-native\"}}'",
|
|
137
|
-
"",
|
|
138
|
-
"Output: single-line JSON describing the write result on stdout.",
|
|
139
|
-
"Exit code 0 on success, 1 on validation/IO failure.",
|
|
140
|
-
];
|
|
141
|
-
console.log(lines.join("\n"));
|
|
142
|
-
}
|
|
143
|
-
function isMainModule() {
|
|
144
|
-
const entry = process.argv[1];
|
|
145
|
-
if (typeof entry !== "string" || entry.length === 0)
|
|
146
|
-
return false;
|
|
147
|
-
try {
|
|
148
|
-
const entryUrl = new URL(`file://${entry}`).href;
|
|
149
|
-
return import.meta.url === entryUrl;
|
|
150
|
-
}
|
|
151
|
-
catch {
|
|
152
|
-
return false;
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
function runCli(argv) {
|
|
156
|
-
if (argv.length === 0 || argv.includes("--help") || argv.includes("-h")) {
|
|
157
|
-
printHelp();
|
|
158
|
-
return argv.length === 0 ? 1 : 0;
|
|
159
|
-
}
|
|
160
|
-
const unsupportedFlags = argv.filter((a) => a.startsWith("--"));
|
|
161
|
-
if (unsupportedFlags.length > 0) {
|
|
162
|
-
console.error(`error: unsupported option(s): ${unsupportedFlags.join(", ")}`);
|
|
163
|
-
return 1;
|
|
164
|
-
}
|
|
165
|
-
const positional = argv.filter((a) => !a.startsWith("--"));
|
|
166
|
-
if (positional.length < 2) {
|
|
167
|
-
console.error("error: two positional arguments required: <config-path> <review-json>. " +
|
|
168
|
-
"Run with --help for usage.");
|
|
169
|
-
return 1;
|
|
170
|
-
}
|
|
171
|
-
const [configPath, reviewJson] = positional;
|
|
172
|
-
let parsed;
|
|
173
|
-
try {
|
|
174
|
-
parsed = JSON.parse(reviewJson);
|
|
175
|
-
}
|
|
176
|
-
catch (err) {
|
|
177
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
178
|
-
console.error(`error: <review-json> is not valid JSON: ${message}`);
|
|
179
|
-
return 1;
|
|
180
|
-
}
|
|
181
|
-
const result = writeReviewBlock(configPath, parsed);
|
|
182
|
-
console.log(JSON.stringify(result));
|
|
183
|
-
return result.ok ? 0 : 1;
|
|
184
|
-
}
|
|
185
|
-
if (isMainModule()) {
|
|
186
|
-
const exitCode = runCli(process.argv.slice(2));
|
|
187
|
-
process.exit(exitCode);
|
|
188
|
-
}
|