@remnic/core 9.3.729 → 9.3.730
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
|
@@ -30,6 +30,7 @@ import type {
|
|
|
30
30
|
MemoryStatus,
|
|
31
31
|
} from "../types.js";
|
|
32
32
|
import type { VersionTrigger, VersioningConfig } from "../page-versioning.js";
|
|
33
|
+
import type { MemoryWriteResult } from "../storage.js";
|
|
33
34
|
import { stripAttributesSuffix } from "../structured-attributes.js";
|
|
34
35
|
import { ARCHITECTURE_CARD_TRUNCATION_MARKER, type ArchitectureCardBuildResult } from "./architecture-card.js";
|
|
35
36
|
import { log } from "../logger.js";
|
|
@@ -118,6 +119,14 @@ export type ArchitectureSurfaceResponse =
|
|
|
118
119
|
byteSize: number;
|
|
119
120
|
truncated: boolean;
|
|
120
121
|
buildCode?: string;
|
|
122
|
+
/**
|
|
123
|
+
* Present when the refresh was tombstone-blocked (#1579): the new card
|
|
124
|
+
* content landed pending_review (no active overwrite). Callers/UI surface
|
|
125
|
+
* this so operators see why the active card didn't update (#1645).
|
|
126
|
+
*/
|
|
127
|
+
blockedReason?: string;
|
|
128
|
+
/** The tombstone id that blocked the write, when `blockedReason` is set. */
|
|
129
|
+
blockedBy?: string;
|
|
121
130
|
};
|
|
122
131
|
|
|
123
132
|
// ──────────────────────────────────────────────────────────────────────────
|
|
@@ -150,7 +159,7 @@ export interface ArchitectureSurfaceStorage {
|
|
|
150
159
|
status?: MemoryStatus;
|
|
151
160
|
structuredAttributes?: Record<string, string>;
|
|
152
161
|
},
|
|
153
|
-
): Promise<
|
|
162
|
+
): Promise<MemoryWriteResult>;
|
|
154
163
|
updateMemory(
|
|
155
164
|
id: string,
|
|
156
165
|
newContent: string,
|
|
@@ -307,22 +316,16 @@ async function architectureRefresh(
|
|
|
307
316
|
log.warn(
|
|
308
317
|
`coding_architecture/refresh: updateMemory returned false for id=${existing.frontmatter.id} — falling back to writeMemory`,
|
|
309
318
|
);
|
|
310
|
-
const
|
|
319
|
+
const fallbackWrite = await storage.writeMemory("fact", cardContent, {
|
|
311
320
|
confidence: 1.0,
|
|
312
321
|
tags,
|
|
313
322
|
source: "coding-architecture",
|
|
314
323
|
structuredAttributes: { cardKind: ARCHITECTURE_CARD_KIND },
|
|
315
324
|
});
|
|
316
325
|
log.info(
|
|
317
|
-
`access-write op=coding_architecture/refresh memoryId=${
|
|
326
|
+
`access-write op=coding_architecture/refresh memoryId=${fallbackWrite.id} (fallback write) byteSize=${buildResult.card.byteSize}`,
|
|
318
327
|
);
|
|
319
|
-
return
|
|
320
|
-
subcommand: "refresh",
|
|
321
|
-
refreshed: true,
|
|
322
|
-
memoryId,
|
|
323
|
-
byteSize: buildResult.card.byteSize,
|
|
324
|
-
truncated: buildResult.card.truncated,
|
|
325
|
-
};
|
|
328
|
+
return refreshResponse(fallbackWrite, buildResult.card);
|
|
326
329
|
}
|
|
327
330
|
log.info(
|
|
328
331
|
`access-write op=coding_architecture/refresh memoryId=${existing.frontmatter.id} (updated) byteSize=${buildResult.card.byteSize}`,
|
|
@@ -337,28 +340,53 @@ async function architectureRefresh(
|
|
|
337
340
|
}
|
|
338
341
|
|
|
339
342
|
// First card for this namespace — write a new memory.
|
|
340
|
-
const
|
|
343
|
+
const newWrite = await storage.writeMemory("fact", cardContent, {
|
|
341
344
|
confidence: 1.0,
|
|
342
345
|
tags,
|
|
343
346
|
source: "coding-architecture",
|
|
344
347
|
structuredAttributes: { cardKind: ARCHITECTURE_CARD_KIND },
|
|
345
348
|
});
|
|
346
349
|
log.info(
|
|
347
|
-
`access-write op=coding_architecture/refresh memoryId=${
|
|
350
|
+
`access-write op=coding_architecture/refresh memoryId=${newWrite.id} (new) byteSize=${buildResult.card.byteSize}`,
|
|
348
351
|
);
|
|
349
|
-
return
|
|
350
|
-
subcommand: "refresh",
|
|
351
|
-
refreshed: true,
|
|
352
|
-
memoryId,
|
|
353
|
-
byteSize: buildResult.card.byteSize,
|
|
354
|
-
truncated: buildResult.card.truncated,
|
|
355
|
-
};
|
|
352
|
+
return refreshResponse(newWrite, buildResult.card);
|
|
356
353
|
}
|
|
357
354
|
|
|
358
355
|
// ──────────────────────────────────────────────────────────────────────────
|
|
359
356
|
// Local helpers
|
|
360
357
|
// ──────────────────────────────────────────────────────────────────────────
|
|
361
358
|
|
|
359
|
+
/**
|
|
360
|
+
* Build the refresh response from a write outcome, surfacing a
|
|
361
|
+
* tombstone block (#1579) as `refreshed:false` + a blocked reason so
|
|
362
|
+
* callers/UI see why the active card didn't update (#1645). Mirrors the
|
|
363
|
+
* guard pattern in wearables/memory-gen.ts: a blocked write lands
|
|
364
|
+
* pending_review (no active copy) and must NOT report `refreshed:true`.
|
|
365
|
+
*/
|
|
366
|
+
function refreshResponse(
|
|
367
|
+
writeResult: MemoryWriteResult,
|
|
368
|
+
card: { byteSize: number; truncated: boolean },
|
|
369
|
+
): ArchitectureSurfaceResponse {
|
|
370
|
+
if (writeResult.tombstoneBlocked) {
|
|
371
|
+
return {
|
|
372
|
+
subcommand: "refresh",
|
|
373
|
+
refreshed: false,
|
|
374
|
+
memoryId: writeResult.id,
|
|
375
|
+
byteSize: card.byteSize,
|
|
376
|
+
truncated: card.truncated,
|
|
377
|
+
blockedReason: "tombstone-blocked",
|
|
378
|
+
...(writeResult.blockedBy !== undefined ? { blockedBy: writeResult.blockedBy } : {}),
|
|
379
|
+
};
|
|
380
|
+
}
|
|
381
|
+
return {
|
|
382
|
+
subcommand: "refresh",
|
|
383
|
+
refreshed: true,
|
|
384
|
+
memoryId: writeResult.id,
|
|
385
|
+
byteSize: card.byteSize,
|
|
386
|
+
truncated: card.truncated,
|
|
387
|
+
};
|
|
388
|
+
}
|
|
389
|
+
|
|
362
390
|
/**
|
|
363
391
|
* Find the existing architecture-card memory in the namespace, if any.
|
|
364
392
|
* Identified by the `architecture-card` tag + `cardKind=architecture`
|
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
type DecisionStatus,
|
|
23
23
|
} from "./decision-records.js";
|
|
24
24
|
import { log } from "../logger.js";
|
|
25
|
+
import type { MemoryWriteResult } from "../storage.js";
|
|
25
26
|
|
|
26
27
|
// ──────────────────────────────────────────────────────────────────────────
|
|
27
28
|
// Subcommands
|
|
@@ -184,7 +185,7 @@ export interface DecisionSurfaceStorage {
|
|
|
184
185
|
* body so the list/get projection has one authoritative source. */
|
|
185
186
|
structuredAttributes?: Record<string, string>;
|
|
186
187
|
},
|
|
187
|
-
): Promise<
|
|
188
|
+
): Promise<MemoryWriteResult>;
|
|
188
189
|
writeMemoryFrontmatter(
|
|
189
190
|
memory: MemoryFile,
|
|
190
191
|
patch: Partial<MemoryFrontmatter>,
|
|
@@ -342,7 +343,7 @@ async function decisionRecord(
|
|
|
342
343
|
const content = serializeDecisionRecord(record);
|
|
343
344
|
const storage = await ctx.resolveStorage(request);
|
|
344
345
|
const isActive = ACTIVE_DECISION_STATUSES.has(status);
|
|
345
|
-
const memoryId = await storage.writeMemory("decision", content, {
|
|
346
|
+
const { id: memoryId } = await storage.writeMemory("decision", content, {
|
|
346
347
|
confidence: 1.0,
|
|
347
348
|
tags: ["decision-record"],
|
|
348
349
|
source: "coding-decision",
|
|
@@ -407,7 +408,7 @@ async function decisionSupersede(
|
|
|
407
408
|
supersedes: targetId,
|
|
408
409
|
};
|
|
409
410
|
const replacementContent = serializeDecisionRecord(replacement);
|
|
410
|
-
const replacementId = await storage.writeMemory(
|
|
411
|
+
const { id: replacementId, tombstoneBlocked: replacementBlocked } = await storage.writeMemory(
|
|
411
412
|
"decision",
|
|
412
413
|
replacementContent,
|
|
413
414
|
{
|
|
@@ -420,6 +421,14 @@ async function decisionSupersede(
|
|
|
420
421
|
structuredAttributes: { decisionStatus: "accepted" },
|
|
421
422
|
},
|
|
422
423
|
);
|
|
424
|
+
if (replacementBlocked) {
|
|
425
|
+
// #1645: the replacement decision matched a tombstone (pending_review).
|
|
426
|
+
// Don't archive the old record — superseding with a non-active
|
|
427
|
+
// replacement retires the only active decision. Abort; old stays active.
|
|
428
|
+
ctx.throwInputError(
|
|
429
|
+
`replacement decision was tombstone-blocked (pending_review ${replacementId}) — keeping decision ${targetId} active`,
|
|
430
|
+
);
|
|
431
|
+
}
|
|
423
432
|
// Mark the old record superseded: set BOTH frontmatter.status (so
|
|
424
433
|
// recall/search/maintenance exclude it from the active corpus — review P2)
|
|
425
434
|
// AND structuredAttributes.decisionStatus (the decision-specific lifecycle
|
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for the structural-context-aware review-context path
|
|
3
|
+
* (issue #1548 Track A PR 5).
|
|
4
|
+
*
|
|
5
|
+
* This file is the prove-fail-before characterization for PR 5:
|
|
6
|
+
* 1. GATE-OFF PARITY — with no provider consulted (the pure
|
|
7
|
+
* {@link packReviewContext} path), output is byte-identical to
|
|
8
|
+
* pre-feature behaviour on every input. Captured FIRST, before any
|
|
9
|
+
* assertion about the wired path.
|
|
10
|
+
* 2. PROVIDER-FAILURE FALLBACK — when the provider fails (any code),
|
|
11
|
+
* {@link packReviewContextStructural} ranking falls back to
|
|
12
|
+
* FILE-PATH-ONLY boosting, IDENTICAL to the pure packer, AND surfaces
|
|
13
|
+
* a distinct `structuralDegradation` so the failure is never silent
|
|
14
|
+
* (rule 34).
|
|
15
|
+
* 3. PROVIDER-SUCCESS EXPANSION — when the provider returns symbols,
|
|
16
|
+
* memories mentioning a symbol name float up via the same bounded
|
|
17
|
+
* additive boost.
|
|
18
|
+
*
|
|
19
|
+
* The provider is a fake implementing the port contract; no subprocess.
|
|
20
|
+
*/
|
|
21
|
+
import assert from "node:assert/strict";
|
|
22
|
+
import { test } from "node:test";
|
|
23
|
+
|
|
24
|
+
import {
|
|
25
|
+
packReviewContext,
|
|
26
|
+
packReviewContextStructural,
|
|
27
|
+
rankReviewCandidates,
|
|
28
|
+
type ReviewCandidate,
|
|
29
|
+
} from "./review-context.js";
|
|
30
|
+
import type {
|
|
31
|
+
StructuralContextProvider,
|
|
32
|
+
SymbolsForDiffResult,
|
|
33
|
+
} from "./structural-context.js";
|
|
34
|
+
|
|
35
|
+
function c(id: string, score: number, entityRefs?: string[]): ReviewCandidate {
|
|
36
|
+
return { id, score, entityRefs };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Fake provider whose symbolsForDiff returns a fixed result. */
|
|
40
|
+
function providerReturning(result: SymbolsForDiffResult): StructuralContextProvider {
|
|
41
|
+
return {
|
|
42
|
+
id: "fake",
|
|
43
|
+
async probe() {
|
|
44
|
+
return { available: true };
|
|
45
|
+
},
|
|
46
|
+
async symbolsForDiff() {
|
|
47
|
+
return result;
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const DIFF_TOUCHING_AUTH = [
|
|
53
|
+
"diff --git a/src/auth.ts b/src/auth.ts",
|
|
54
|
+
"--- a/src/auth.ts",
|
|
55
|
+
"+++ b/src/auth.ts",
|
|
56
|
+
"@@ -1,3 +1,4 @@",
|
|
57
|
+
"+ export function login() {}",
|
|
58
|
+
].join("\n");
|
|
59
|
+
|
|
60
|
+
// ──────────────────────────────────────────────────────────────────────────
|
|
61
|
+
// 1. GATE-OFF PARITY — pure packReviewContext is byte-identical to today
|
|
62
|
+
// (prove-fail-before: this must pass BEFORE the structural path exists,
|
|
63
|
+
// and must keep passing unchanged after).
|
|
64
|
+
// ──────────────────────────────────────────────────────────────────────────
|
|
65
|
+
|
|
66
|
+
test("gate-off parity: pure packReviewContext boosts only on touched file paths", () => {
|
|
67
|
+
const candidates = [
|
|
68
|
+
c("auth-history", 0.2, ["src/auth.ts"]),
|
|
69
|
+
c("unrelated", 0.9, ["src/other.ts"]),
|
|
70
|
+
c("weak-match", 0.1, ["auth.ts"]),
|
|
71
|
+
];
|
|
72
|
+
const result = packReviewContext({ diff: DIFF_TOUCHING_AUTH, candidates });
|
|
73
|
+
assert.equal(result.touchedFiles.length, 1);
|
|
74
|
+
assert.equal(result.touchedFiles[0], "src/auth.ts");
|
|
75
|
+
// structuralDegradation is NOT set on the pure path.
|
|
76
|
+
assert.equal(result.structuralDegradation, undefined);
|
|
77
|
+
// strongest unrelated wins; auth-history floats above weak-match via boost.
|
|
78
|
+
const ids = result.rankedRecall.map((r) => r.id);
|
|
79
|
+
assert.deepEqual(ids, ["unrelated", "auth-history", "weak-match"]);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
test("gate-off parity: pure packReviewContext has no structuralDegradation field effect", () => {
|
|
83
|
+
const result = packReviewContext({ diff: "", candidates: [c("x", 0.5)] });
|
|
84
|
+
assert.equal(result.structuralDegradation, undefined);
|
|
85
|
+
assert.equal(result.rankedRecall[0]!.boost, 0);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
test("gate-off parity: deterministic ordering matches rankReviewCandidates directly", () => {
|
|
89
|
+
const candidates = [c("b", 0.3, ["a.ts"]), c("a", 0.8), c("c", 0.3, ["a.ts"])];
|
|
90
|
+
const packed = packReviewContext({ diff: "diff --git a/a.ts b/a.ts", candidates });
|
|
91
|
+
const direct = rankReviewCandidates(candidates, ["a.ts"]);
|
|
92
|
+
assert.deepEqual(
|
|
93
|
+
packed.rankedRecall.map((r) => ({ id: r.id, boost: r.boost })),
|
|
94
|
+
direct.map((r) => ({ id: r.id, boost: r.boost })),
|
|
95
|
+
);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
// ──────────────────────────────────────────────────────────────────────────
|
|
99
|
+
// 2. PROVIDER-FAILURE FALLBACK — ranking identical to pure packer +
|
|
100
|
+
// structuralDegradation populated (rule 34 — never silent)
|
|
101
|
+
// ──────────────────────────────────────────────────────────────────────────
|
|
102
|
+
|
|
103
|
+
test("structural fallback (a) provider_unavailable: ranking == pure packer, degradation set", async () => {
|
|
104
|
+
const candidates = [
|
|
105
|
+
c("auth-history", 0.2, ["src/auth.ts"]),
|
|
106
|
+
c("unrelated", 0.9, ["src/other.ts"]),
|
|
107
|
+
];
|
|
108
|
+
const provider = providerReturning({
|
|
109
|
+
ok: false,
|
|
110
|
+
code: "provider_unavailable",
|
|
111
|
+
detail: "binary not found",
|
|
112
|
+
});
|
|
113
|
+
const result = await packReviewContextStructural({
|
|
114
|
+
diff: DIFF_TOUCHING_AUTH,
|
|
115
|
+
candidates,
|
|
116
|
+
provider,
|
|
117
|
+
});
|
|
118
|
+
// Ranking must be IDENTICAL to the pure packer (file-path-only).
|
|
119
|
+
const pure = packReviewContext({ diff: DIFF_TOUCHING_AUTH, candidates });
|
|
120
|
+
assert.deepEqual(
|
|
121
|
+
result.rankedRecall.map((r) => ({ id: r.id, boost: r.boost })),
|
|
122
|
+
pure.rankedRecall.map((r) => ({ id: r.id, boost: r.boost })),
|
|
123
|
+
);
|
|
124
|
+
// Degradation surfaced.
|
|
125
|
+
assert.equal(result.structuralDegradation?.backend, "structural-context");
|
|
126
|
+
assert.equal(result.structuralDegradation?.code, "provider_unavailable");
|
|
127
|
+
assert.equal(result.structuralDegradation?.detail, "binary not found");
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
test("structural fallback (c) provider_timeout: ranking == pure packer, degradation code distinct", async () => {
|
|
131
|
+
const candidates = [c("auth-history", 0.2, ["src/auth.ts"])];
|
|
132
|
+
const provider = providerReturning({ ok: false, code: "provider_timeout" });
|
|
133
|
+
const result = await packReviewContextStructural({
|
|
134
|
+
diff: DIFF_TOUCHING_AUTH,
|
|
135
|
+
candidates,
|
|
136
|
+
provider,
|
|
137
|
+
});
|
|
138
|
+
const pure = packReviewContext({ diff: DIFF_TOUCHING_AUTH, candidates });
|
|
139
|
+
assert.deepEqual(
|
|
140
|
+
result.rankedRecall.map((r) => r.id),
|
|
141
|
+
pure.rankedRecall.map((r) => r.id),
|
|
142
|
+
);
|
|
143
|
+
assert.equal(result.structuralDegradation?.code, "provider_timeout");
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
test("structural fallback (d) provider_malformed: ranking == pure packer, degradation code distinct", async () => {
|
|
147
|
+
const candidates = [c("auth-history", 0.2, ["src/auth.ts"])];
|
|
148
|
+
const provider = providerReturning({ ok: false, code: "provider_malformed" });
|
|
149
|
+
const result = await packReviewContextStructural({
|
|
150
|
+
diff: DIFF_TOUCHING_AUTH,
|
|
151
|
+
candidates,
|
|
152
|
+
provider,
|
|
153
|
+
});
|
|
154
|
+
assert.equal(result.structuralDegradation?.code, "provider_malformed");
|
|
155
|
+
// No symbol expansion happened — touched files unchanged.
|
|
156
|
+
assert.deepEqual(result.touchedFiles, ["src/auth.ts"]);
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
test("structural fallback: provider_error code surfaces", async () => {
|
|
160
|
+
const provider = providerReturning({
|
|
161
|
+
ok: false,
|
|
162
|
+
code: "provider_error",
|
|
163
|
+
detail: "exit 2",
|
|
164
|
+
});
|
|
165
|
+
const result = await packReviewContextStructural({
|
|
166
|
+
diff: DIFF_TOUCHING_AUTH,
|
|
167
|
+
candidates: [c("x", 0.5)],
|
|
168
|
+
provider,
|
|
169
|
+
});
|
|
170
|
+
assert.equal(result.structuralDegradation?.code, "provider_error");
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
test("structural fallback: empty diff + failing provider → no boosts, degradation still set", async () => {
|
|
174
|
+
const provider = providerReturning({ ok: false, code: "provider_unavailable" });
|
|
175
|
+
const result = await packReviewContextStructural({
|
|
176
|
+
diff: "",
|
|
177
|
+
candidates: [c("x", 0.5, ["src/auth.ts"])],
|
|
178
|
+
provider,
|
|
179
|
+
});
|
|
180
|
+
assert.equal(result.rankedRecall[0]!.boost, 0);
|
|
181
|
+
assert.equal(result.structuralDegradation?.code, "provider_unavailable");
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
// ──────────────────────────────────────────────────────────────────────────
|
|
185
|
+
// 3. PROVIDER-SUCCESS EXPANSION — symbols widen the match set
|
|
186
|
+
// ──────────────────────────────────────────────────────────────────────────
|
|
187
|
+
|
|
188
|
+
test("structural success: symbol match floats a memory that shares NO file path", async () => {
|
|
189
|
+
// Candidate mentions the SYMBOL but not the touched file path.
|
|
190
|
+
const candidates = [
|
|
191
|
+
c("symbol-memory", 0.1, ["AuthService.login"]),
|
|
192
|
+
c("file-memory", 0.1, ["src/auth.ts"]),
|
|
193
|
+
c("unrelated", 0.9, ["src/other.ts"]),
|
|
194
|
+
];
|
|
195
|
+
const provider = providerReturning({
|
|
196
|
+
ok: true,
|
|
197
|
+
symbols: [{ symbol: "AuthService.login", path: "src/auth.ts" }],
|
|
198
|
+
});
|
|
199
|
+
const result = await packReviewContextStructural({
|
|
200
|
+
diff: DIFF_TOUCHING_AUTH,
|
|
201
|
+
candidates,
|
|
202
|
+
provider,
|
|
203
|
+
});
|
|
204
|
+
// No degradation on success.
|
|
205
|
+
assert.equal(result.structuralDegradation, undefined);
|
|
206
|
+
// Both symbol- and file-matched memories get the boost; unrelated still wins.
|
|
207
|
+
const symbolMem = result.rankedRecall.find((r) => r.id === "symbol-memory")!;
|
|
208
|
+
const fileMem = result.rankedRecall.find((r) => r.id === "file-memory")!;
|
|
209
|
+
assert.equal(symbolMem.boost, 0.5, "symbol name match yields the same boost as a file match");
|
|
210
|
+
assert.equal(fileMem.boost, 0.5);
|
|
211
|
+
assert.equal(result.rankedRecall[0]!.id, "unrelated");
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
test("structural success: only-file-matched baseline does NOT get a symbol boost", async () => {
|
|
215
|
+
const candidates = [c("only-file", 0.1, ["src/auth.ts"])];
|
|
216
|
+
const provider = providerReturning({
|
|
217
|
+
ok: true,
|
|
218
|
+
symbols: [{ symbol: "CompletelyUnrelated.symbol" }],
|
|
219
|
+
});
|
|
220
|
+
const result = await packReviewContextStructural({
|
|
221
|
+
diff: DIFF_TOUCHING_AUTH,
|
|
222
|
+
candidates,
|
|
223
|
+
provider,
|
|
224
|
+
});
|
|
225
|
+
// The file match still boosts; the unrelated symbol adds nothing for this memory.
|
|
226
|
+
assert.equal(result.rankedRecall[0]!.boost, 0.5);
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
test("structural success: empty symbol list behaves like pure packer (no degradation)", async () => {
|
|
230
|
+
const candidates = [c("auth-history", 0.2, ["src/auth.ts"])];
|
|
231
|
+
const provider = providerReturning({ ok: true, symbols: [] });
|
|
232
|
+
const result = await packReviewContextStructural({
|
|
233
|
+
diff: DIFF_TOUCHING_AUTH,
|
|
234
|
+
candidates,
|
|
235
|
+
provider,
|
|
236
|
+
});
|
|
237
|
+
assert.equal(result.structuralDegradation, undefined);
|
|
238
|
+
const pure = packReviewContext({ diff: DIFF_TOUCHING_AUTH, candidates });
|
|
239
|
+
assert.deepEqual(
|
|
240
|
+
result.rankedRecall.map((r) => ({ id: r.id, boost: r.boost })),
|
|
241
|
+
pure.rankedRecall.map((r) => ({ id: r.id, boost: r.boost })),
|
|
242
|
+
);
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
test("structural success: multiple symbols each contribute to the boost cap", async () => {
|
|
246
|
+
const candidate = c("multi", 0, ["login", "logout", "validateToken", "session"]);
|
|
247
|
+
const provider = providerReturning({
|
|
248
|
+
ok: true,
|
|
249
|
+
symbols: [
|
|
250
|
+
{ symbol: "login" },
|
|
251
|
+
{ symbol: "logout" },
|
|
252
|
+
{ symbol: "validateToken" },
|
|
253
|
+
{ symbol: "session" },
|
|
254
|
+
],
|
|
255
|
+
});
|
|
256
|
+
const result = await packReviewContextStructural({
|
|
257
|
+
diff: DIFF_TOUCHING_AUTH,
|
|
258
|
+
candidates: [candidate],
|
|
259
|
+
provider,
|
|
260
|
+
});
|
|
261
|
+
// Boost caps at MAX_BOOST (1.0) regardless of how many symbols match.
|
|
262
|
+
assert.equal(result.rankedRecall[0]!.boost, 1.0);
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
test("structural success: ranking is deterministic (stable id tie-break preserved)", async () => {
|
|
266
|
+
const candidates = [c("zeta", 0.5, ["x"]), c("alpha", 0.5, ["x"])];
|
|
267
|
+
const provider = providerReturning({ ok: true, symbols: [{ symbol: "x" }] });
|
|
268
|
+
const result = await packReviewContextStructural({
|
|
269
|
+
diff: DIFF_TOUCHING_AUTH,
|
|
270
|
+
candidates,
|
|
271
|
+
provider,
|
|
272
|
+
});
|
|
273
|
+
assert.deepEqual(
|
|
274
|
+
result.rankedRecall.map((r) => r.id),
|
|
275
|
+
["alpha", "zeta"],
|
|
276
|
+
);
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
// ──────────────────────────────────────────────────────────────────────────
|
|
280
|
+
// AbortSignal is forwarded to the provider (rule 40)
|
|
281
|
+
// ──────────────────────────────────────────────────────────────────────────
|
|
282
|
+
|
|
283
|
+
test("structural: abort signal is forwarded to symbolsForDiff", async () => {
|
|
284
|
+
let observed: AbortSignal | undefined;
|
|
285
|
+
const provider: StructuralContextProvider = {
|
|
286
|
+
id: "fake",
|
|
287
|
+
async probe() {
|
|
288
|
+
return { available: true };
|
|
289
|
+
},
|
|
290
|
+
async symbolsForDiff(_diff, opts) {
|
|
291
|
+
observed = opts?.signal;
|
|
292
|
+
return { ok: true, symbols: [] };
|
|
293
|
+
},
|
|
294
|
+
};
|
|
295
|
+
const ac = new AbortController();
|
|
296
|
+
await packReviewContextStructural({
|
|
297
|
+
diff: "",
|
|
298
|
+
candidates: [],
|
|
299
|
+
provider,
|
|
300
|
+
signal: ac.signal,
|
|
301
|
+
});
|
|
302
|
+
assert.equal(observed, ac.signal);
|
|
303
|
+
});
|
|
@@ -20,6 +20,11 @@
|
|
|
20
20
|
* surface is what PRs 5/6/7 will call).
|
|
21
21
|
*/
|
|
22
22
|
|
|
23
|
+
import type {
|
|
24
|
+
StructuralContextDegradation,
|
|
25
|
+
StructuralContextProvider,
|
|
26
|
+
} from "./structural-context.js";
|
|
27
|
+
|
|
23
28
|
// ──────────────────────────────────────────────────────────────────────────
|
|
24
29
|
// Public types
|
|
25
30
|
// ──────────────────────────────────────────────────────────────────────────
|
|
@@ -61,6 +66,15 @@ export interface ReviewContext {
|
|
|
61
66
|
* boost is recorded on each entry as `boost` for observability.
|
|
62
67
|
*/
|
|
63
68
|
rankedRecall: Array<ReviewCandidate & { boost: number }>;
|
|
69
|
+
/**
|
|
70
|
+
* Structural-context provider degradation observed while expanding the
|
|
71
|
+
* diff to touched symbols (issue #1548 Track A PR 5, #1536 pattern).
|
|
72
|
+
* Present only when a provider was consulted AND it failed — a silent
|
|
73
|
+
* provider failure must never look like "no structural matches"
|
|
74
|
+
* (CLAUDE.md rule 34). Undefined when no provider was consulted (the
|
|
75
|
+
* pure {@link packReviewContext} path) or when the provider succeeded.
|
|
76
|
+
*/
|
|
77
|
+
structuralDegradation?: StructuralContextDegradation;
|
|
64
78
|
}
|
|
65
79
|
|
|
66
80
|
// ──────────────────────────────────────────────────────────────────────────
|
|
@@ -423,3 +437,68 @@ export function packReviewContext(input: PackReviewContextInput): ReviewContext
|
|
|
423
437
|
const rankedRecall = rankReviewCandidates(input.candidates, touchedFiles);
|
|
424
438
|
return { touchedFiles, rankedRecall };
|
|
425
439
|
}
|
|
440
|
+
|
|
441
|
+
// ──────────────────────────────────────────────────────────────────────────
|
|
442
|
+
// Structural-context enhancement (issue #1548 Track A PR 5)
|
|
443
|
+
// ──────────────────────────────────────────────────────────────────────────
|
|
444
|
+
|
|
445
|
+
/**
|
|
446
|
+
* Input for the structural-context-aware review-context packer. The pure
|
|
447
|
+
* {@link packReviewContext} stays unchanged (gate-off parity); this async
|
|
448
|
+
* variant consults a {@link StructuralContextProvider} to expand the diff's
|
|
449
|
+
* touched FILE paths into touched SYMBOL names, widening the match set so
|
|
450
|
+
* memories that mention a changed symbol — not just the file path — float
|
|
451
|
+
* up. The gate (`structuralProvider !== "none"`) is checked ONCE by the
|
|
452
|
+
* caller, never inside this function (rule 39).
|
|
453
|
+
*/
|
|
454
|
+
export interface PackReviewContextStructuralInput extends PackReviewContextInput {
|
|
455
|
+
/** A probed structural-context provider. Never undefined here. */
|
|
456
|
+
provider: StructuralContextProvider;
|
|
457
|
+
/** Cancellation forwarded to the provider subprocess (rule 40). */
|
|
458
|
+
signal?: AbortSignal;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
/**
|
|
462
|
+
* Structural-context-aware packer.
|
|
463
|
+
*
|
|
464
|
+
* Contract (issue #1548 Track A PR 5, prove-fail-before characterization):
|
|
465
|
+
* - Provider returns symbols (`ok: true`) → symbol names are ADDED to the
|
|
466
|
+
* match set alongside touched files; memories mentioning a symbol get the
|
|
467
|
+
* same bounded additive boost as a file-path match.
|
|
468
|
+
* - Provider fails (`ok: false`, any code) → ranking falls back to
|
|
469
|
+
* FILE-PATH-ONLY boosting, BYTE-IDENTICAL to {@link packReviewContext}
|
|
470
|
+
* (the match set is exactly the touched files), AND `structuralDegradation`
|
|
471
|
+
* is populated so xray/doctor can surface the failure (rule 34).
|
|
472
|
+
*
|
|
473
|
+
* The underlying ranker ({@link rankReviewCandidates}) is reused unchanged,
|
|
474
|
+
* so the deterministic ordering, boost cap, and tie-break are identical to
|
|
475
|
+
* the pure path.
|
|
476
|
+
*/
|
|
477
|
+
export async function packReviewContextStructural(
|
|
478
|
+
input: PackReviewContextStructuralInput,
|
|
479
|
+
): Promise<ReviewContext> {
|
|
480
|
+
const touchedFiles = parseTouchedFiles(input.diff);
|
|
481
|
+
const diffString = typeof input.diff === "string" ? input.diff : "";
|
|
482
|
+
const result = await input.provider.symbolsForDiff(diffString, {
|
|
483
|
+
signal: input.signal,
|
|
484
|
+
});
|
|
485
|
+
|
|
486
|
+
let matchTerms: string[] = touchedFiles;
|
|
487
|
+
let structuralDegradation: StructuralContextDegradation | undefined;
|
|
488
|
+
|
|
489
|
+
if (result.ok) {
|
|
490
|
+
const symbolNames = result.symbols.map((sym) => sym.symbol);
|
|
491
|
+
matchTerms = symbolNames.length > 0 ? [...touchedFiles, ...symbolNames] : touchedFiles;
|
|
492
|
+
} else {
|
|
493
|
+
// Fallback: file-path-only ranking, identical to the pure packer. The
|
|
494
|
+
// degradation is surfaced so the failure is never silent (rule 34).
|
|
495
|
+
structuralDegradation = {
|
|
496
|
+
backend: "structural-context",
|
|
497
|
+
code: result.code,
|
|
498
|
+
detail: result.detail,
|
|
499
|
+
};
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
const rankedRecall = rankReviewCandidates(input.candidates, matchTerms);
|
|
503
|
+
return { touchedFiles, rankedRecall, structuralDegradation };
|
|
504
|
+
}
|