@remnic/core 9.3.729 → 9.3.731
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/dist/access-boundary.d.ts +6 -5
- package/dist/access-boundary.js +13 -13
- package/dist/access-cli.js +34 -33
- package/dist/access-cli.js.map +1 -1
- package/dist/access-http.d.ts +6 -5
- package/dist/access-http.js +19 -19
- package/dist/access-mcp.d.ts +6 -5
- package/dist/access-mcp.js +18 -18
- package/dist/access-operations-batch.js +16 -16
- package/dist/access-operations.d.ts +11 -10
- package/dist/access-operations.js +17 -17
- package/dist/access-schema.d.ts +42 -42
- package/dist/{access-service-BXMJqkTA.d.ts → access-service-Byoe3HsT.d.ts} +18 -3
- package/dist/access-service.d.ts +6 -5
- package/dist/access-service.js +12 -12
- package/dist/access-surface-catalog.d.ts +6 -5
- package/dist/action-confidence.d.ts +1 -1
- package/dist/active-memory-bridge.d.ts +1 -1
- package/dist/active-recall.d.ts +1 -1
- package/dist/active-recall.js +2 -2
- package/dist/{auto-sync-F3KCSQFJ.js → auto-sync-G3V5AN4C.js} +2 -2
- package/dist/behavior-learner.d.ts +1 -1
- package/dist/behavior-signals.d.ts +1 -1
- package/dist/bootstrap.d.ts +5 -4
- package/dist/briefing.d.ts +1 -1
- package/dist/briefing.js +3 -3
- package/dist/buffer-surprise-report.d.ts +1 -1
- package/dist/buffer.d.ts +1 -1
- package/dist/calibration.d.ts +1 -1
- package/dist/capabilities.d.ts +55 -2
- package/dist/capabilities.js +5 -1
- package/dist/{catalog-bMczlO2G.d.ts → catalog-DM1nk7cM.d.ts} +1 -1
- package/dist/causal-behavior.d.ts +1 -1
- package/dist/causal-consolidation.d.ts +1 -1
- package/dist/causal-consolidation.js +4 -4
- package/dist/causal-trajectory-graph.d.ts +1 -1
- package/dist/{chunk-4NOI577P.js → chunk-24ZLC5QN.js} +2 -2
- package/dist/{chunk-DMQWV6SQ.js → chunk-5RZOFIDL.js} +2 -2
- package/dist/{chunk-UM5AWJNQ.js → chunk-646B5ERJ.js} +62 -37
- package/dist/chunk-646B5ERJ.js.map +1 -0
- package/dist/{chunk-H5BWGCRZ.js → chunk-6TUWSUUD.js} +51 -47
- package/dist/{chunk-H5BWGCRZ.js.map → chunk-6TUWSUUD.js.map} +1 -1
- package/dist/{chunk-36SEDFAU.js → chunk-C5WN74HH.js} +2 -2
- package/dist/{chunk-MLKWJOAJ.js → chunk-CGPHMNL4.js} +2 -2
- package/dist/{chunk-R6OVFAX6.js → chunk-DJECGBBM.js} +7 -2
- package/dist/{chunk-R6OVFAX6.js.map → chunk-DJECGBBM.js.map} +1 -1
- package/dist/{chunk-3RCBIOOJ.js → chunk-ESF7TRYJ.js} +18 -2
- package/dist/{chunk-3RCBIOOJ.js.map → chunk-ESF7TRYJ.js.map} +1 -1
- package/dist/{chunk-EY6MDMHK.js → chunk-F6OEXJQQ.js} +2 -2
- package/dist/{chunk-M4V2JVSZ.js → chunk-FFUCCKPN.js} +2 -2
- package/dist/{chunk-O6YH5F47.js → chunk-FYWUNVFZ.js} +8 -8
- package/dist/{chunk-37436GML.js → chunk-GEXI357K.js} +2 -2
- package/dist/{chunk-YGIQORJ3.js → chunk-GGL34GCW.js} +3 -3
- package/dist/{chunk-4H4F3BFL.js → chunk-GXTYEG7Q.js} +3 -3
- package/dist/{chunk-4H4F3BFL.js.map → chunk-GXTYEG7Q.js.map} +1 -1
- package/dist/{chunk-WAS3NV46.js → chunk-H3MB3F62.js} +2 -2
- package/dist/{chunk-UTHQPSRC.js → chunk-HHNSLPX7.js} +11 -8
- package/dist/chunk-HHNSLPX7.js.map +1 -0
- package/dist/{chunk-76NHR6ZL.js → chunk-HJWQJUKQ.js} +4 -4
- package/dist/{chunk-Y4YD5TYW.js → chunk-IIBFCZA7.js} +3 -3
- package/dist/{chunk-UVPL565C.js → chunk-IQOEW5YV.js} +2 -2
- package/dist/{chunk-V4GNSDFH.js → chunk-KQYGNGON.js} +2 -2
- package/dist/{chunk-CBWI22JN.js → chunk-KSJCXXOU.js} +2 -2
- package/dist/{chunk-M4AYVEWS.js → chunk-LUCARK4W.js} +12 -11
- package/dist/chunk-LUCARK4W.js.map +1 -0
- package/dist/{chunk-I5REUWLH.js → chunk-NIZQH6AU.js} +3 -3
- package/dist/{chunk-4ESAR77N.js → chunk-NOU77OHX.js} +2 -2
- package/dist/{chunk-BQCHEV7O.js → chunk-O4ZIBPIW.js} +3 -3
- package/dist/{chunk-HOHVIVDR.js → chunk-OBJUKYDO.js} +2 -2
- package/dist/{chunk-YKBHOBJK.js → chunk-OEHCMALU.js} +56 -4
- package/dist/chunk-OEHCMALU.js.map +1 -0
- package/dist/{chunk-YOLZS7NY.js → chunk-OTQE66IX.js} +2 -2
- package/dist/{chunk-HEDZUSCS.js → chunk-OUITHS6V.js} +246 -368
- package/dist/chunk-OUITHS6V.js.map +1 -0
- package/dist/{chunk-4T3BMHKD.js → chunk-OZECYVGI.js} +20 -7
- package/dist/chunk-OZECYVGI.js.map +1 -0
- package/dist/{chunk-C2PG7IEH.js → chunk-P3VJCTVE.js} +2 -2
- package/dist/{chunk-EH5ED3C6.js → chunk-PMAQRUN4.js} +4 -4
- package/dist/chunk-PMAQRUN4.js.map +1 -0
- package/dist/{chunk-7RLCCNHU.js → chunk-Q6BAA57B.js} +4 -3
- package/dist/chunk-Q6BAA57B.js.map +1 -0
- package/dist/{chunk-HGNOCPZX.js → chunk-QZHF4BWN.js} +6 -6
- package/dist/chunk-SYX73XD7.js +283 -0
- package/dist/chunk-SYX73XD7.js.map +1 -0
- package/dist/{chunk-ASKXIAHD.js → chunk-WDJCZQB4.js} +2 -2
- package/dist/{chunk-AQLOP5F4.js → chunk-WNGXQMRI.js} +95 -6
- package/dist/chunk-WNGXQMRI.js.map +1 -0
- package/dist/{chunk-JYLFZWUX.js → chunk-XYUFGHUW.js} +2 -2
- package/dist/{chunk-3TSTGI6F.js → chunk-ZQUAVZNG.js} +2 -2
- package/dist/{chunk-4NR2PDZ5.js → chunk-ZVHRSR2R.js} +14 -4
- package/dist/chunk-ZVHRSR2R.js.map +1 -0
- package/dist/{cli-DmAiDd8A.d.ts → cli-Dt-tuiYH.d.ts} +3 -3
- package/dist/cli.d.ts +7 -6
- package/dist/cli.js +35 -35
- package/dist/compounding/engine.d.ts +12 -1
- package/dist/compounding/engine.js +3 -3
- package/dist/compounding/preference-consolidator.d.ts +1 -1
- package/dist/compression-optimizer.d.ts +1 -1
- package/dist/config.d.ts +1 -1
- package/dist/config.js +2 -2
- package/dist/connectors/codex-materialize-runner.d.ts +1 -1
- package/dist/connectors/codex-materialize-runner.js +3 -3
- package/dist/connectors/codex-materialize.d.ts +1 -1
- package/dist/connectors/index.d.ts +1 -1
- package/dist/connectors/index.js +3 -3
- package/dist/consolidation-provenance-check.d.ts +1 -1
- package/dist/consolidation-undo.d.ts +1 -1
- package/dist/contradiction/index.d.ts +1 -1
- package/dist/contradiction/index.js +1 -1
- package/dist/conversation-index/backend.d.ts +1 -1
- package/dist/conversation-index/chunker.d.ts +1 -1
- package/dist/conversation-index/faiss-adapter.d.ts +1 -1
- package/dist/conversation-index/indexer.d.ts +1 -1
- package/dist/conversation-index/search.d.ts +1 -1
- package/dist/day-summary.d.ts +1 -1
- package/dist/delinearize.d.ts +1 -1
- package/dist/direct-answer-wiring.d.ts +1 -1
- package/dist/direct-answer.d.ts +1 -1
- package/dist/embedding-fallback.d.ts +1 -1
- package/dist/embedding-fallback.js +2 -2
- package/dist/enrichment/index.d.ts +1 -1
- package/dist/entity-retrieval.d.ts +1 -1
- package/dist/entity-retrieval.js +3 -3
- package/dist/entity-schema.d.ts +1 -1
- package/dist/explicit-capture.d.ts +6 -4
- package/dist/explicit-capture.js +1 -1
- package/dist/extraction-faithfulness.d.ts +1 -1
- package/dist/extraction-judge-telemetry.d.ts +1 -1
- package/dist/extraction-judge-training.d.ts +1 -1
- package/dist/extraction-judge.d.ts +1 -1
- package/dist/extraction.d.ts +1 -1
- package/dist/extraction.js +3 -3
- package/dist/fallback-llm.d.ts +1 -1
- package/dist/{first-start-migration-22LPFHD5.js → first-start-migration-OOR57AYR.js} +2 -2
- package/dist/graph-dashboard-diff.d.ts +1 -1
- package/dist/graph-dashboard-key.d.ts +1 -1
- package/dist/graph-dashboard-parser.d.ts +1 -1
- package/dist/graph-edge-reinforcement.d.ts +1 -1
- package/dist/graph-snapshot.d.ts +1 -1
- package/dist/graph.d.ts +1 -1
- package/dist/identity-continuity.d.ts +1 -1
- package/dist/importance.d.ts +1 -1
- package/dist/index.d.ts +296 -11
- package/dist/index.js +359 -47
- package/dist/index.js.map +1 -1
- package/dist/intent.d.ts +1 -1
- package/dist/lcm/engine.d.ts +1 -1
- package/dist/lcm/index.d.ts +1 -1
- package/dist/lcm/tools.d.ts +1 -1
- package/dist/lifecycle.d.ts +1 -1
- package/dist/live-connectors-runner.d.ts +1 -1
- package/dist/local-llm.d.ts +1 -1
- package/dist/local-model-endpoint.d.ts +1 -1
- package/dist/maintenance/memory-governance.d.ts +1 -1
- package/dist/maintenance/memory-governance.js +3 -3
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +3 -3
- package/dist/maintenance/rebuild-memory-projection.js +4 -4
- package/dist/mcp-memory-inspector-app.d.ts +6 -5
- package/dist/memory-action-policy.d.ts +1 -1
- package/dist/memory-cache.d.ts +1 -1
- package/dist/memory-lifecycle-ledger-utils.d.ts +1 -1
- package/dist/memory-projection-store.d.ts +1 -1
- package/dist/memory-provenance.d.ts +1 -1
- package/dist/memory-worth-outcomes.d.ts +1 -1
- package/dist/models-json.d.ts +1 -1
- package/dist/namespaces/migrate.d.ts +2 -2
- package/dist/namespaces/migrate.js +9 -9
- package/dist/namespaces/principal.d.ts +1 -1
- package/dist/namespaces/search.d.ts +1 -1
- package/dist/namespaces/search.js +5 -5
- package/dist/namespaces/storage.d.ts +2 -2
- package/dist/namespaces/storage.js +3 -3
- package/dist/native-knowledge.d.ts +1 -1
- package/dist/operator-toolkit.d.ts +1 -1
- package/dist/operator-toolkit.js +14 -14
- package/dist/orchestration/compression-guideline-coordinator.d.ts +105 -0
- package/dist/orchestration/compression-guideline-coordinator.js +12 -0
- package/dist/orchestration/maintenance.d.ts +2 -2
- package/dist/orchestration/maintenance.js +5 -5
- package/dist/{orchestrator-BR0sGsME.d.ts → orchestrator-B4KjaOtY.d.ts} +24 -8
- package/dist/orchestrator.d.ts +5 -4
- package/dist/orchestrator.js +28 -26
- package/dist/patterns-cli.d.ts +1 -1
- package/dist/policy-runtime.d.ts +1 -1
- package/dist/provenance.d.ts +1 -1
- package/dist/provenance.js +1 -1
- package/dist/qmd-recall-cache.d.ts +1 -1
- package/dist/qmd.d.ts +1 -1
- package/dist/recall-disclosure-escalation.d.ts +1 -1
- package/dist/recall-explain-renderer.d.ts +1 -1
- package/dist/recall-planner-llm.d.ts +1 -1
- package/dist/recall-state.d.ts +1 -1
- package/dist/recall-tag-filter.d.ts +1 -1
- package/dist/recall-xray-cli.d.ts +1 -1
- package/dist/recall-xray-renderer.d.ts +1 -1
- package/dist/recall-xray.d.ts +1 -1
- package/dist/{resolution-MN36NW5P.js → resolution-33PIX3TT.js} +2 -2
- package/dist/resolution-33PIX3TT.js.map +1 -0
- package/dist/resolve-auth-token.d.ts +1 -1
- package/dist/resume-bundles.js +3 -3
- package/dist/retrieval-agents.d.ts +1 -1
- package/dist/retrieval-tiers.d.ts +1 -1
- package/dist/routing/engine.d.ts +1 -1
- package/dist/routing/store.d.ts +1 -1
- package/dist/schemas.d.ts +84 -84
- package/dist/search/embed-helper.d.ts +1 -1
- package/dist/search/embed-helper.js +2 -2
- package/dist/search/factory.d.ts +1 -1
- package/dist/search/factory.js +4 -4
- package/dist/search/index.d.ts +1 -1
- package/dist/search/index.js +8 -8
- package/dist/search/lancedb-backend.d.ts +1 -1
- package/dist/search/meilisearch-backend.d.ts +1 -1
- package/dist/search/noop-backend.d.ts +1 -1
- package/dist/search/orama-backend.d.ts +1 -1
- package/dist/search/port.d.ts +1 -1
- package/dist/search/remote-backend.d.ts +1 -1
- package/dist/{semantic-consolidation-Cl15ksZy.d.ts → semantic-consolidation-CstTvHPo.d.ts} +1 -1
- package/dist/semantic-consolidation.d.ts +2 -2
- package/dist/semantic-consolidation.js +4 -4
- package/dist/semantic-rule-promotion.js +3 -3
- package/dist/semantic-rule-verifier.d.ts +1 -1
- package/dist/semantic-rule-verifier.js +3 -3
- package/dist/session-observer-bands.d.ts +1 -1
- package/dist/session-observer-state.d.ts +1 -1
- package/dist/shared-context/manager.d.ts +3 -3
- package/dist/signal.d.ts +1 -1
- package/dist/storage.d.ts +22 -3
- package/dist/storage.js +2 -2
- package/dist/summarizer.d.ts +1 -1
- package/dist/summary-snapshot.d.ts +1 -1
- package/dist/temporal-supersession.d.ts +1 -1
- package/dist/temporal-validity.d.ts +1 -1
- package/dist/threading.d.ts +1 -1
- package/dist/tier-migration.d.ts +1 -1
- package/dist/tier-routing.d.ts +1 -1
- package/dist/topics.d.ts +1 -1
- package/dist/transcript.d.ts +1 -1
- package/dist/transfer/types.d.ts +46 -46
- package/dist/trust-score-stage.d.ts +1 -1
- package/dist/trust-score.d.ts +1 -1
- package/dist/{types-ChQb9D6s.d.ts → types-VkxElCHR.d.ts} +8 -1
- package/dist/types.d.ts +1 -1
- package/dist/utility-runtime.d.ts +1 -1
- package/dist/verified-recall.js +3 -3
- package/package.json +12 -2
- package/src/access-service-coding-write.test.ts +2 -2
- package/src/access-service.ts +33 -14
- package/src/bitemporal-dedup-backfill.test.ts +10 -10
- package/src/bitemporal-frontmatter.test.ts +3 -3
- package/src/capabilities.test.ts +185 -0
- package/src/capabilities.ts +110 -0
- package/src/cli/creation-ledger-commands.ts +22 -21
- package/src/coding/architecture-surfaces.test.ts +25 -1
- package/src/coding/architecture-surfaces.ts +47 -19
- package/src/coding/decision-surfaces.ts +12 -3
- package/src/coding/review-context-structural.test.ts +303 -0
- package/src/coding/review-context.ts +79 -0
- package/src/coding/structural-context.test.ts +259 -0
- package/src/coding/structural-context.ts +315 -0
- package/src/coding/structural-subprocess-provider.test.ts +430 -0
- package/src/coding/structural-subprocess-provider.ts +355 -0
- package/src/compounding/engine.test.ts +90 -0
- package/src/compounding/engine.ts +25 -1
- package/src/contradiction/contradiction.test.ts +1 -1
- package/src/contradiction/resolution.ts +9 -1
- package/src/correction/correction-access-wiring.ts +21 -3
- package/src/correction/correction-contract.ts +7 -0
- package/src/correction/correction-planner.test.ts +231 -0
- package/src/correction/correction-planner.ts +73 -0
- package/src/correction/correction-service.ts +29 -0
- package/src/correction/correction-storage-integration.test.ts +2 -2
- package/src/dedup/semantic.test.ts +1 -1
- package/src/explicit-capture.ts +7 -4
- package/src/extraction-eventtime-wiring.test.ts +3 -3
- package/src/extraction-redaction-rules.test.ts +172 -0
- package/src/index.ts +29 -0
- package/src/maintenance/forget.test.ts +1 -1
- package/src/maintenance/memory-governance.test.ts +1 -1
- package/src/maintenance/pattern-reinforcement.test.ts +2 -2
- package/src/operator-toolkit.ts +4 -3
- package/src/orchestration/compression-guideline-coordinator.ts +449 -0
- package/src/orchestrator.ts +345 -464
- package/src/procedural/procedure-miner.test.ts +2 -2
- package/src/provenance-extraction.test.ts +382 -4
- package/src/provenance.ts +158 -22
- package/src/recall-handles-wiring.test.ts +1 -1
- package/src/search/factory.ts +4 -3
- package/src/semantic-rule-promotion.ts +1 -1
- package/src/source-attribution-roundtrip.test.ts +2 -2
- package/src/storage-contract/atomicity.test.ts +1 -1
- package/src/storage-contract/cache-coherence.test.ts +2 -2
- package/src/storage-contract/content-hash.test.ts +1 -1
- package/src/storage-contract/namespace-routing.test.ts +1 -1
- package/src/storage-contract/round-trip.test.ts +3 -3
- package/src/storage.ts +23 -2
- package/src/temporal-supersession.test.ts +12 -11
- package/src/temporal-validity.test.ts +11 -11
- package/src/wearables/cli.test.ts +30 -0
- package/src/wearables/cli.ts +3 -0
- package/src/wearables/memory-gen.test.ts +57 -1
- package/src/wearables/memory-gen.ts +31 -7
- package/src/wearables/pipeline.test.ts +40 -1
- package/src/wearables/pipeline.ts +4 -0
- package/src/wearables/service.test.ts +2 -2
- package/src/wearables/storage-io.test.ts +3 -3
- package/src/wearables/types.ts +7 -0
- package/dist/chunk-4NR2PDZ5.js.map +0 -1
- package/dist/chunk-4T3BMHKD.js.map +0 -1
- package/dist/chunk-7RLCCNHU.js.map +0 -1
- package/dist/chunk-AQLOP5F4.js.map +0 -1
- package/dist/chunk-EH5ED3C6.js.map +0 -1
- package/dist/chunk-HEDZUSCS.js.map +0 -1
- package/dist/chunk-M4AYVEWS.js.map +0 -1
- package/dist/chunk-UM5AWJNQ.js.map +0 -1
- package/dist/chunk-UTHQPSRC.js.map +0 -1
- package/dist/chunk-YKBHOBJK.js.map +0 -1
- /package/dist/{auto-sync-F3KCSQFJ.js.map → auto-sync-G3V5AN4C.js.map} +0 -0
- /package/dist/{chunk-4NOI577P.js.map → chunk-24ZLC5QN.js.map} +0 -0
- /package/dist/{chunk-DMQWV6SQ.js.map → chunk-5RZOFIDL.js.map} +0 -0
- /package/dist/{chunk-36SEDFAU.js.map → chunk-C5WN74HH.js.map} +0 -0
- /package/dist/{chunk-MLKWJOAJ.js.map → chunk-CGPHMNL4.js.map} +0 -0
- /package/dist/{chunk-EY6MDMHK.js.map → chunk-F6OEXJQQ.js.map} +0 -0
- /package/dist/{chunk-M4V2JVSZ.js.map → chunk-FFUCCKPN.js.map} +0 -0
- /package/dist/{chunk-O6YH5F47.js.map → chunk-FYWUNVFZ.js.map} +0 -0
- /package/dist/{chunk-37436GML.js.map → chunk-GEXI357K.js.map} +0 -0
- /package/dist/{chunk-YGIQORJ3.js.map → chunk-GGL34GCW.js.map} +0 -0
- /package/dist/{chunk-WAS3NV46.js.map → chunk-H3MB3F62.js.map} +0 -0
- /package/dist/{chunk-76NHR6ZL.js.map → chunk-HJWQJUKQ.js.map} +0 -0
- /package/dist/{chunk-Y4YD5TYW.js.map → chunk-IIBFCZA7.js.map} +0 -0
- /package/dist/{chunk-UVPL565C.js.map → chunk-IQOEW5YV.js.map} +0 -0
- /package/dist/{chunk-V4GNSDFH.js.map → chunk-KQYGNGON.js.map} +0 -0
- /package/dist/{chunk-CBWI22JN.js.map → chunk-KSJCXXOU.js.map} +0 -0
- /package/dist/{chunk-I5REUWLH.js.map → chunk-NIZQH6AU.js.map} +0 -0
- /package/dist/{chunk-4ESAR77N.js.map → chunk-NOU77OHX.js.map} +0 -0
- /package/dist/{chunk-BQCHEV7O.js.map → chunk-O4ZIBPIW.js.map} +0 -0
- /package/dist/{chunk-HOHVIVDR.js.map → chunk-OBJUKYDO.js.map} +0 -0
- /package/dist/{chunk-YOLZS7NY.js.map → chunk-OTQE66IX.js.map} +0 -0
- /package/dist/{chunk-C2PG7IEH.js.map → chunk-P3VJCTVE.js.map} +0 -0
- /package/dist/{chunk-HGNOCPZX.js.map → chunk-QZHF4BWN.js.map} +0 -0
- /package/dist/{chunk-ASKXIAHD.js.map → chunk-WDJCZQB4.js.map} +0 -0
- /package/dist/{chunk-JYLFZWUX.js.map → chunk-XYUFGHUW.js.map} +0 -0
- /package/dist/{chunk-3TSTGI6F.js.map → chunk-ZQUAVZNG.js.map} +0 -0
- /package/dist/{first-start-migration-22LPFHD5.js.map → first-start-migration-OOR57AYR.js.map} +0 -0
- /package/dist/{resolution-MN36NW5P.js.map → orchestration/compression-guideline-coordinator.js.map} +0 -0
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for the structural-context provider PORT (issue #1548 Track A PR 5).
|
|
3
|
+
*
|
|
4
|
+
* Pure module under test — registry, gate predicate, and the config-only
|
|
5
|
+
* doctor status summariser. The subprocess adapter and the review-context
|
|
6
|
+
* wiring have their own test files. No filesystem, no subprocess.
|
|
7
|
+
*
|
|
8
|
+
* Standards: #1520. Rule 34 (degraded ≠ empty) is the load-bearing invariant
|
|
9
|
+
* for every tagged outcome here.
|
|
10
|
+
*/
|
|
11
|
+
import assert from "node:assert/strict";
|
|
12
|
+
import { afterEach, test } from "node:test";
|
|
13
|
+
|
|
14
|
+
import type { PluginConfig } from "../types.js";
|
|
15
|
+
import {
|
|
16
|
+
clearStructuralContextProvidersForTest,
|
|
17
|
+
describeStructuralProviderStatus,
|
|
18
|
+
getStructuralContextProvider,
|
|
19
|
+
registerStructuralContextProvider,
|
|
20
|
+
renderStructuralProviderStatusLine,
|
|
21
|
+
structuralProviderActive,
|
|
22
|
+
toStructuralContextDegradation,
|
|
23
|
+
type StructuralContextProvider,
|
|
24
|
+
type SymbolsForDiffResult,
|
|
25
|
+
} from "./structural-context.js";
|
|
26
|
+
|
|
27
|
+
function configWith(
|
|
28
|
+
codingKnowledge: Partial<PluginConfig["codingKnowledge"]>,
|
|
29
|
+
): PluginConfig {
|
|
30
|
+
return {
|
|
31
|
+
codingKnowledge: {
|
|
32
|
+
enabled: false,
|
|
33
|
+
decisionRecords: true,
|
|
34
|
+
architectureCard: true,
|
|
35
|
+
sessionDelta: true,
|
|
36
|
+
architectureCardLlmSummary: false,
|
|
37
|
+
structuralProvider: "none",
|
|
38
|
+
structuralProviderCommand: "",
|
|
39
|
+
codegraphTools: false,
|
|
40
|
+
codegraphDbDir: "",
|
|
41
|
+
...codingKnowledge,
|
|
42
|
+
},
|
|
43
|
+
} as unknown as PluginConfig;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function fakeProvider(
|
|
47
|
+
id: string,
|
|
48
|
+
symbolsResult: SymbolsForDiffResult,
|
|
49
|
+
): StructuralContextProvider {
|
|
50
|
+
return {
|
|
51
|
+
id,
|
|
52
|
+
async probe() {
|
|
53
|
+
return { available: true };
|
|
54
|
+
},
|
|
55
|
+
async symbolsForDiff() {
|
|
56
|
+
return symbolsResult;
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
afterEach(() => {
|
|
62
|
+
clearStructuralContextProvidersForTest();
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
// ──────────────────────────────────────────────────────────────────────────
|
|
66
|
+
// Gate predicate (rule 39 — one predicate, identical on every path)
|
|
67
|
+
// ──────────────────────────────────────────────────────────────────────────
|
|
68
|
+
|
|
69
|
+
test("structuralProviderActive: false when master gate off (default)", () => {
|
|
70
|
+
assert.equal(structuralProviderActive(configWith({})), false);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
test("structuralProviderActive: false when enabled but provider is 'none'", () => {
|
|
74
|
+
assert.equal(
|
|
75
|
+
structuralProviderActive(configWith({ enabled: true, structuralProvider: "none" })),
|
|
76
|
+
false,
|
|
77
|
+
);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
test("structuralProviderActive: true only when enabled AND provider !== 'none'", () => {
|
|
81
|
+
assert.equal(
|
|
82
|
+
structuralProviderActive(configWith({ enabled: true, structuralProvider: "subprocess" })),
|
|
83
|
+
true,
|
|
84
|
+
);
|
|
85
|
+
assert.equal(
|
|
86
|
+
structuralProviderActive(configWith({ enabled: true, structuralProvider: "native" })),
|
|
87
|
+
true,
|
|
88
|
+
);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
test("structuralProviderActive: provider set but master gate off → false", () => {
|
|
92
|
+
assert.equal(
|
|
93
|
+
structuralProviderActive(configWith({ enabled: false, structuralProvider: "subprocess" })),
|
|
94
|
+
false,
|
|
95
|
+
);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
test("structuralProviderActive: missing/null codingKnowledge → false (defensive)", () => {
|
|
99
|
+
assert.equal(
|
|
100
|
+
structuralProviderActive({ codingKnowledge: undefined } as unknown as PluginConfig),
|
|
101
|
+
false,
|
|
102
|
+
);
|
|
103
|
+
assert.equal(
|
|
104
|
+
structuralProviderActive({ codingKnowledge: null } as unknown as PluginConfig),
|
|
105
|
+
false,
|
|
106
|
+
);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
// ──────────────────────────────────────────────────────────────────────────
|
|
110
|
+
// Registry (rule 11 — instance-scoped via Symbol; mirrors host-embedding)
|
|
111
|
+
// ──────────────────────────────────────────────────────────────────────────
|
|
112
|
+
|
|
113
|
+
test("registry: register + lookup by scope", () => {
|
|
114
|
+
const provider = fakeProvider("test", { ok: true, symbols: [] });
|
|
115
|
+
const unregister = registerStructuralContextProvider("svc-1", provider);
|
|
116
|
+
assert.equal(getStructuralContextProvider("svc-1"), provider);
|
|
117
|
+
unregister();
|
|
118
|
+
assert.equal(getStructuralContextProvider("svc-1"), undefined);
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
test("registry: empty/whitespace scope normalises to 'default'", () => {
|
|
122
|
+
const provider = fakeProvider("test", { ok: true, symbols: [] });
|
|
123
|
+
registerStructuralContextProvider(" ", provider);
|
|
124
|
+
assert.equal(getStructuralContextProvider(""), provider);
|
|
125
|
+
assert.equal(getStructuralContextProvider("default"), provider);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
test("registry: re-registering a scope closes the previous provider", async () => {
|
|
129
|
+
let closed = 0;
|
|
130
|
+
const first: StructuralContextProvider = {
|
|
131
|
+
id: "first",
|
|
132
|
+
async probe() {
|
|
133
|
+
return { available: true };
|
|
134
|
+
},
|
|
135
|
+
async symbolsForDiff() {
|
|
136
|
+
return { ok: true, symbols: [] };
|
|
137
|
+
},
|
|
138
|
+
close() {
|
|
139
|
+
closed += 1;
|
|
140
|
+
},
|
|
141
|
+
};
|
|
142
|
+
const second = fakeProvider("second", { ok: true, symbols: [] });
|
|
143
|
+
registerStructuralContextProvider("svc", first);
|
|
144
|
+
registerStructuralContextProvider("svc", second);
|
|
145
|
+
assert.equal(getStructuralContextProvider("svc"), second);
|
|
146
|
+
// Allow the microtask queue to drain the close() call.
|
|
147
|
+
await Promise.resolve();
|
|
148
|
+
assert.equal(closed, 1, "previous provider must be closed on overwrite");
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
test("registry: unregister is idempotent and only closes its own provider", () => {
|
|
152
|
+
const a = fakeProvider("a", { ok: true, symbols: [] });
|
|
153
|
+
const unregister = registerStructuralContextProvider("svc", a);
|
|
154
|
+
unregister();
|
|
155
|
+
unregister();
|
|
156
|
+
assert.equal(getStructuralContextProvider("svc"), undefined);
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
// ──────────────────────────────────────────────────────────────────────────
|
|
160
|
+
// toStructuralContextDegradation — single chokepoint for the backend tag
|
|
161
|
+
// ──────────────────────────────────────────────────────────────────────────
|
|
162
|
+
|
|
163
|
+
test("toStructuralContextDegradation: carries code + detail with the backend tag", () => {
|
|
164
|
+
const degradation = toStructuralContextDegradation({
|
|
165
|
+
ok: false,
|
|
166
|
+
code: "provider_timeout",
|
|
167
|
+
detail: "exceeded 5000ms",
|
|
168
|
+
});
|
|
169
|
+
assert.deepEqual(degradation, {
|
|
170
|
+
backend: "structural-context",
|
|
171
|
+
code: "provider_timeout",
|
|
172
|
+
detail: "exceeded 5000ms",
|
|
173
|
+
});
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
test("toStructuralContextDegradation: detail omitted when absent", () => {
|
|
177
|
+
const degradation = toStructuralContextDegradation({ ok: false, code: "provider_malformed" });
|
|
178
|
+
assert.equal(degradation.detail, undefined);
|
|
179
|
+
assert.equal(degradation.backend, "structural-context");
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
// ──────────────────────────────────────────────────────────────────────────
|
|
183
|
+
// Doctor status summariser (pure, config-only)
|
|
184
|
+
// ──────────────────────────────────────────────────────────────────────────
|
|
185
|
+
|
|
186
|
+
test("describeStructuralProviderStatus: inactive when gate off", () => {
|
|
187
|
+
const status = describeStructuralProviderStatus(configWith({}));
|
|
188
|
+
assert.equal(status.active, false);
|
|
189
|
+
assert.equal(status.mode, "none");
|
|
190
|
+
assert.equal(status.command, undefined);
|
|
191
|
+
assert.equal(status.probed, undefined);
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
test("describeStructuralProviderStatus: subprocess mode surfaces configured command", () => {
|
|
195
|
+
const status = describeStructuralProviderStatus(
|
|
196
|
+
configWith({ enabled: true, structuralProvider: "subprocess", structuralProviderCommand: "/usr/local/bin/cbm" }),
|
|
197
|
+
);
|
|
198
|
+
assert.equal(status.active, true);
|
|
199
|
+
assert.equal(status.mode, "subprocess");
|
|
200
|
+
assert.equal(status.command, "/usr/local/bin/cbm");
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
test("describeStructuralProviderStatus: subprocess mode without command → no command field", () => {
|
|
204
|
+
const status = describeStructuralProviderStatus(
|
|
205
|
+
configWith({ enabled: true, structuralProvider: "subprocess", structuralProviderCommand: "" }),
|
|
206
|
+
);
|
|
207
|
+
assert.equal(status.active, true);
|
|
208
|
+
assert.equal(status.command, undefined);
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
test("describeStructuralProviderStatus: registered provider id surfaces when scope given", () => {
|
|
212
|
+
const provider = fakeProvider("my-adapter", { ok: true, symbols: [] });
|
|
213
|
+
registerStructuralContextProvider("svc-7", provider);
|
|
214
|
+
const status = describeStructuralProviderStatus(
|
|
215
|
+
configWith({ enabled: true, structuralProvider: "native" }),
|
|
216
|
+
"svc-7",
|
|
217
|
+
);
|
|
218
|
+
assert.equal(status.providerId, "my-adapter");
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
// ──────────────────────────────────────────────────────────────────────────
|
|
222
|
+
// Status line renderer (pure)
|
|
223
|
+
// ──────────────────────────────────────────────────────────────────────────
|
|
224
|
+
|
|
225
|
+
test("renderStructuralProviderStatusLine: inactive line is deterministic", () => {
|
|
226
|
+
const line = renderStructuralProviderStatusLine(
|
|
227
|
+
describeStructuralProviderStatus(configWith({})),
|
|
228
|
+
);
|
|
229
|
+
assert.equal(
|
|
230
|
+
line,
|
|
231
|
+
"structural-context provider: none (inactive — review-context stays file-path-only)",
|
|
232
|
+
);
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
test("renderStructuralProviderStatusLine: subprocess + command + probed available", () => {
|
|
236
|
+
const line = renderStructuralProviderStatusLine({
|
|
237
|
+
active: true,
|
|
238
|
+
mode: "subprocess",
|
|
239
|
+
command: "/usr/local/bin/cbm",
|
|
240
|
+
probed: { available: true },
|
|
241
|
+
});
|
|
242
|
+
assert.equal(
|
|
243
|
+
line,
|
|
244
|
+
"structural-context provider: subprocess, command=/usr/local/bin/cbm, probed=available",
|
|
245
|
+
);
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
test("renderStructuralProviderStatusLine: probed unavailable carries detail", () => {
|
|
249
|
+
const line = renderStructuralProviderStatusLine({
|
|
250
|
+
active: true,
|
|
251
|
+
mode: "subprocess",
|
|
252
|
+
command: "/usr/local/bin/cbm",
|
|
253
|
+
probed: { available: false, detail: "binary not found: /usr/local/bin/cbm" },
|
|
254
|
+
});
|
|
255
|
+
assert.equal(
|
|
256
|
+
line,
|
|
257
|
+
"structural-context provider: subprocess, command=/usr/local/bin/cbm, probed=unavailable (binary not found: /usr/local/bin/cbm)",
|
|
258
|
+
);
|
|
259
|
+
});
|
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structural-context provider port (issue #1548 Track A PR 5).
|
|
3
|
+
*
|
|
4
|
+
* The architectural boundary between Track A (durable coding-memory
|
|
5
|
+
* features) and Track B (the native @remnic/coding-graph engine). A
|
|
6
|
+
* structural-context provider expands a unified diff into the SYMBOLS it
|
|
7
|
+
* touches (and optionally yields architecture hints), so review-intent
|
|
8
|
+
* recall can boost memories that mention those symbols — not just the
|
|
9
|
+
* bare file paths that `review-context.ts` already boosts on.
|
|
10
|
+
*
|
|
11
|
+
* Three providers satisfy this port, selected by
|
|
12
|
+
* `codingKnowledge.structuralProvider`:
|
|
13
|
+
*
|
|
14
|
+
* - `"none"` — no provider consulted (default; review-context stays
|
|
15
|
+
* file-path-only, byte-identical to pre-feature — rule 39).
|
|
16
|
+
* - `"subprocess"` — an external binary shelled out via `execFile` with an
|
|
17
|
+
* argv ARRAY (never a shell string — rule 10). The
|
|
18
|
+
* built-in adapter lives in `structural-subprocess-provider.ts`;
|
|
19
|
+
* codebase-memory-mcp's CLI mode is the canonical target,
|
|
20
|
+
* but the provider is command-agnostic.
|
|
21
|
+
* - `"native"` — the in-family @remnic/coding-graph engine, adapted to
|
|
22
|
+
* this port by a future PR (#1551/#1554). Only the enum
|
|
23
|
+
* value and this doc note land in this PR — no engine
|
|
24
|
+
* code. codebase-memory-mcp remains a supported
|
|
25
|
+
* subprocess provider through this same port regardless.
|
|
26
|
+
*
|
|
27
|
+
* Every result is a tagged outcome shaped like #1536's `SearchDegradation`
|
|
28
|
+
* (CLAUDE.md rule 34 — a degraded/absent provider must NEVER masquerade as
|
|
29
|
+
* "no structural matches"):
|
|
30
|
+
* - `{ ok: true, symbols }` on success
|
|
31
|
+
* - `{ ok: false, code }` on every failure (distinct codes per mode)
|
|
32
|
+
*
|
|
33
|
+
* No module-level mutable state (rule 11): the registry is keyed per
|
|
34
|
+
* orchestrator/service instance via a `Symbol.for(...)` slot on
|
|
35
|
+
* `globalThis`, mirroring `host-embedding-provider.ts`.
|
|
36
|
+
*/
|
|
37
|
+
import type { CodingKnowledgeConfig, PluginConfig } from "../types.js";
|
|
38
|
+
|
|
39
|
+
// ──────────────────────────────────────────────────────────────────────────
|
|
40
|
+
// Tagged-outcome types (rule 34 — #1536 pattern; rule 18 — object-not-null)
|
|
41
|
+
// ──────────────────────────────────────────────────────────────────────────
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Distinct failure codes for the structural-context port. Every code is
|
|
45
|
+
* load-bearing: callers (review-context, `remnic doctor`, xray) switch on
|
|
46
|
+
* `code` to distinguish "binary not installed" from "timed out" from
|
|
47
|
+
* "protocol violation" so a silent provider failure can never look like an
|
|
48
|
+
* empty-but-healthy result.
|
|
49
|
+
*/
|
|
50
|
+
export type StructuralContextErrorCode =
|
|
51
|
+
/** Probe failed: binary missing / not executable, or native package absent. */
|
|
52
|
+
| "provider_unavailable"
|
|
53
|
+
/** The provider exceeded its deadline. */
|
|
54
|
+
| "provider_timeout"
|
|
55
|
+
/** Output was present but not a valid JSON object/array (rule 18). */
|
|
56
|
+
| "provider_malformed"
|
|
57
|
+
/** Provider ran but returned an explicit error or threw. */
|
|
58
|
+
| "provider_error";
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* A single symbol touched by a diff. The `symbol` name is the load-bearing
|
|
62
|
+
* field — it is what review-context adds to its match set. `path`/`kind`
|
|
63
|
+
* are informational and surface in xray when present.
|
|
64
|
+
*/
|
|
65
|
+
export interface StructuralSymbol {
|
|
66
|
+
/** Qualified or bare symbol name, e.g. `"AuthService.login"` or `"login"`. */
|
|
67
|
+
readonly symbol: string;
|
|
68
|
+
/** Source file the symbol lives in (forward-slashed, repo-relative when possible). */
|
|
69
|
+
readonly path?: string;
|
|
70
|
+
/** Coarse kind if the provider knows it (`function`/`method`/`class`/...). */
|
|
71
|
+
readonly kind?: string;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export interface SymbolsForDiffOk {
|
|
75
|
+
readonly ok: true;
|
|
76
|
+
readonly symbols: StructuralSymbol[];
|
|
77
|
+
/** Provider-reported latency in ms, when available. */
|
|
78
|
+
readonly latencyMs?: number;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export interface SymbolsForDiffErr {
|
|
82
|
+
readonly ok: false;
|
|
83
|
+
readonly code: StructuralContextErrorCode;
|
|
84
|
+
readonly detail?: string;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export type SymbolsForDiffResult = SymbolsForDiffOk | SymbolsForDiffErr;
|
|
88
|
+
|
|
89
|
+
export interface ArchitectureHintsOk {
|
|
90
|
+
readonly ok: true;
|
|
91
|
+
readonly hints: string[];
|
|
92
|
+
}
|
|
93
|
+
export interface ArchitectureHintsErr {
|
|
94
|
+
readonly ok: false;
|
|
95
|
+
readonly code: StructuralContextErrorCode;
|
|
96
|
+
readonly detail?: string;
|
|
97
|
+
}
|
|
98
|
+
export type ArchitectureHintsResult = ArchitectureHintsOk | ArchitectureHintsErr;
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* The port contract. Both the built-in subprocess adapter
|
|
102
|
+
* (`structural-subprocess-provider.ts`) and the future native engine
|
|
103
|
+
* adapter (#1551) implement this. Registry callers retrieve an instance by
|
|
104
|
+
* scope; never construct one inline at a call site.
|
|
105
|
+
*/
|
|
106
|
+
export interface StructuralContextProvider {
|
|
107
|
+
/** Stable identifier for registry logging / `remnic doctor`. */
|
|
108
|
+
readonly id: string;
|
|
109
|
+
/**
|
|
110
|
+
* Probe availability. Resolves to a tagged result so a missing binary
|
|
111
|
+
* (subprocess) or absent package (native) is surfaced, never thrown
|
|
112
|
+
* (rule 34). Implementations SHOULD cache the outcome per instance
|
|
113
|
+
* (rule 11).
|
|
114
|
+
*/
|
|
115
|
+
probe(): Promise<{ available: boolean; detail?: string }>;
|
|
116
|
+
/**
|
|
117
|
+
* Expand a unified diff to the symbols it touches. NEVER throws — every
|
|
118
|
+
* failure path returns a tagged `{ ok: false, code }`. The optional
|
|
119
|
+
* `signal` lets the caller cancel an in-flight subprocess (rule 40).
|
|
120
|
+
*/
|
|
121
|
+
symbolsForDiff(
|
|
122
|
+
diff: string,
|
|
123
|
+
options?: { signal?: AbortSignal },
|
|
124
|
+
): Promise<SymbolsForDiffResult>;
|
|
125
|
+
/**
|
|
126
|
+
* Optional architecture hints for a repo root. Used by the architecture
|
|
127
|
+
* card composition (#1548 Track A PR 3 + #1554). Implementations MAY omit
|
|
128
|
+
* this; callers MUST handle its absence.
|
|
129
|
+
*/
|
|
130
|
+
architectureHints?(
|
|
131
|
+
root: string,
|
|
132
|
+
options?: { signal?: AbortSignal },
|
|
133
|
+
): Promise<ArchitectureHintsResult>;
|
|
134
|
+
/** Release any resources the provider holds (subprocess handles, etc.). */
|
|
135
|
+
close?(): Promise<void> | void;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// ──────────────────────────────────────────────────────────────────────────
|
|
139
|
+
// Degradation (shaped like SearchDegradation from search/port.ts — #1536)
|
|
140
|
+
// ──────────────────────────────────────────────────────────────────────────
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Backend tag is a literal so consumers can switch on it programmatically
|
|
144
|
+
* alongside `SearchDegradation` (`backend: "qmd"`). `detail` is optional
|
|
145
|
+
* and never load-bearing — `code` is the signal.
|
|
146
|
+
*/
|
|
147
|
+
export interface StructuralContextDegradation {
|
|
148
|
+
readonly backend: "structural-context";
|
|
149
|
+
readonly code: StructuralContextErrorCode;
|
|
150
|
+
readonly detail?: string;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Coerce a {@link SymbolsForDiffErr} into the degradation record consumers
|
|
155
|
+
* attach to recall snapshots / xray. Centralised so the `{ backend: ... }`
|
|
156
|
+
* tag is set in one place (rule 22 spirit).
|
|
157
|
+
*/
|
|
158
|
+
export function toStructuralContextDegradation(
|
|
159
|
+
err: SymbolsForDiffErr,
|
|
160
|
+
): StructuralContextDegradation {
|
|
161
|
+
return { backend: "structural-context", code: err.code, detail: err.detail };
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// ──────────────────────────────────────────────────────────────────────────
|
|
165
|
+
// Registry — instance-scoped via Symbol (rule 11; mirrors host-embedding)
|
|
166
|
+
// ──────────────────────────────────────────────────────────────────────────
|
|
167
|
+
|
|
168
|
+
const STRUCTURAL_PROVIDERS_KEY = Symbol.for(
|
|
169
|
+
"remnic.structuralContextProviders",
|
|
170
|
+
);
|
|
171
|
+
|
|
172
|
+
function providerMap(): Map<string, StructuralContextProvider> {
|
|
173
|
+
const store = globalThis as Record<PropertyKey, unknown>;
|
|
174
|
+
const existing = store[STRUCTURAL_PROVIDERS_KEY];
|
|
175
|
+
if (existing instanceof Map) {
|
|
176
|
+
return existing as Map<string, StructuralContextProvider>;
|
|
177
|
+
}
|
|
178
|
+
const created = new Map<string, StructuralContextProvider>();
|
|
179
|
+
store[STRUCTURAL_PROVIDERS_KEY] = created;
|
|
180
|
+
return created;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function normalizeScope(scope: string): string {
|
|
184
|
+
const normalized = typeof scope === "string" ? scope.trim() : "";
|
|
185
|
+
return normalized || "default";
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Register a provider for a scope (typically an orchestrator `serviceId`).
|
|
190
|
+
* Overwriting a previous registration closes the old provider. Returns an
|
|
191
|
+
* unregister function — callers SHOULD invoke it on service teardown so a
|
|
192
|
+
* hot-reloaded host does not leak subprocess handles.
|
|
193
|
+
*/
|
|
194
|
+
export function registerStructuralContextProvider(
|
|
195
|
+
scope: string,
|
|
196
|
+
provider: StructuralContextProvider,
|
|
197
|
+
): () => void {
|
|
198
|
+
const key = normalizeScope(scope);
|
|
199
|
+
const providers = providerMap();
|
|
200
|
+
const previous = providers.get(key);
|
|
201
|
+
if (previous && previous !== provider) {
|
|
202
|
+
void previous.close?.();
|
|
203
|
+
}
|
|
204
|
+
providers.set(key, provider);
|
|
205
|
+
return () => {
|
|
206
|
+
const current = providerMap();
|
|
207
|
+
if (current.get(key) === provider) {
|
|
208
|
+
current.delete(key);
|
|
209
|
+
void provider.close?.();
|
|
210
|
+
}
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/** Look up the provider registered for a scope, if any. */
|
|
215
|
+
export function getStructuralContextProvider(
|
|
216
|
+
scope: string,
|
|
217
|
+
): StructuralContextProvider | undefined {
|
|
218
|
+
return providerMap().get(normalizeScope(scope));
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/** Test-only: clear every registered provider. */
|
|
222
|
+
export function clearStructuralContextProvidersForTest(): void {
|
|
223
|
+
providerMap().clear();
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// ──────────────────────────────────────────────────────────────────────────
|
|
227
|
+
// Gate predicate — rule 39: ONE predicate, checked identically on every path
|
|
228
|
+
// ──────────────────────────────────────────────────────────────────────────
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* The single gate every review-context consumer consults before consulting a
|
|
232
|
+
* provider. Returns `true` iff the master `codingKnowledge.enabled` switch is
|
|
233
|
+
* on AND `structuralProvider` is not `"none"`. When this returns `false`,
|
|
234
|
+
* review-context runs its pure file-path-only ranker — byte-identical to
|
|
235
|
+
* pre-feature behaviour on every path.
|
|
236
|
+
*/
|
|
237
|
+
export function structuralProviderActive(config: PluginConfig): boolean {
|
|
238
|
+
const ck = config.codingKnowledge;
|
|
239
|
+
if (ck === undefined || ck === null) return false;
|
|
240
|
+
return ck.enabled === true && ck.structuralProvider !== "none";
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// ──────────────────────────────────────────────────────────────────────────
|
|
244
|
+
// Doctor / xray status — pure config-only summariser + renderer
|
|
245
|
+
// (the live PROBE lives in structural-subprocess-provider.ts so this module
|
|
246
|
+
// stays free of subprocess / optional-package imports)
|
|
247
|
+
// ──────────────────────────────────────────────────────────────────────────
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Config-only structural provider status. `remnic doctor` and xray render
|
|
251
|
+
* this; an async sibling in `structural-subprocess-provider.ts` augments it
|
|
252
|
+
* with a live `probed` field.
|
|
253
|
+
*/
|
|
254
|
+
export interface StructuralProviderStatus {
|
|
255
|
+
readonly active: boolean;
|
|
256
|
+
readonly mode: CodingKnowledgeConfig["structuralProvider"];
|
|
257
|
+
/** Command path when mode is `"subprocess"` and a command is configured. */
|
|
258
|
+
readonly command?: string;
|
|
259
|
+
/** Provider id when one is registered for the scope. */
|
|
260
|
+
readonly providerId?: string;
|
|
261
|
+
/** Live probe outcome — set only by the async doctor probe. */
|
|
262
|
+
probed?: { available: boolean; detail?: string };
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Summarise the structural provider status from config ALONE (no probe, no
|
|
267
|
+
* I/O). Pure — safe to call from any context, including the recall hot path
|
|
268
|
+
* for xray labels.
|
|
269
|
+
*/
|
|
270
|
+
export function describeStructuralProviderStatus(
|
|
271
|
+
config: PluginConfig,
|
|
272
|
+
scope?: string,
|
|
273
|
+
): StructuralProviderStatus {
|
|
274
|
+
const ck = config.codingKnowledge;
|
|
275
|
+
const mode: CodingKnowledgeConfig["structuralProvider"] =
|
|
276
|
+
ck?.structuralProvider ?? "none";
|
|
277
|
+
const active = structuralProviderActive(config);
|
|
278
|
+
const provider =
|
|
279
|
+
scope !== undefined ? getStructuralContextProvider(scope) : undefined;
|
|
280
|
+
return {
|
|
281
|
+
active,
|
|
282
|
+
mode,
|
|
283
|
+
command:
|
|
284
|
+
mode === "subprocess" && ck?.structuralProviderCommand
|
|
285
|
+
? ck.structuralProviderCommand
|
|
286
|
+
: undefined,
|
|
287
|
+
providerId: provider?.id,
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Render a single human-readable line for `remnic doctor`. Pure.
|
|
293
|
+
*
|
|
294
|
+
* `structural-context provider: none (inactive — review-context stays file-path-only)`
|
|
295
|
+
* `structural-context provider: subprocess, command=/usr/local/bin/cbm, probed=available`
|
|
296
|
+
* `structural-context provider: subprocess, command=/usr/local/bin/cbm, probed=unavailable (binary not found)`
|
|
297
|
+
*/
|
|
298
|
+
export function renderStructuralProviderStatusLine(
|
|
299
|
+
status: StructuralProviderStatus,
|
|
300
|
+
): string {
|
|
301
|
+
if (!status.active) {
|
|
302
|
+
return `structural-context provider: ${status.mode} (inactive — review-context stays file-path-only)`;
|
|
303
|
+
}
|
|
304
|
+
const parts = [`structural-context provider: ${status.mode}`];
|
|
305
|
+
if (status.command) parts.push(`command=${status.command}`);
|
|
306
|
+
if (status.providerId) parts.push(`registered=${status.providerId}`);
|
|
307
|
+
if (status.probed) {
|
|
308
|
+
parts.push(
|
|
309
|
+
status.probed.available
|
|
310
|
+
? "probed=available"
|
|
311
|
+
: `probed=unavailable${status.probed.detail ? ` (${status.probed.detail})` : ""}`,
|
|
312
|
+
);
|
|
313
|
+
}
|
|
314
|
+
return parts.join(", ");
|
|
315
|
+
}
|