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,150 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Phase 3 Promote — Collection health snapshot (Step 9d).
|
|
3
|
-
*
|
|
4
|
-
* Design authority:
|
|
5
|
-
* - .onto/processes/learn/promote.md §9 (Collection Health Snapshot)
|
|
6
|
-
* - learn-phase3-design-v4.md §1.3 (P-15 metric assembly)
|
|
7
|
-
*
|
|
8
|
-
* Responsibility:
|
|
9
|
-
* - Aggregate axis distribution, purpose distribution, judgment ratio,
|
|
10
|
-
* and other operator-facing metrics from the global learning pool.
|
|
11
|
-
* - Roll up panel-derived counts (creation gate failures, axis tag
|
|
12
|
-
* re-evaluation, applied learnings yes/no) supplied by the orchestrator.
|
|
13
|
-
* - Identify agents whose learning files have crossed the separation
|
|
14
|
-
* trigger threshold (>100 lines per agent file).
|
|
15
|
-
*
|
|
16
|
-
* Scope:
|
|
17
|
-
* - Pure aggregator. No I/O. No mutation.
|
|
18
|
-
* - Uses ParsedLearningItem fields and a few report-side counters that the
|
|
19
|
-
* orchestrator computes after panel review and audit completion.
|
|
20
|
-
*
|
|
21
|
-
* Note: time-series comparison (Previous/Δ) is intentionally NOT implemented
|
|
22
|
-
* (promote.md §9 final paragraph). The user manually compares against the
|
|
23
|
-
* prior promote report.
|
|
24
|
-
*/
|
|
25
|
-
// ---------------------------------------------------------------------------
|
|
26
|
-
// Constants
|
|
27
|
-
// ---------------------------------------------------------------------------
|
|
28
|
-
/**
|
|
29
|
-
* Separation trigger: when an agent's learning file exceeds this row count
|
|
30
|
-
* the operator is prompted to consider splitting it (promote.md §9 footnote).
|
|
31
|
-
*/
|
|
32
|
-
export const SEPARATION_TRIGGER_LINE_COUNT = 100;
|
|
33
|
-
function classifyAxis(item) {
|
|
34
|
-
const hasMethodology = item.applicability_tags.includes("methodology");
|
|
35
|
-
const hasDomain = item.applicability_tags.some((t) => t.startsWith("domain/"));
|
|
36
|
-
if (hasMethodology && hasDomain)
|
|
37
|
-
return "dual";
|
|
38
|
-
if (hasMethodology)
|
|
39
|
-
return "methodology_only";
|
|
40
|
-
if (hasDomain)
|
|
41
|
-
return "domain_only";
|
|
42
|
-
return null; // unclassified — not counted in distribution
|
|
43
|
-
}
|
|
44
|
-
function tallyAxis(items) {
|
|
45
|
-
const counts = {
|
|
46
|
-
methodology_only: 0,
|
|
47
|
-
domain_only: 0,
|
|
48
|
-
dual: 0,
|
|
49
|
-
};
|
|
50
|
-
for (const item of items) {
|
|
51
|
-
const axis = classifyAxis(item);
|
|
52
|
-
if (axis !== null)
|
|
53
|
-
counts[axis] += 1;
|
|
54
|
-
}
|
|
55
|
-
return counts;
|
|
56
|
-
}
|
|
57
|
-
function tallyPurpose(items) {
|
|
58
|
-
const counts = { guardrail: 0, foundation: 0, convention: 0, insight: 0 };
|
|
59
|
-
for (const item of items) {
|
|
60
|
-
if (item.role === null)
|
|
61
|
-
continue;
|
|
62
|
-
counts[item.role] += 1;
|
|
63
|
-
}
|
|
64
|
-
return counts;
|
|
65
|
-
}
|
|
66
|
-
function judgmentRatio(items) {
|
|
67
|
-
if (items.length === 0)
|
|
68
|
-
return 0;
|
|
69
|
-
const judgmentCount = items.filter((i) => i.type === "judgment").length;
|
|
70
|
-
return Math.round((judgmentCount / items.length) * 1000) / 10; // 1 decimal
|
|
71
|
-
}
|
|
72
|
-
function pct(num, denom) {
|
|
73
|
-
if (denom === 0)
|
|
74
|
-
return 0;
|
|
75
|
-
return Math.round((num / denom) * 1000) / 10;
|
|
76
|
-
}
|
|
77
|
-
/**
|
|
78
|
-
* Identify agents whose learning files have grown past the separation
|
|
79
|
-
* trigger threshold. Uses the highest line_number observed per agent in the
|
|
80
|
-
* given item pool, or the explicit `agent_line_counts` override when
|
|
81
|
-
* provided. The override exists because line_number reflects the position
|
|
82
|
-
* of an item, not the file's total line count — for empty/sparse files the
|
|
83
|
-
* override gives a more accurate signal.
|
|
84
|
-
*/
|
|
85
|
-
function findSeparationTriggers(items, override) {
|
|
86
|
-
const triggered = [];
|
|
87
|
-
if (override) {
|
|
88
|
-
for (const [agent, count] of override) {
|
|
89
|
-
if (count > SEPARATION_TRIGGER_LINE_COUNT)
|
|
90
|
-
triggered.push(agent);
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
else {
|
|
94
|
-
const maxLineByAgent = new Map();
|
|
95
|
-
for (const item of items) {
|
|
96
|
-
const current = maxLineByAgent.get(item.agent_id) ?? 0;
|
|
97
|
-
if (item.line_number > current) {
|
|
98
|
-
maxLineByAgent.set(item.agent_id, item.line_number);
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
for (const [agent, count] of maxLineByAgent) {
|
|
102
|
-
if (count > SEPARATION_TRIGGER_LINE_COUNT)
|
|
103
|
-
triggered.push(agent);
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
return triggered.sort();
|
|
107
|
-
}
|
|
108
|
-
/**
|
|
109
|
-
* Build the snapshot from already-assembled report components.
|
|
110
|
-
*
|
|
111
|
-
* Why pass in panel_verdicts/retirement_candidates rather than recomputing:
|
|
112
|
-
* - Avoids drifting from the report state. The report is canonical; the
|
|
113
|
-
* snapshot is a derived view of it.
|
|
114
|
-
* - Lets the orchestrator decide whether to recompute — e.g., a re-run
|
|
115
|
-
* after audit changes might want to refresh metrics without re-running
|
|
116
|
-
* panel review.
|
|
117
|
-
*
|
|
118
|
-
* Note on `event_marker_review_candidates`: this is the count of items the
|
|
119
|
-
* retirement analyzer surfaced as candidates (i.e., 2+ countable markers).
|
|
120
|
-
* It is not the total event marker count across the global pool.
|
|
121
|
-
*
|
|
122
|
-
* Scope semantics (W-C-06 signal scope 명시):
|
|
123
|
-
* - globalItems, retirementCandidates → GLOBAL scope (~/.onto/learnings/)
|
|
124
|
-
* - panelVerdicts → PROJECT scope ({project}/.onto/learnings/)
|
|
125
|
-
* - crossAgentDedupClusters → GLOBAL scope (cross-agent dedup)
|
|
126
|
-
* These scopes are intentionally different: retirement operates on promoted
|
|
127
|
-
* (global) items, while panel review operates on project items being promoted.
|
|
128
|
-
* The snapshot aggregates both, labeled by their source scope.
|
|
129
|
-
*/
|
|
130
|
-
export function buildHealthSnapshot(config) {
|
|
131
|
-
const total = config.globalItems.length;
|
|
132
|
-
const axis = tallyAxis(config.globalItems);
|
|
133
|
-
const purpose = tallyPurpose(config.globalItems);
|
|
134
|
-
return {
|
|
135
|
-
total_global_learnings: total,
|
|
136
|
-
axis_distribution: {
|
|
137
|
-
methodology_only_pct: pct(axis.methodology_only, total),
|
|
138
|
-
domain_only_pct: pct(axis.domain_only, total),
|
|
139
|
-
dual_pct: pct(axis.dual, total),
|
|
140
|
-
},
|
|
141
|
-
purpose_distribution: purpose,
|
|
142
|
-
judgment_ratio_pct: judgmentRatio(config.globalItems),
|
|
143
|
-
cross_agent_dedup_clusters_remaining: config.crossAgentDedupClusters.length,
|
|
144
|
-
axis_tag_re_evaluation_changes_this_session: config.extras.axis_tag_re_evaluation_changes_this_session,
|
|
145
|
-
event_marker_review_candidates: config.retirementCandidates.length,
|
|
146
|
-
creation_gate_failures: config.extras.creation_gate_failures,
|
|
147
|
-
applied_learnings_aggregate: config.extras.applied_learnings_aggregate,
|
|
148
|
-
separation_trigger_agents: findSeparationTriggers(config.globalItems, config.extras.agent_line_counts),
|
|
149
|
-
};
|
|
150
|
-
}
|
|
@@ -1,544 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Phase 3 — Insight Reclassifier (Step 12 + follow-up apply path).
|
|
3
|
-
*
|
|
4
|
-
* Design authority:
|
|
5
|
-
* - learn-phase3-design-v2.md DD-9 (separate insight reclassifier path)
|
|
6
|
-
* - learn-phase3-design-v3.md DD-9 (collector mode + lineage)
|
|
7
|
-
* - learn-phase3-design-v4.md DD-9 (panel-reviewer reuse, batch-size,
|
|
8
|
-
* resumable progress)
|
|
9
|
-
*
|
|
10
|
-
* Purpose:
|
|
11
|
-
* - Phase 1 era left ~462 global learnings tagged `[insight]`. Insight is
|
|
12
|
-
* an under-classified label meaning "we don't know if this is a guardrail,
|
|
13
|
-
* foundation, or convention". This command walks each insight item, calls
|
|
14
|
-
* the LLM, and records a recommended reclassification. A follow-up apply
|
|
15
|
-
* path rewrites the role tag in place.
|
|
16
|
-
*
|
|
17
|
-
* Why a separate path from promotion:
|
|
18
|
-
* - Cost separation: 462 × 3 panel members ≈ 1,400 LLM calls. Running this
|
|
19
|
-
* on every promote pass would balloon cost.
|
|
20
|
-
* - Migration vs steady-state separation: this is a one-shot cleanup that
|
|
21
|
-
* ideally finishes once and never runs again. Promote is a recurring
|
|
22
|
-
* workflow.
|
|
23
|
-
* - Audit trail isolation: insights reclassified here are tracked
|
|
24
|
-
* independently of promote outcomes so a reclassification regret can be
|
|
25
|
-
* undone without rolling back unrelated promotions.
|
|
26
|
-
*
|
|
27
|
-
* Phase A (analyze) vs Phase B (apply) split mirrors promote:
|
|
28
|
-
* - runInsightReclassifier is Phase A: classify and write report JSON.
|
|
29
|
-
* - applyInsightReclassifications is Phase B: read report JSON, rewrite
|
|
30
|
-
* the `[insight]` role tag to the proposed role in place, and record
|
|
31
|
-
* which entries were applied / skipped / failed.
|
|
32
|
-
*
|
|
33
|
-
* Apply semantics (Phase B):
|
|
34
|
-
* - Idempotent: each report entry carries the raw_line captured at classify
|
|
35
|
-
* time. At apply time we look up the line in the file by literal match.
|
|
36
|
-
* If the original line is gone (file was already rewritten or edited),
|
|
37
|
-
* the entry is recorded as "skipped_already_applied" rather than failing.
|
|
38
|
-
* - Three target forms:
|
|
39
|
-
* guardrail / foundation / convention → replace [insight] with [{role}]
|
|
40
|
-
* drop_role → remove the [insight] bracket
|
|
41
|
-
* - Skips entries without a proposed_role (kept as unclassified_pending in
|
|
42
|
-
* the Phase A output).
|
|
43
|
-
*
|
|
44
|
-
* Failure model:
|
|
45
|
-
* - Classify LLM unreachable: item is left unclassified, recorded as
|
|
46
|
-
* `unclassified_pending`. The operator re-runs after fixing API access.
|
|
47
|
-
* - Classify returns unrecognized role: skipped with error.
|
|
48
|
-
* - Apply target file missing: applyResult.failed entry with reason.
|
|
49
|
-
*/
|
|
50
|
-
import fs from "node:fs";
|
|
51
|
-
import path from "node:path";
|
|
52
|
-
import { callLlm, hashPrompt } from "../shared/llm-caller.js";
|
|
53
|
-
import { collect } from "./collector.js";
|
|
54
|
-
// ---------------------------------------------------------------------------
|
|
55
|
-
// Prompt
|
|
56
|
-
// ---------------------------------------------------------------------------
|
|
57
|
-
const RECLASSIFY_SYSTEM_PROMPT = `You are reclassifying [insight]-tagged learnings into one of three concrete role categories.
|
|
58
|
-
|
|
59
|
-
Output ONE JSON object:
|
|
60
|
-
{
|
|
61
|
-
"proposed_role": "guardrail" | "foundation" | "convention" | "drop_role",
|
|
62
|
-
"reason": "<one sentence explaining the choice>"
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
Definitions (learning-rules.md):
|
|
66
|
-
- guardrail: situational warning with corrective action (Situation + Result + Corrective action)
|
|
67
|
-
- foundation: reusable structural fact that other reasoning depends on
|
|
68
|
-
- convention: formatting/naming/process rule the team follows
|
|
69
|
-
- drop_role: the item is too vague or context-specific to fit any of the above —
|
|
70
|
-
drop the role tag entirely
|
|
71
|
-
|
|
72
|
-
NO markdown fences, JSON only.`;
|
|
73
|
-
function buildReclassifyUserPrompt(item) {
|
|
74
|
-
return [
|
|
75
|
-
`Agent: ${item.agent_id}`,
|
|
76
|
-
`Tags: [${item.applicability_tags.join(" ")}]`,
|
|
77
|
-
`Type: ${item.type}`,
|
|
78
|
-
`Source: ${item.source_project ?? "?"} / ${item.source_domain ?? "?"} / ${item.source_date ?? "?"}`,
|
|
79
|
-
"",
|
|
80
|
-
"Content:",
|
|
81
|
-
item.content,
|
|
82
|
-
"",
|
|
83
|
-
'Respond with {"proposed_role": "...", "reason": "..."}.',
|
|
84
|
-
].join("\n");
|
|
85
|
-
}
|
|
86
|
-
const VALID_ROLES = [
|
|
87
|
-
"guardrail",
|
|
88
|
-
"foundation",
|
|
89
|
-
"convention",
|
|
90
|
-
"drop_role",
|
|
91
|
-
];
|
|
92
|
-
async function attemptClassify(item, modelId, retryFeedback) {
|
|
93
|
-
let userPrompt = buildReclassifyUserPrompt(item);
|
|
94
|
-
if (retryFeedback) {
|
|
95
|
-
userPrompt +=
|
|
96
|
-
`\n\nPrevious attempt was rejected: ${retryFeedback}\n` +
|
|
97
|
-
`Fix the issue and respond again.`;
|
|
98
|
-
}
|
|
99
|
-
let llmText;
|
|
100
|
-
let llmModelId;
|
|
101
|
-
try {
|
|
102
|
-
const result = await callLlm(RECLASSIFY_SYSTEM_PROMPT, userPrompt, {
|
|
103
|
-
max_tokens: 512,
|
|
104
|
-
...(modelId ? { model_id: modelId } : {}),
|
|
105
|
-
});
|
|
106
|
-
llmText = result.text;
|
|
107
|
-
llmModelId = result.model_id;
|
|
108
|
-
}
|
|
109
|
-
catch (error) {
|
|
110
|
-
return {
|
|
111
|
-
ok: false,
|
|
112
|
-
kind: "provider_error",
|
|
113
|
-
reason: `LLM unreachable: ${error instanceof Error ? error.message : String(error)}`,
|
|
114
|
-
};
|
|
115
|
-
}
|
|
116
|
-
let parsed;
|
|
117
|
-
try {
|
|
118
|
-
let cleaned = llmText.trim();
|
|
119
|
-
if (cleaned.startsWith("```")) {
|
|
120
|
-
cleaned = cleaned.replace(/^```(?:json)?\s*/, "").replace(/```\s*$/, "");
|
|
121
|
-
}
|
|
122
|
-
parsed = JSON.parse(cleaned);
|
|
123
|
-
}
|
|
124
|
-
catch (error) {
|
|
125
|
-
return {
|
|
126
|
-
ok: false,
|
|
127
|
-
kind: "validation_error",
|
|
128
|
-
reason: `malformed LLM JSON: ${error instanceof Error ? error.message : String(error)}`,
|
|
129
|
-
};
|
|
130
|
-
}
|
|
131
|
-
const role = parsed.proposed_role;
|
|
132
|
-
if (!VALID_ROLES.includes(role)) {
|
|
133
|
-
return {
|
|
134
|
-
ok: false,
|
|
135
|
-
kind: "validation_error",
|
|
136
|
-
reason: `invalid proposed_role "${String(parsed.proposed_role)}"`,
|
|
137
|
-
};
|
|
138
|
-
}
|
|
139
|
-
return {
|
|
140
|
-
ok: true,
|
|
141
|
-
role,
|
|
142
|
-
reason: typeof parsed.reason === "string" ? parsed.reason : "",
|
|
143
|
-
modelId: llmModelId,
|
|
144
|
-
};
|
|
145
|
-
}
|
|
146
|
-
async function classifyOne(item, modelId) {
|
|
147
|
-
// 1 retry on validation_error (malformed JSON or invalid role enum).
|
|
148
|
-
// Provider errors (network, auth) skip the retry — they're not the
|
|
149
|
-
// model's fault and retry won't help.
|
|
150
|
-
let llmCalls = 0;
|
|
151
|
-
const first = await attemptClassify(item, modelId);
|
|
152
|
-
llmCalls += 1;
|
|
153
|
-
let final = first;
|
|
154
|
-
if (!first.ok && first.kind === "validation_error") {
|
|
155
|
-
const second = await attemptClassify(item, modelId, first.reason);
|
|
156
|
-
llmCalls += 1;
|
|
157
|
-
final = second;
|
|
158
|
-
}
|
|
159
|
-
if (!final.ok) {
|
|
160
|
-
return { ok: false, reason: final.reason, llmCalls };
|
|
161
|
-
}
|
|
162
|
-
const userPrompt = buildReclassifyUserPrompt(item);
|
|
163
|
-
return {
|
|
164
|
-
ok: true,
|
|
165
|
-
llmCalls,
|
|
166
|
-
reclassification: {
|
|
167
|
-
agent_id: item.agent_id,
|
|
168
|
-
source_path: item.source_path,
|
|
169
|
-
line_number: item.line_number,
|
|
170
|
-
raw_line: item.raw_line,
|
|
171
|
-
current_role: item.role,
|
|
172
|
-
proposed_role: final.role,
|
|
173
|
-
reason: final.reason,
|
|
174
|
-
llm_model_id: final.modelId,
|
|
175
|
-
llm_prompt_hash: hashPrompt(RECLASSIFY_SYSTEM_PROMPT + "\n" + userPrompt),
|
|
176
|
-
},
|
|
177
|
-
};
|
|
178
|
-
}
|
|
179
|
-
// ---------------------------------------------------------------------------
|
|
180
|
-
// Public entry point
|
|
181
|
-
// ---------------------------------------------------------------------------
|
|
182
|
-
/**
|
|
183
|
-
* Run the reclassifier in analyze mode.
|
|
184
|
-
*
|
|
185
|
-
* Output is written to `<sessionRoot>/insight-reclassification-report.json`
|
|
186
|
-
* (unless dryRun). The apply phase (Phase B) is implemented in this same
|
|
187
|
-
* module as applyInsightReclassifications — it reads the report JSON and
|
|
188
|
-
* rewrites role tags in place. Originally planned to reuse Phase B's
|
|
189
|
-
* axis_tag_change applicator via promote-executor, but the standalone
|
|
190
|
-
* function path is simpler because single-line role rewrites don't need
|
|
191
|
-
* the full PromoteReport + PromoteDecisions infrastructure.
|
|
192
|
-
*/
|
|
193
|
-
export async function runInsightReclassifier(config) {
|
|
194
|
-
const sessionRoot = path.join(config.projectRoot, ".onto", "sessions", "reclassify-insights", config.sessionId);
|
|
195
|
-
fs.mkdirSync(sessionRoot, { recursive: true });
|
|
196
|
-
// Use the collector in reclassify-insights mode so candidate_items is
|
|
197
|
-
// pre-filtered to global insight items per DD-18 §SST.
|
|
198
|
-
const collection = collect({
|
|
199
|
-
mode: "reclassify-insights",
|
|
200
|
-
projectRoot: config.projectRoot,
|
|
201
|
-
});
|
|
202
|
-
let insights = collection.candidate_items;
|
|
203
|
-
if (config.targetAgent) {
|
|
204
|
-
insights = insights.filter((i) => i.agent_id === config.targetAgent);
|
|
205
|
-
}
|
|
206
|
-
if (config.batchSize !== undefined) {
|
|
207
|
-
insights = insights.slice(0, config.batchSize);
|
|
208
|
-
}
|
|
209
|
-
const reclassified = [];
|
|
210
|
-
const unclassified = [];
|
|
211
|
-
let llmCalls = 0;
|
|
212
|
-
if (config.dryRun) {
|
|
213
|
-
return {
|
|
214
|
-
session_id: config.sessionId,
|
|
215
|
-
session_root: sessionRoot,
|
|
216
|
-
total_insights: insights.length,
|
|
217
|
-
reclassified: [],
|
|
218
|
-
unclassified_pending: [],
|
|
219
|
-
llm_calls: 0,
|
|
220
|
-
dry_run: true,
|
|
221
|
-
};
|
|
222
|
-
}
|
|
223
|
-
for (const item of insights) {
|
|
224
|
-
const r = await classifyOne(item, config.modelId);
|
|
225
|
-
llmCalls += r.llmCalls;
|
|
226
|
-
if (r.ok) {
|
|
227
|
-
reclassified.push(r.reclassification);
|
|
228
|
-
}
|
|
229
|
-
else {
|
|
230
|
-
unclassified.push({ item, reason: r.reason });
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
const report = {
|
|
234
|
-
session_id: config.sessionId,
|
|
235
|
-
generated_at: new Date().toISOString(),
|
|
236
|
-
total_insights: insights.length,
|
|
237
|
-
reclassified,
|
|
238
|
-
unclassified_pending: unclassified.map((u) => ({
|
|
239
|
-
agent_id: u.item.agent_id,
|
|
240
|
-
source_path: u.item.source_path,
|
|
241
|
-
line_number: u.item.line_number,
|
|
242
|
-
reason: u.reason,
|
|
243
|
-
})),
|
|
244
|
-
llm_calls: llmCalls,
|
|
245
|
-
};
|
|
246
|
-
const reportPath = path.join(sessionRoot, "insight-reclassification-report.json");
|
|
247
|
-
fs.writeFileSync(reportPath, JSON.stringify(report, null, 2), "utf8");
|
|
248
|
-
// ontoHome is currently unused but kept on the config so a follow-up Step
|
|
249
|
-
// 13 patch can wire it through to the global learning file path resolver
|
|
250
|
-
// when the apply phase lands.
|
|
251
|
-
void config.ontoHome;
|
|
252
|
-
return {
|
|
253
|
-
session_id: config.sessionId,
|
|
254
|
-
session_root: sessionRoot,
|
|
255
|
-
total_insights: insights.length,
|
|
256
|
-
reclassified,
|
|
257
|
-
unclassified_pending: unclassified,
|
|
258
|
-
llm_calls: llmCalls,
|
|
259
|
-
dry_run: false,
|
|
260
|
-
};
|
|
261
|
-
}
|
|
262
|
-
/**
|
|
263
|
-
* Rewrite a single raw learning line, replacing the `[insight]` role
|
|
264
|
-
* bracket with the proposed role (or removing the bracket for drop_role).
|
|
265
|
-
*
|
|
266
|
-
* Returns null when the raw line does not contain an `[insight]` bracket
|
|
267
|
-
* at all — the caller should treat this as "already applied" to stay
|
|
268
|
-
* idempotent. Production usage is that the current_role was "insight" at
|
|
269
|
-
* classify time, so the bracket should exist unless a previous apply ran.
|
|
270
|
-
*/
|
|
271
|
-
export function rewriteInsightRoleTag(rawLine, proposedRole) {
|
|
272
|
-
const bracket = "[insight]";
|
|
273
|
-
if (!rawLine.includes(bracket))
|
|
274
|
-
return null;
|
|
275
|
-
if (proposedRole === "drop_role") {
|
|
276
|
-
// Remove the bracket plus one adjacent space so we don't leave a
|
|
277
|
-
// double-space in the output. Try trailing space first, then leading.
|
|
278
|
-
if (rawLine.includes(`${bracket} `)) {
|
|
279
|
-
return rawLine.replace(`${bracket} `, "");
|
|
280
|
-
}
|
|
281
|
-
if (rawLine.includes(` ${bracket}`)) {
|
|
282
|
-
return rawLine.replace(` ${bracket}`, "");
|
|
283
|
-
}
|
|
284
|
-
return rawLine.replace(bracket, "");
|
|
285
|
-
}
|
|
286
|
-
return rawLine.replace(bracket, `[${proposedRole}]`);
|
|
287
|
-
}
|
|
288
|
-
function resolveAnchor(fileLines, rec) {
|
|
289
|
-
const preRewrite = rec.raw_line;
|
|
290
|
-
const postRewrite = rec.proposed_role !== null
|
|
291
|
-
? rewriteInsightRoleTag(preRewrite, rec.proposed_role)
|
|
292
|
-
: null;
|
|
293
|
-
// Anchor #1: the 1-indexed line_number pointer.
|
|
294
|
-
const anchoredIdx = rec.line_number - 1;
|
|
295
|
-
if (anchoredIdx >= 0 && anchoredIdx < fileLines.length) {
|
|
296
|
-
const candidate = fileLines[anchoredIdx];
|
|
297
|
-
if (candidate === preRewrite) {
|
|
298
|
-
return {
|
|
299
|
-
kind: "match_original",
|
|
300
|
-
lineIndex: anchoredIdx,
|
|
301
|
-
anchor: "line_number_and_raw_line",
|
|
302
|
-
};
|
|
303
|
-
}
|
|
304
|
-
if (postRewrite !== null && candidate === postRewrite) {
|
|
305
|
-
return {
|
|
306
|
-
kind: "already_rewritten",
|
|
307
|
-
lineIndex: anchoredIdx,
|
|
308
|
-
anchor: "line_number_only",
|
|
309
|
-
};
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
// Anchor #2: verbatim raw_line scan fallback. Must be UNAMBIGUOUS.
|
|
313
|
-
let firstMatch = -1;
|
|
314
|
-
let matchCount = 0;
|
|
315
|
-
for (let i = 0; i < fileLines.length; i++) {
|
|
316
|
-
if (fileLines[i] === preRewrite) {
|
|
317
|
-
if (firstMatch === -1)
|
|
318
|
-
firstMatch = i;
|
|
319
|
-
matchCount += 1;
|
|
320
|
-
if (matchCount > 1)
|
|
321
|
-
break;
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
if (matchCount === 1 && firstMatch !== -1) {
|
|
325
|
-
return {
|
|
326
|
-
kind: "verbatim_fallback",
|
|
327
|
-
lineIndex: firstMatch,
|
|
328
|
-
anchor: "raw_line_scan",
|
|
329
|
-
};
|
|
330
|
-
}
|
|
331
|
-
if (matchCount > 1) {
|
|
332
|
-
return { kind: "ambiguous_raw_line", lineIndex: null, anchor: "none" };
|
|
333
|
-
}
|
|
334
|
-
// No anchor hit. File has drifted out from under the report.
|
|
335
|
-
return { kind: "drift", lineIndex: null, anchor: "none" };
|
|
336
|
-
}
|
|
337
|
-
/**
|
|
338
|
-
* Apply a reclassification report to the on-disk learning files.
|
|
339
|
-
*
|
|
340
|
-
* Reads the JSON report, walks each reclassification entry, resolves
|
|
341
|
-
* the target line via line_number + raw_line anchors (see resolveAnchor),
|
|
342
|
-
* and rewrites the `[insight]` role tag in the source file.
|
|
343
|
-
*
|
|
344
|
-
* Evidence-based idempotency (C3 + CC1):
|
|
345
|
-
* - applied / would_apply — original raw_line was located and rewritten
|
|
346
|
-
* (or would have been in dry-run)
|
|
347
|
-
* - skipped_already_applied — the proposed rewrite is already present at
|
|
348
|
-
* the recorded line_number (not just "line not found")
|
|
349
|
-
* - skipped_source_drift — neither original nor rewritten form matched.
|
|
350
|
-
* Operator must re-classify to resync.
|
|
351
|
-
*
|
|
352
|
-
* Atomicity: writes are per-file. Write failures roll back every applied
|
|
353
|
-
* entry for that file. Dry-run never writes.
|
|
354
|
-
*/
|
|
355
|
-
export function applyInsightReclassifications(config) {
|
|
356
|
-
const reportPath = config.reportPath;
|
|
357
|
-
if (!fs.existsSync(reportPath)) {
|
|
358
|
-
throw new Error(`Insight reclassification report not found: ${reportPath}`);
|
|
359
|
-
}
|
|
360
|
-
const raw = fs.readFileSync(reportPath, "utf8");
|
|
361
|
-
const report = JSON.parse(raw);
|
|
362
|
-
const reclassified = report.reclassified ?? [];
|
|
363
|
-
const dryRun = config.dryRun === true;
|
|
364
|
-
const entries = [];
|
|
365
|
-
let applied = 0;
|
|
366
|
-
let wouldApply = 0;
|
|
367
|
-
let skippedNoProposal = 0;
|
|
368
|
-
let skippedAlreadyApplied = 0;
|
|
369
|
-
let skippedSourceDrift = 0;
|
|
370
|
-
let failed = 0;
|
|
371
|
-
// Group by source_path so we load each file at most once per run.
|
|
372
|
-
const byFile = new Map();
|
|
373
|
-
for (const r of reclassified) {
|
|
374
|
-
const bucket = byFile.get(r.source_path);
|
|
375
|
-
if (bucket)
|
|
376
|
-
bucket.push(r);
|
|
377
|
-
else
|
|
378
|
-
byFile.set(r.source_path, [r]);
|
|
379
|
-
}
|
|
380
|
-
for (const [filePath, items] of byFile) {
|
|
381
|
-
let fileLines = null;
|
|
382
|
-
if (fs.existsSync(filePath)) {
|
|
383
|
-
fileLines = fs.readFileSync(filePath, "utf8").split("\n");
|
|
384
|
-
}
|
|
385
|
-
for (const r of items) {
|
|
386
|
-
if (r.proposed_role === null) {
|
|
387
|
-
entries.push({
|
|
388
|
-
agent_id: r.agent_id,
|
|
389
|
-
source_path: r.source_path,
|
|
390
|
-
line_number: r.line_number,
|
|
391
|
-
raw_line: r.raw_line,
|
|
392
|
-
current_role: r.current_role,
|
|
393
|
-
proposed_role: null,
|
|
394
|
-
outcome: "skipped_no_proposal",
|
|
395
|
-
anchor_resolution: "none",
|
|
396
|
-
new_line: null,
|
|
397
|
-
error_message: null,
|
|
398
|
-
});
|
|
399
|
-
skippedNoProposal += 1;
|
|
400
|
-
continue;
|
|
401
|
-
}
|
|
402
|
-
if (fileLines === null) {
|
|
403
|
-
entries.push({
|
|
404
|
-
agent_id: r.agent_id,
|
|
405
|
-
source_path: r.source_path,
|
|
406
|
-
line_number: r.line_number,
|
|
407
|
-
raw_line: r.raw_line,
|
|
408
|
-
current_role: r.current_role,
|
|
409
|
-
proposed_role: r.proposed_role,
|
|
410
|
-
outcome: "failed",
|
|
411
|
-
anchor_resolution: "none",
|
|
412
|
-
new_line: null,
|
|
413
|
-
error_message: `source file not found: ${filePath}`,
|
|
414
|
-
});
|
|
415
|
-
failed += 1;
|
|
416
|
-
continue;
|
|
417
|
-
}
|
|
418
|
-
const resolution = resolveAnchor(fileLines, r);
|
|
419
|
-
// Already-rewritten: evidence-based idempotency success
|
|
420
|
-
if (resolution.kind === "already_rewritten") {
|
|
421
|
-
entries.push({
|
|
422
|
-
agent_id: r.agent_id,
|
|
423
|
-
source_path: r.source_path,
|
|
424
|
-
line_number: r.line_number,
|
|
425
|
-
raw_line: r.raw_line,
|
|
426
|
-
current_role: r.current_role,
|
|
427
|
-
proposed_role: r.proposed_role,
|
|
428
|
-
outcome: "skipped_already_applied",
|
|
429
|
-
anchor_resolution: resolution.anchor,
|
|
430
|
-
new_line: null,
|
|
431
|
-
error_message: null,
|
|
432
|
-
});
|
|
433
|
-
skippedAlreadyApplied += 1;
|
|
434
|
-
continue;
|
|
435
|
-
}
|
|
436
|
-
// Drift or ambiguous: fail safe
|
|
437
|
-
if (resolution.kind === "drift" ||
|
|
438
|
-
resolution.kind === "ambiguous_raw_line") {
|
|
439
|
-
entries.push({
|
|
440
|
-
agent_id: r.agent_id,
|
|
441
|
-
source_path: r.source_path,
|
|
442
|
-
line_number: r.line_number,
|
|
443
|
-
raw_line: r.raw_line,
|
|
444
|
-
current_role: r.current_role,
|
|
445
|
-
proposed_role: r.proposed_role,
|
|
446
|
-
outcome: "skipped_source_drift",
|
|
447
|
-
anchor_resolution: resolution.anchor,
|
|
448
|
-
new_line: null,
|
|
449
|
-
error_message: resolution.kind === "ambiguous_raw_line"
|
|
450
|
-
? "multiple verbatim matches for raw_line (line_number drifted)"
|
|
451
|
-
: "neither line_number anchor nor verbatim raw_line matched",
|
|
452
|
-
});
|
|
453
|
-
skippedSourceDrift += 1;
|
|
454
|
-
continue;
|
|
455
|
-
}
|
|
456
|
-
// Match confirmed — compute the rewrite
|
|
457
|
-
const lineIdx = resolution.lineIndex;
|
|
458
|
-
const newLine = rewriteInsightRoleTag(r.raw_line, r.proposed_role);
|
|
459
|
-
if (newLine === null) {
|
|
460
|
-
// raw_line has no [insight] bracket. The anchor matched so we
|
|
461
|
-
// know this is the right line but the rewrite is a no-op —
|
|
462
|
-
// treat as already applied for safety.
|
|
463
|
-
entries.push({
|
|
464
|
-
agent_id: r.agent_id,
|
|
465
|
-
source_path: r.source_path,
|
|
466
|
-
line_number: r.line_number,
|
|
467
|
-
raw_line: r.raw_line,
|
|
468
|
-
current_role: r.current_role,
|
|
469
|
-
proposed_role: r.proposed_role,
|
|
470
|
-
outcome: "skipped_already_applied",
|
|
471
|
-
anchor_resolution: resolution.anchor,
|
|
472
|
-
new_line: null,
|
|
473
|
-
error_message: null,
|
|
474
|
-
});
|
|
475
|
-
skippedAlreadyApplied += 1;
|
|
476
|
-
continue;
|
|
477
|
-
}
|
|
478
|
-
if (dryRun) {
|
|
479
|
-
// Do NOT mutate fileLines in dry-run. Record preview only.
|
|
480
|
-
entries.push({
|
|
481
|
-
agent_id: r.agent_id,
|
|
482
|
-
source_path: r.source_path,
|
|
483
|
-
line_number: r.line_number,
|
|
484
|
-
raw_line: r.raw_line,
|
|
485
|
-
current_role: r.current_role,
|
|
486
|
-
proposed_role: r.proposed_role,
|
|
487
|
-
outcome: "would_apply",
|
|
488
|
-
anchor_resolution: resolution.anchor,
|
|
489
|
-
new_line: newLine,
|
|
490
|
-
error_message: null,
|
|
491
|
-
});
|
|
492
|
-
wouldApply += 1;
|
|
493
|
-
continue;
|
|
494
|
-
}
|
|
495
|
-
// Real apply
|
|
496
|
-
fileLines[lineIdx] = newLine;
|
|
497
|
-
entries.push({
|
|
498
|
-
agent_id: r.agent_id,
|
|
499
|
-
source_path: r.source_path,
|
|
500
|
-
line_number: r.line_number,
|
|
501
|
-
raw_line: r.raw_line,
|
|
502
|
-
current_role: r.current_role,
|
|
503
|
-
proposed_role: r.proposed_role,
|
|
504
|
-
outcome: "applied",
|
|
505
|
-
anchor_resolution: resolution.anchor,
|
|
506
|
-
new_line: newLine,
|
|
507
|
-
error_message: null,
|
|
508
|
-
});
|
|
509
|
-
applied += 1;
|
|
510
|
-
}
|
|
511
|
-
// Flush the file if any line was mutated AND we're not in dry-run.
|
|
512
|
-
if (!dryRun &&
|
|
513
|
-
fileLines !== null &&
|
|
514
|
-
entries.some((e) => e.source_path === filePath && e.outcome === "applied")) {
|
|
515
|
-
try {
|
|
516
|
-
fs.writeFileSync(filePath, fileLines.join("\n"), "utf8");
|
|
517
|
-
}
|
|
518
|
-
catch (error) {
|
|
519
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
520
|
-
for (const e of entries) {
|
|
521
|
-
if (e.source_path === filePath && e.outcome === "applied") {
|
|
522
|
-
e.outcome = "failed";
|
|
523
|
-
e.error_message = `write failed: ${message}`;
|
|
524
|
-
applied -= 1;
|
|
525
|
-
failed += 1;
|
|
526
|
-
}
|
|
527
|
-
}
|
|
528
|
-
}
|
|
529
|
-
}
|
|
530
|
-
}
|
|
531
|
-
return {
|
|
532
|
-
report_path: reportPath,
|
|
533
|
-
report_generated_at: typeof report.generated_at === "string" ? report.generated_at : null,
|
|
534
|
-
total_entries: reclassified.length,
|
|
535
|
-
applied,
|
|
536
|
-
would_apply: wouldApply,
|
|
537
|
-
skipped_no_proposal: skippedNoProposal,
|
|
538
|
-
skipped_already_applied: skippedAlreadyApplied,
|
|
539
|
-
skipped_source_drift: skippedSourceDrift,
|
|
540
|
-
failed,
|
|
541
|
-
entries,
|
|
542
|
-
dry_run: dryRun,
|
|
543
|
-
};
|
|
544
|
-
}
|