@remnic/core 9.3.699 → 9.3.700
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 +5 -5
- package/dist/access-boundary.js +14 -10
- package/dist/access-cli.js +28 -26
- package/dist/access-cli.js.map +1 -1
- package/dist/access-http.d.ts +5 -5
- package/dist/access-http.js +18 -14
- package/dist/access-mcp.d.ts +5 -5
- package/dist/access-mcp.js +17 -13
- package/dist/access-operations.d.ts +5 -5
- package/dist/access-operations.js +16 -12
- package/dist/access-schema.js +3 -3
- package/dist/{access-service-DftqtNUy.d.ts → access-service-Cte3ol0W.d.ts} +2 -2
- package/dist/access-service.d.ts +5 -5
- package/dist/access-service.js +13 -9
- package/dist/access-surface-catalog.d.ts +5 -5
- package/dist/action-confidence.d.ts +1 -1
- package/dist/action-confidence.js +2 -2
- package/dist/active-memory-bridge.d.ts +1 -1
- package/dist/active-recall.d.ts +1 -1
- package/dist/active-recall.js +1 -1
- package/dist/behavior-learner.d.ts +1 -1
- package/dist/behavior-signals.d.ts +1 -1
- package/dist/bootstrap.d.ts +4 -4
- package/dist/briefing.d.ts +1 -1
- package/dist/briefing.js +7 -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 +1 -1
- package/dist/{catalog-D7YDNNF7.d.ts → catalog-DN1PzThs.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 +8 -4
- package/dist/causal-consolidation.js.map +1 -1
- package/dist/{chunk-SJQ4HY3E.js → chunk-2NWHLAXX.js} +2 -2
- package/dist/{chunk-UDDSC6PO.js → chunk-33L6XHU2.js} +54 -6
- package/dist/chunk-33L6XHU2.js.map +1 -0
- package/dist/{chunk-XFG3PVZE.js → chunk-3E5WRQNQ.js} +8 -8
- package/dist/{chunk-EZWQZYLK.js → chunk-6JDGADXK.js} +2 -2
- package/dist/{chunk-IZ5E6ZTK.js → chunk-CCOXIDRM.js} +6 -6
- package/dist/{chunk-COEZR6F5.js → chunk-CHM274U6.js} +2 -2
- package/dist/{chunk-TIVZ4MCG.js → chunk-D75JXBV4.js} +3 -3
- package/dist/{chunk-M4DQWUKX.js → chunk-DR2JTSLZ.js} +2 -2
- package/dist/{chunk-XL5RSHZP.js → chunk-EOBJRBLC.js} +2 -2
- package/dist/{chunk-YY2HGKWR.js → chunk-G5PKTQ5J.js} +2 -2
- package/dist/{chunk-JYVD2XZ4.js → chunk-GA5A6MJH.js} +57 -24
- package/dist/chunk-GA5A6MJH.js.map +1 -0
- package/dist/{chunk-E6GOVHHJ.js → chunk-GYVVQYA3.js} +3 -3
- package/dist/{chunk-2KJG6ZZK.js → chunk-HDLC75NX.js} +2 -2
- package/dist/{chunk-AC5LO7IU.js → chunk-I4WBNM3M.js} +7 -1
- package/dist/chunk-I4WBNM3M.js.map +1 -0
- package/dist/{chunk-QVTSLRKT.js → chunk-IJEZMWKA.js} +3 -3
- package/dist/{chunk-ZXUOAFUG.js → chunk-IKNQAGBV.js} +1 -1
- package/dist/chunk-IKNQAGBV.js.map +1 -0
- package/dist/{chunk-N55RJT4N.js → chunk-JKW5XSWC.js} +2 -2
- package/dist/{chunk-CQ4PGFMC.js → chunk-JX3YZVII.js} +4 -4
- package/dist/{chunk-3LSSFCB4.js → chunk-K4DWSPMW.js} +11 -1
- package/dist/chunk-K4DWSPMW.js.map +1 -0
- package/dist/{chunk-HZ5DRA4Q.js → chunk-NHBEO3F3.js} +20 -20
- package/dist/{chunk-FSEQXHEZ.js → chunk-O54DY26V.js} +2 -2
- package/dist/{chunk-MRFVMSTR.js → chunk-OMNTQPZO.js} +20 -1
- package/dist/{chunk-MRFVMSTR.js.map → chunk-OMNTQPZO.js.map} +1 -1
- package/dist/chunk-PCZR32VL.js +446 -0
- package/dist/chunk-PCZR32VL.js.map +1 -0
- package/dist/{chunk-2K64VH66.js → chunk-PQG4T5V3.js} +22 -2
- package/dist/chunk-PQG4T5V3.js.map +1 -0
- package/dist/{chunk-JI6HWBYL.js → chunk-Q4O3ET6F.js} +2 -2
- package/dist/{chunk-ANZLT74L.js → chunk-RJ2THZ4H.js} +2 -2
- package/dist/{chunk-LPIWEKZ3.js → chunk-SDPDU2PM.js} +3 -3
- package/dist/{chunk-A62RAIBN.js → chunk-SEWF2O74.js} +2 -2
- package/dist/{chunk-2ULWWAQH.js → chunk-SMIVW7XC.js} +2 -2
- package/dist/{chunk-RTN2BLZM.js → chunk-T5QAZIBO.js} +2 -2
- package/dist/{chunk-RIC5U67B.js → chunk-X74FJSW7.js} +232 -4
- package/dist/chunk-X74FJSW7.js.map +1 -0
- package/dist/{chunk-SIDSEXUG.js → chunk-XY4WJTEX.js} +2 -2
- package/dist/{chunk-LIWU4QI6.js → chunk-YMTGXDN6.js} +5 -5
- package/dist/chunk-YPR7DOPD.js +286 -0
- package/dist/chunk-YPR7DOPD.js.map +1 -0
- package/dist/{chunk-MRX6S22R.js → chunk-YXIFA36P.js} +2 -2
- package/dist/{chunk-AH2JUU6X.js → chunk-Z2YFLXHU.js} +2 -2
- package/dist/{chunk-PJIHCOGV.js → chunk-ZDK2IW5F.js} +2 -2
- package/dist/{cli-DUMkkdLl.d.ts → cli--yVN9yEV.d.ts} +3 -3
- package/dist/cli.d.ts +6 -6
- package/dist/cli.js +32 -28
- package/dist/compounding/engine.d.ts +1 -1
- package/dist/compounding/engine.js +7 -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 +1 -1
- package/dist/connectors/codex-materialize-runner.d.ts +1 -1
- package/dist/connectors/codex-materialize-runner.js +7 -3
- package/dist/connectors/codex-materialize.d.ts +1 -1
- package/dist/connectors/index.d.ts +1 -1
- package/dist/connectors/index.js +7 -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/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/enrichment/index.d.ts +1 -1
- package/dist/entity-retrieval.d.ts +1 -1
- package/dist/entity-retrieval.js +7 -3
- package/dist/entity-schema.d.ts +1 -1
- package/dist/explicit-capture.d.ts +6 -6
- 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/fallback-llm.d.ts +1 -1
- package/dist/{forget-PLR6J5DN.js → forget-BEXG5PQC.js} +32 -1
- package/dist/forget-BEXG5PQC.js.map +1 -0
- package/dist/identity-continuity.d.ts +1 -1
- package/dist/importance.d.ts +1 -1
- package/dist/index.d.ts +21 -10
- package/dist/index.js +63 -41
- 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/maintenance/memory-governance.d.ts +1 -1
- package/dist/maintenance/memory-governance.js +7 -3
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +7 -3
- package/dist/maintenance/rebuild-memory-projection.js +8 -4
- package/dist/mcp-memory-inspector-app.d.ts +5 -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-provenance.js +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 +8 -4
- package/dist/namespaces/principal.d.ts +1 -1
- package/dist/namespaces/search.d.ts +1 -1
- package/dist/namespaces/storage.d.ts +20 -2
- package/dist/namespaces/storage.js +7 -3
- package/dist/native-knowledge.d.ts +1 -1
- package/dist/operator-toolkit.d.ts +19 -2
- package/dist/operator-toolkit.js +17 -11
- package/dist/orchestration/maintenance.d.ts +2 -2
- package/dist/orchestration/maintenance.js +9 -5
- package/dist/{orchestrator-Dv8JQu3-.d.ts → orchestrator-CJI4xdqV.d.ts} +3 -3
- package/dist/orchestrator.d.ts +4 -4
- package/dist/orchestrator.js +20 -18
- 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/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-explain-renderer.js +4 -4
- 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-cli.js +5 -5
- package/dist/recall-xray-renderer.d.ts +1 -1
- package/dist/recall-xray-renderer.js +4 -4
- package/dist/recall-xray.d.ts +1 -1
- package/dist/recall-xray.js +3 -3
- package/dist/resolve-auth-token.d.ts +1 -1
- package/dist/resume-bundles.js +2 -2
- 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 +28 -28
- package/dist/search/embed-helper.d.ts +1 -1
- package/dist/search/factory.d.ts +1 -1
- package/dist/search/index.d.ts +1 -1
- 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-CgREi9E0.d.ts → semantic-consolidation-BtUfv-AL.d.ts} +1 -1
- package/dist/semantic-consolidation.d.ts +2 -2
- package/dist/semantic-consolidation.js +8 -4
- package/dist/semantic-rule-promotion.js +7 -3
- package/dist/semantic-rule-verifier.d.ts +1 -1
- package/dist/semantic-rule-verifier.js +7 -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 +1 -1
- package/dist/signal.d.ts +1 -1
- package/dist/storage.d.ts +306 -2
- package/dist/storage.js +6 -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-supersession.js +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 +12 -12
- package/dist/{types-CeZYSC-E.d.ts → types-PuiPZ9iE.d.ts} +34 -1
- package/dist/types.d.ts +1 -1
- package/dist/types.js +1 -1
- package/dist/utility-runtime.d.ts +1 -1
- package/dist/utils/serialize-mutations.js +5 -280
- package/dist/utils/serialize-mutations.js.map +1 -1
- package/dist/verified-recall.js +7 -3
- package/package.json +2 -2
- package/src/config.ts +10 -0
- package/src/lifecycle/tombstones.test.ts +355 -0
- package/src/lifecycle/tombstones.ts +807 -0
- package/src/maintenance/forget.ts +41 -0
- package/src/maintenance/pattern-reinforcement.ts +47 -0
- package/src/memory-provenance.ts +12 -0
- package/src/namespaces/storage.ts +33 -0
- package/src/operator-toolkit.ts +77 -0
- package/src/orchestrator.ts +16 -0
- package/src/review/index.ts +48 -3
- package/src/storage.ts +329 -2
- package/src/temporal-supersession.ts +38 -0
- package/src/types.ts +34 -0
- package/dist/chunk-2K64VH66.js.map +0 -1
- package/dist/chunk-3LSSFCB4.js.map +0 -1
- package/dist/chunk-AC5LO7IU.js.map +0 -1
- package/dist/chunk-JYVD2XZ4.js.map +0 -1
- package/dist/chunk-RIC5U67B.js.map +0 -1
- package/dist/chunk-UDDSC6PO.js.map +0 -1
- package/dist/chunk-ZXUOAFUG.js.map +0 -1
- package/dist/forget-PLR6J5DN.js.map +0 -1
- /package/dist/{chunk-SJQ4HY3E.js.map → chunk-2NWHLAXX.js.map} +0 -0
- /package/dist/{chunk-XFG3PVZE.js.map → chunk-3E5WRQNQ.js.map} +0 -0
- /package/dist/{chunk-EZWQZYLK.js.map → chunk-6JDGADXK.js.map} +0 -0
- /package/dist/{chunk-IZ5E6ZTK.js.map → chunk-CCOXIDRM.js.map} +0 -0
- /package/dist/{chunk-COEZR6F5.js.map → chunk-CHM274U6.js.map} +0 -0
- /package/dist/{chunk-TIVZ4MCG.js.map → chunk-D75JXBV4.js.map} +0 -0
- /package/dist/{chunk-M4DQWUKX.js.map → chunk-DR2JTSLZ.js.map} +0 -0
- /package/dist/{chunk-XL5RSHZP.js.map → chunk-EOBJRBLC.js.map} +0 -0
- /package/dist/{chunk-YY2HGKWR.js.map → chunk-G5PKTQ5J.js.map} +0 -0
- /package/dist/{chunk-E6GOVHHJ.js.map → chunk-GYVVQYA3.js.map} +0 -0
- /package/dist/{chunk-2KJG6ZZK.js.map → chunk-HDLC75NX.js.map} +0 -0
- /package/dist/{chunk-QVTSLRKT.js.map → chunk-IJEZMWKA.js.map} +0 -0
- /package/dist/{chunk-N55RJT4N.js.map → chunk-JKW5XSWC.js.map} +0 -0
- /package/dist/{chunk-CQ4PGFMC.js.map → chunk-JX3YZVII.js.map} +0 -0
- /package/dist/{chunk-HZ5DRA4Q.js.map → chunk-NHBEO3F3.js.map} +0 -0
- /package/dist/{chunk-FSEQXHEZ.js.map → chunk-O54DY26V.js.map} +0 -0
- /package/dist/{chunk-JI6HWBYL.js.map → chunk-Q4O3ET6F.js.map} +0 -0
- /package/dist/{chunk-ANZLT74L.js.map → chunk-RJ2THZ4H.js.map} +0 -0
- /package/dist/{chunk-LPIWEKZ3.js.map → chunk-SDPDU2PM.js.map} +0 -0
- /package/dist/{chunk-A62RAIBN.js.map → chunk-SEWF2O74.js.map} +0 -0
- /package/dist/{chunk-2ULWWAQH.js.map → chunk-SMIVW7XC.js.map} +0 -0
- /package/dist/{chunk-RTN2BLZM.js.map → chunk-T5QAZIBO.js.map} +0 -0
- /package/dist/{chunk-SIDSEXUG.js.map → chunk-XY4WJTEX.js.map} +0 -0
- /package/dist/{chunk-LIWU4QI6.js.map → chunk-YMTGXDN6.js.map} +0 -0
- /package/dist/{chunk-MRX6S22R.js.map → chunk-YXIFA36P.js.map} +0 -0
- /package/dist/{chunk-AH2JUU6X.js.map → chunk-Z2YFLXHU.js.map} +0 -0
- /package/dist/{chunk-PJIHCOGV.js.map → chunk-ZDK2IW5F.js.map} +0 -0
|
@@ -22,6 +22,8 @@
|
|
|
22
22
|
*/
|
|
23
23
|
|
|
24
24
|
import type { StorageManager } from "../storage.js";
|
|
25
|
+
import { buildRetiredFactTombstoneInputs } from "../lifecycle/tombstones.js";
|
|
26
|
+
import { supersessionKeysForFact } from "../temporal-supersession.js";
|
|
25
27
|
import type { MemoryFile } from "../types.js";
|
|
26
28
|
|
|
27
29
|
export interface ForgetMemoryRequest {
|
|
@@ -100,6 +102,45 @@ export async function forgetMemory(
|
|
|
100
102
|
actor: "remnic-forget",
|
|
101
103
|
reasonCode: "operator_forget",
|
|
102
104
|
});
|
|
105
|
+
// Issue #1579 thread OchiF: emit a LIVE tombstone at forget time so a
|
|
106
|
+
// forgotten FACT cannot resurrect via re-extraction/import before an
|
|
107
|
+
// operator runs `remnic doctor --rebuild-tombstones`. Without this, the
|
|
108
|
+
// non-resurrection chokepoint in writeMemory has nothing to match — the
|
|
109
|
+
// forgotten fact's content hash is gone from the active index but no
|
|
110
|
+
// tombstone exists until a manual rebuild derives one — so the same fact
|
|
111
|
+
// written again through writeMemory becomes active. Mirrors supersedeMemory
|
|
112
|
+
// (contradiction) and applyTemporalSupersession via the shared
|
|
113
|
+
// buildRetiredFactTombstoneInputs helper. Best-effort (rule 34): a tombstone
|
|
114
|
+
// append failure must not fail the forget, which already succeeded on disk.
|
|
115
|
+
if (memory.frontmatter.category === "fact") {
|
|
116
|
+
for (const input of buildRetiredFactTombstoneInputs(
|
|
117
|
+
{
|
|
118
|
+
id,
|
|
119
|
+
content: memory.content,
|
|
120
|
+
...(memory.frontmatter.contentHash
|
|
121
|
+
? { contentHash: memory.frontmatter.contentHash }
|
|
122
|
+
: {}),
|
|
123
|
+
...(memory.frontmatter.entityRef
|
|
124
|
+
? { entityRef: memory.frontmatter.entityRef }
|
|
125
|
+
: {}),
|
|
126
|
+
...(memory.frontmatter.structuredAttributes
|
|
127
|
+
? { structuredAttributes: memory.frontmatter.structuredAttributes }
|
|
128
|
+
: {}),
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
reason: "retraction",
|
|
132
|
+
createdBy: "user_correction",
|
|
133
|
+
createdAt: forgottenAt,
|
|
134
|
+
supersessionKeysForFact,
|
|
135
|
+
},
|
|
136
|
+
)) {
|
|
137
|
+
try {
|
|
138
|
+
await storage.appendTombstone(input);
|
|
139
|
+
} catch {
|
|
140
|
+
// Best-effort — the forget already succeeded on disk.
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
103
144
|
return {
|
|
104
145
|
id,
|
|
105
146
|
path: memory.path,
|
|
@@ -33,6 +33,8 @@
|
|
|
33
33
|
*/
|
|
34
34
|
|
|
35
35
|
import { clusterByKey } from "../procedural/reinforcement-core.js";
|
|
36
|
+
import { buildRetiredFactTombstoneInputs, type TombstoneReason, type TombstoneCreatedBy } from "../lifecycle/tombstones.js";
|
|
37
|
+
import { supersessionKeysForFact } from "../temporal-supersession.js";
|
|
36
38
|
import type {
|
|
37
39
|
MemoryFile,
|
|
38
40
|
MemoryFrontmatter,
|
|
@@ -50,6 +52,21 @@ export interface PatternReinforcementStorage {
|
|
|
50
52
|
memory: MemoryFile,
|
|
51
53
|
patch: Partial<MemoryFrontmatter>,
|
|
52
54
|
): Promise<boolean>;
|
|
55
|
+
/**
|
|
56
|
+
* Best-effort tombstone append for retired duplicates (issue #1579 thread
|
|
57
|
+
* Oblq9). Optional so unit-test stubs need not implement it; the production
|
|
58
|
+
* StorageManager satisfies it via its appendTombstone chokepoint.
|
|
59
|
+
*/
|
|
60
|
+
appendTombstone?(input: {
|
|
61
|
+
reason: TombstoneReason;
|
|
62
|
+
createdBy: TombstoneCreatedBy;
|
|
63
|
+
sourceMemoryId: string;
|
|
64
|
+
contentHash?: string;
|
|
65
|
+
rawContent: string;
|
|
66
|
+
entityRef?: string;
|
|
67
|
+
supersessionKey?: string;
|
|
68
|
+
createdAt: string;
|
|
69
|
+
}): Promise<string | null>;
|
|
53
70
|
}
|
|
54
71
|
|
|
55
72
|
export interface PatternReinforcementOptions {
|
|
@@ -345,6 +362,36 @@ export async function runPatternReinforcement(
|
|
|
345
362
|
await storage.writeMemoryFrontmatter(dup, patch);
|
|
346
363
|
supersededIds.push(dup.frontmatter.id);
|
|
347
364
|
result.duplicatesSuperseded += 1;
|
|
365
|
+
// Issue #1579 thread Oblq9 / Ocn0C / OcoPs: emit a tombstone so the
|
|
366
|
+
// retired duplicate cannot resurrect through re-extraction / import.
|
|
367
|
+
// Pattern reinforcement retires via writeMemoryFrontmatter which
|
|
368
|
+
// bypasses the writeMemory chokepoint; without this emit, the same
|
|
369
|
+
// content re-extracted before a manual rebuild would be stored active.
|
|
370
|
+
// Threads Ocn0C/OcoPs: emit one tombstone PER derived supersession key
|
|
371
|
+
// via buildRetiredFactTombstoneInputs (same helper as supersedeMemory +
|
|
372
|
+
// forgetMemory), not a single entityRef-only record — a paraphrased
|
|
373
|
+
// re-extraction with the same entity/attribute can only be caught by
|
|
374
|
+
// the keyed tier. Best-effort (the interface method is optional; a
|
|
375
|
+
// failure must not fail the reinforcement job).
|
|
376
|
+
if (storage.appendTombstone) {
|
|
377
|
+
for (const input of buildRetiredFactTombstoneInputs(
|
|
378
|
+
{
|
|
379
|
+
id: dup.frontmatter.id,
|
|
380
|
+
content: dup.content,
|
|
381
|
+
contentHash: dup.frontmatter.contentHash,
|
|
382
|
+
entityRef: dup.frontmatter.entityRef,
|
|
383
|
+
structuredAttributes: dup.frontmatter.structuredAttributes,
|
|
384
|
+
},
|
|
385
|
+
{ reason: "supersession", createdBy: "supersession", createdAt: nowIso, supersessionKeysForFact },
|
|
386
|
+
)) {
|
|
387
|
+
try {
|
|
388
|
+
await storage.appendTombstone(input);
|
|
389
|
+
} catch {
|
|
390
|
+
// Best-effort: a tombstone append failure must not fail the
|
|
391
|
+
// reinforcement job (gotcha #13 / rule 34 spirit).
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
}
|
|
348
395
|
}
|
|
349
396
|
|
|
350
397
|
result.clusters.push({
|
package/src/memory-provenance.ts
CHANGED
|
@@ -328,6 +328,18 @@ function inferSafetyReasons(input: {
|
|
|
328
328
|
if (frontmatter.verificationState === "disputed") {
|
|
329
329
|
reasons.push("verification=disputed");
|
|
330
330
|
}
|
|
331
|
+
// Issue #1579 — x-ray visibility (rule 34: never a silent drop). When a
|
|
332
|
+
// memory carries `blockedBy`, it was tombstone-blocked at the storage
|
|
333
|
+
// chokepoint. Surface the tombstone id (and the matched tier when known)
|
|
334
|
+
// in the safety reasons so every recall x-ray consumer (CLI / HTTP / MCP)
|
|
335
|
+
// can attribute the pending_review state to a tombstone rather than a
|
|
336
|
+
// generic low-confidence review.
|
|
337
|
+
if (typeof frontmatter.blockedBy === "string" && frontmatter.blockedBy.length > 0) {
|
|
338
|
+
reasons.push(`tombstone-blocked=${frontmatter.blockedBy}`);
|
|
339
|
+
if (frontmatter.tombstoneBlockTier) {
|
|
340
|
+
reasons.push(`tombstone-tier=${frontmatter.tombstoneBlockTier}`);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
331
343
|
if (stale) reasons.push("stale=true");
|
|
332
344
|
|
|
333
345
|
const boundaryScopes = userContextScopes.filter(isUserBoundaryScope);
|
|
@@ -297,6 +297,9 @@ export class NamespaceStorageRouter {
|
|
|
297
297
|
// #1522: install the post-write catalog touch at the chokepoint — every
|
|
298
298
|
// successful write on this StorageManager records the namespace touch.
|
|
299
299
|
this.bindCatalogWriteHook(sm, ns);
|
|
300
|
+
// #1579: apply the tombstone non-resurrection config so every namespace
|
|
301
|
+
// storage enforces the invariant at its own writeMemory chokepoint.
|
|
302
|
+
this.applyTombstonesConfig(sm, ns);
|
|
300
303
|
this.cache.set(ns, sm);
|
|
301
304
|
this.notifyResolved(ns, root);
|
|
302
305
|
return sm;
|
|
@@ -387,6 +390,36 @@ export class NamespaceStorageRouter {
|
|
|
387
390
|
sm.onCatalogWrite = () => this.touchCatalogWrite(namespace, sm.dir);
|
|
388
391
|
}
|
|
389
392
|
|
|
393
|
+
/**
|
|
394
|
+
* Install the tombstone config on an externally-constructed StorageManager
|
|
395
|
+
* (issue #1579). Mirrors `bindCatalogWriteHook` — used for the legacy
|
|
396
|
+
* default-namespace storage that bypasses the router. Router-created
|
|
397
|
+
* storages are wired inline in `storageFor()` via `applyTombstonesConfig`.
|
|
398
|
+
*/
|
|
399
|
+
bindTombstonesConfig(
|
|
400
|
+
sm: StorageManager,
|
|
401
|
+
namespace: string,
|
|
402
|
+
config: { enabled: boolean; semanticMatch: boolean; semanticThreshold: number },
|
|
403
|
+
): void {
|
|
404
|
+
this.tombstonesGlobalConfig = { ...config };
|
|
405
|
+
sm.setTombstonesConfig({ ...config, namespace });
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
private tombstonesGlobalConfig: {
|
|
409
|
+
enabled: boolean;
|
|
410
|
+
semanticMatch: boolean;
|
|
411
|
+
semanticThreshold: number;
|
|
412
|
+
} = { enabled: false, semanticMatch: false, semanticThreshold: 0.9 };
|
|
413
|
+
|
|
414
|
+
/**
|
|
415
|
+
* Apply the tombstone config to a router-created StorageManager. Called
|
|
416
|
+
* inline in `storageFor()` so every namespace storage enforces the
|
|
417
|
+
* non-resurrection invariant, namespace-scoped (rule 42).
|
|
418
|
+
*/
|
|
419
|
+
private applyTombstonesConfig(sm: StorageManager, namespace: string): void {
|
|
420
|
+
sm.setTombstonesConfig({ ...this.tombstonesGlobalConfig, namespace });
|
|
421
|
+
}
|
|
422
|
+
|
|
390
423
|
/**
|
|
391
424
|
* Post-write catalog touch (issue #1522 chokepoint). Called by every
|
|
392
425
|
* StorageManager's post-write hook AFTER a successful write. Best-effort
|
package/src/operator-toolkit.ts
CHANGED
|
@@ -1326,6 +1326,12 @@ export async function runOperatorDoctor(options: OperatorDoctorOptions): Promise
|
|
|
1326
1326
|
details: namespaceMaintenanceHealth,
|
|
1327
1327
|
});
|
|
1328
1328
|
|
|
1329
|
+
// Tombstone non-resurrection invariant (issue #1579). Reports active
|
|
1330
|
+
// tombstone count, last append, corrupted-line count, and rebuild
|
|
1331
|
+
// staleness so operators can verify the invariant is enforced and know
|
|
1332
|
+
// when a rebuild is warranted. Informational: never errors on its own.
|
|
1333
|
+
checks.push(await summarizeTombstoneStatus(options.orchestrator.storage));
|
|
1334
|
+
|
|
1329
1335
|
const summary = checks.reduce(
|
|
1330
1336
|
(acc, check) => {
|
|
1331
1337
|
acc[check.status] += 1;
|
|
@@ -1871,6 +1877,77 @@ export async function summarizeObservationThroughput(
|
|
|
1871
1877
|
}
|
|
1872
1878
|
}
|
|
1873
1879
|
|
|
1880
|
+
/**
|
|
1881
|
+
* Tombstone non-resurrection invariant status for `remnic doctor`
|
|
1882
|
+
* (issue #1579).
|
|
1883
|
+
*
|
|
1884
|
+
* Reports:
|
|
1885
|
+
* - active tombstone count (excluding revoked)
|
|
1886
|
+
* - revoked count (re-allowed via the review queue)
|
|
1887
|
+
* - last append timestamp
|
|
1888
|
+
* - corrupted-line count (skipped, not crashed — rule 34/18)
|
|
1889
|
+
* - rebuild staleness (whether the in-memory index loaded cleanly)
|
|
1890
|
+
*
|
|
1891
|
+
* Always informational: a disabled or empty tombstone store is the expected
|
|
1892
|
+
* cold-install state and is never an error. A non-zero corrupted-line count
|
|
1893
|
+
* surfaces as `warn` so operators know a rebuild (`remnic doctor
|
|
1894
|
+
* --rebuild-tombstones`) will repair the log.
|
|
1895
|
+
*/
|
|
1896
|
+
export async function summarizeTombstoneStatus(
|
|
1897
|
+
storage: StorageManager,
|
|
1898
|
+
): Promise<OperatorDoctorCheck> {
|
|
1899
|
+
try {
|
|
1900
|
+
const stats = await storage.getTombstoneStats();
|
|
1901
|
+
if (stats === null) {
|
|
1902
|
+
return {
|
|
1903
|
+
key: "tombstones",
|
|
1904
|
+
status: "ok",
|
|
1905
|
+
summary: "Tombstone non-resurrection invariant is disabled (tombstonesEnabled=false).",
|
|
1906
|
+
details: { enabled: false },
|
|
1907
|
+
};
|
|
1908
|
+
}
|
|
1909
|
+
const corrupted = stats.corruptedLines > 0;
|
|
1910
|
+
const parts = [
|
|
1911
|
+
`${stats.count} active tombstone(s)`,
|
|
1912
|
+
...(stats.revoked > 0 ? [`${stats.revoked} revoked`] : []),
|
|
1913
|
+
];
|
|
1914
|
+
if (stats.lastAppendAt) {
|
|
1915
|
+
parts.push(`last append ${stats.lastAppendAt}`);
|
|
1916
|
+
}
|
|
1917
|
+
if (corrupted) {
|
|
1918
|
+
parts.push(`${stats.corruptedLines} corrupted line(s) skipped`);
|
|
1919
|
+
}
|
|
1920
|
+
return {
|
|
1921
|
+
key: "tombstones",
|
|
1922
|
+
status: corrupted ? "warn" : "ok",
|
|
1923
|
+
summary:
|
|
1924
|
+
`Tombstone invariant enabled. ${parts.join(", ")}. ` +
|
|
1925
|
+
(corrupted
|
|
1926
|
+
? "Run `remnic doctor --rebuild-tombstones` to repair the log."
|
|
1927
|
+
: "Re-observation of retired facts is blocked at the storage chokepoint."),
|
|
1928
|
+
remediation: corrupted
|
|
1929
|
+
? "Rebuild the tombstone log from retired memories on disk."
|
|
1930
|
+
: undefined,
|
|
1931
|
+
details: {
|
|
1932
|
+
enabled: true,
|
|
1933
|
+
count: stats.count,
|
|
1934
|
+
revoked: stats.revoked,
|
|
1935
|
+
lastAppendAt: stats.lastAppendAt,
|
|
1936
|
+
corruptedLines: stats.corruptedLines,
|
|
1937
|
+
loaded: stats.loaded,
|
|
1938
|
+
},
|
|
1939
|
+
};
|
|
1940
|
+
} catch (err) {
|
|
1941
|
+
return {
|
|
1942
|
+
key: "tombstones",
|
|
1943
|
+
status: "warn",
|
|
1944
|
+
summary: "Could not read tombstone store stats.",
|
|
1945
|
+
remediation: "Ensure the memory state directory is readable and rerun `remnic doctor`.",
|
|
1946
|
+
details: { error: String(err) },
|
|
1947
|
+
};
|
|
1948
|
+
}
|
|
1949
|
+
}
|
|
1950
|
+
|
|
1874
1951
|
export async function summarizeConsolidationProvenance(
|
|
1875
1952
|
storage: StorageManager,
|
|
1876
1953
|
config: Pick<PluginConfig, "memoryDir"> & { versioningSidecarDir?: string },
|
package/src/orchestrator.ts
CHANGED
|
@@ -2728,6 +2728,22 @@ export class Orchestrator {
|
|
|
2728
2728
|
maxVersionsPerPage: config.versioningMaxPerPage,
|
|
2729
2729
|
sidecarDir: config.versioningSidecarDir,
|
|
2730
2730
|
});
|
|
2731
|
+
// Wire the tombstone non-resurrection invariant (issue #1579) on the
|
|
2732
|
+
// primary default-namespace storage. Router-created namespace storages
|
|
2733
|
+
// get the same config via NamespaceStorageRouter.bindTombstonesConfig
|
|
2734
|
+
// (installed below) so every write path funnels through the chokepoint.
|
|
2735
|
+
const { tombstonesEnabled, tombstonesSemanticMatch, tombstonesSemanticThreshold } = config;
|
|
2736
|
+
this.storage.setTombstonesConfig({
|
|
2737
|
+
enabled: tombstonesEnabled,
|
|
2738
|
+
semanticMatch: tombstonesSemanticMatch,
|
|
2739
|
+
semanticThreshold: tombstonesSemanticThreshold,
|
|
2740
|
+
namespace: config.defaultNamespace,
|
|
2741
|
+
});
|
|
2742
|
+
this.storageRouter.bindTombstonesConfig(this.storage, config.defaultNamespace, {
|
|
2743
|
+
enabled: tombstonesEnabled,
|
|
2744
|
+
semanticMatch: tombstonesSemanticMatch,
|
|
2745
|
+
semanticThreshold: tombstonesSemanticThreshold,
|
|
2746
|
+
});
|
|
2731
2747
|
// Wire at-rest encryption (issue #690 PR 3/4).
|
|
2732
2748
|
// If secureStoreEnabled, check whether the keyring already holds a key
|
|
2733
2749
|
// for this memory dir (e.g. operator unlocked before daemon restart).
|
package/src/review/index.ts
CHANGED
|
@@ -29,7 +29,7 @@ export interface ReviewItem {
|
|
|
29
29
|
/** Created date */
|
|
30
30
|
created: string;
|
|
31
31
|
/** Reason it's in review */
|
|
32
|
-
reviewReason: "low_confidence" | "suggestion" | "contradiction" | "duplicate";
|
|
32
|
+
reviewReason: "low_confidence" | "suggestion" | "contradiction" | "duplicate" | "tombstone_blocked";
|
|
33
33
|
/** Additional context */
|
|
34
34
|
context?: string;
|
|
35
35
|
}
|
|
@@ -45,6 +45,8 @@ export interface ReviewResult {
|
|
|
45
45
|
updatedPath?: string;
|
|
46
46
|
/** Status message */
|
|
47
47
|
message: string;
|
|
48
|
+
/** Tombstone id cleared by an approve action (for the caller to revoke). */
|
|
49
|
+
clearedTombstoneId?: string;
|
|
48
50
|
}
|
|
49
51
|
|
|
50
52
|
export interface ReviewListResult {
|
|
@@ -70,6 +72,15 @@ export interface ReviewOptions {
|
|
|
70
72
|
export interface ReviewActionOptions {
|
|
71
73
|
/** Match the threshold used when listing review items (default: 0.7) */
|
|
72
74
|
confidenceThreshold?: number;
|
|
75
|
+
/**
|
|
76
|
+
* Revocation hook (issue #1579). When approving a memory whose frontmatter
|
|
77
|
+
* carries `blockedBy: <tombstoneId>`, the hook fires so the caller (CLI /
|
|
78
|
+
* orchestrator) can append a `kind: "revocation"` tombstone entry —
|
|
79
|
+
* re-allowing the content. Fire-and-forget: a revocation failure MUST NOT
|
|
80
|
+
* fail the approval (gotcha #13). The hook receives the tombstone id and
|
|
81
|
+
* the memory id.
|
|
82
|
+
*/
|
|
83
|
+
onApproveBlockedMemory?: (tombstoneId: string, memoryId: string) => void | Promise<void>;
|
|
73
84
|
}
|
|
74
85
|
|
|
75
86
|
interface ReviewFileMatch {
|
|
@@ -211,7 +222,7 @@ export function listReviewItems(options: ReviewOptions): ReviewListResult {
|
|
|
211
222
|
if (!fm?.id) return false;
|
|
212
223
|
|
|
213
224
|
const confidence = parseConfidence(fm.confidence, 1);
|
|
214
|
-
if (confidence >= confidenceThreshold) return false;
|
|
225
|
+
if (confidence >= confidenceThreshold && !(typeof fm.blockedBy === "string" && fm.blockedBy.length > 0)) return false;
|
|
215
226
|
if (parseBoolean(fm.reviewDismissed)) return false;
|
|
216
227
|
|
|
217
228
|
// Skip if already in items
|
|
@@ -226,7 +237,7 @@ export function listReviewItems(options: ReviewOptions): ReviewListResult {
|
|
|
226
237
|
source: (fm.source as string) ?? "unknown",
|
|
227
238
|
filePath,
|
|
228
239
|
created: (fm.created as string) ?? new Date().toISOString(),
|
|
229
|
-
reviewReason: "low_confidence",
|
|
240
|
+
reviewReason: (typeof fm.blockedBy === "string" && fm.blockedBy.length > 0) ? "tombstone_blocked" : "low_confidence",
|
|
230
241
|
});
|
|
231
242
|
return isLimitReached();
|
|
232
243
|
});
|
|
@@ -276,10 +287,32 @@ function approveItem(
|
|
|
276
287
|
const fm = parseFrontmatter(content);
|
|
277
288
|
if (!fm) return { itemId, action: "approve", message: "Could not parse frontmatter" };
|
|
278
289
|
|
|
290
|
+
// Issue #1579 — when approving a tombstone-blocked memory, fire the
|
|
291
|
+
// revocation hook so the content is re-allowed (append-only log: a
|
|
292
|
+
// `kind: "revocation"` entry supersedes the tombstone at lookup). Clear
|
|
293
|
+
// blockedBy/tombstoneBlockTier on the promoted memory so it is fully
|
|
294
|
+
// active. Fire-and-forget: a hook failure never fails the approval.
|
|
295
|
+
const blockedBy = typeof fm.blockedBy === "string" ? fm.blockedBy : null;
|
|
296
|
+
if (blockedBy && options.onApproveBlockedMemory) {
|
|
297
|
+
try {
|
|
298
|
+
const maybe = options.onApproveBlockedMemory(blockedBy, itemId);
|
|
299
|
+
if (maybe && typeof (maybe as Promise<void>).then === "function") {
|
|
300
|
+
void (maybe as Promise<void>).catch(() => undefined);
|
|
301
|
+
}
|
|
302
|
+
} catch {
|
|
303
|
+
/* fire-and-forget — never fail the approval (gotcha #13) */
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
279
307
|
const updatedContent = updateFrontmatterFields(content, {
|
|
280
308
|
confidence: "0.9",
|
|
281
309
|
confidenceTier: "high",
|
|
282
310
|
reviewDismissed: null,
|
|
311
|
+
// Clear the tombstone-block markers so the promoted memory is active.
|
|
312
|
+
// Clear the tombstone-block markers AND restore active status so the
|
|
313
|
+
// promoted memory is fully active in recall + provenance (issue #1579
|
|
314
|
+
// review: leaving status: pending_review kept it invisible to recall).
|
|
315
|
+
...(blockedBy ? { blockedBy: null, tombstoneBlockTier: null, status: "active" } : {}),
|
|
283
316
|
});
|
|
284
317
|
|
|
285
318
|
if (found.location === "category") {
|
|
@@ -289,6 +322,7 @@ function approveItem(
|
|
|
289
322
|
action: "approve",
|
|
290
323
|
updatedPath: found.filePath,
|
|
291
324
|
message: "Approved low-confidence memory in place with confidence 0.9",
|
|
325
|
+
...(blockedBy ? { clearedTombstoneId: blockedBy } : {}),
|
|
292
326
|
};
|
|
293
327
|
}
|
|
294
328
|
|
|
@@ -310,6 +344,7 @@ function approveItem(
|
|
|
310
344
|
action: "approve",
|
|
311
345
|
updatedPath: promotedPath,
|
|
312
346
|
message: `Promoted to ${category} with confidence 0.9`,
|
|
347
|
+
...(blockedBy ? { clearedTombstoneId: blockedBy } : {}),
|
|
313
348
|
};
|
|
314
349
|
}
|
|
315
350
|
|
|
@@ -418,6 +453,16 @@ function isLowConfidenceReviewCandidate(
|
|
|
418
453
|
options: ReviewActionOptions,
|
|
419
454
|
): boolean {
|
|
420
455
|
const threshold = options.confidenceThreshold ?? DEFAULT_CONFIDENCE_THRESHOLD;
|
|
456
|
+
// Issue #1579: tombstone-blocked facts are ALWAYS review candidates,
|
|
457
|
+
// regardless of confidence. The block sets status: pending_review +
|
|
458
|
+
// blockedBy; these must be visible in the review queue so an operator
|
|
459
|
+
// can approve (revoke the tombstone) or dismiss them. Without this, a
|
|
460
|
+
// high-confidence blocked fact is visible on disk but absent from the
|
|
461
|
+
// review UI — a silent drop, which violates the non-resurrection design
|
|
462
|
+
// (rule 34: visible, not silent).
|
|
463
|
+
if (typeof fm.blockedBy === "string" && fm.blockedBy.length > 0) {
|
|
464
|
+
return !parseBoolean(fm.reviewDismissed);
|
|
465
|
+
}
|
|
421
466
|
return (
|
|
422
467
|
parseConfidence(fm.confidence, 1) < threshold &&
|
|
423
468
|
!parseBoolean(fm.reviewDismissed)
|