@modusensus/dsh-mneme 0.8.13 → 0.8.15
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/LICENSE +21 -21
- package/README.md +655 -626
- package/assets/icon.svg +3 -3
- package/bin/cli.mjs +689 -689
- package/bin/dsh-mneme-mcp.mjs +510 -510
- package/bin/dsh-mneme-serve.mjs +151 -0
- package/cordis.patch.yml +21 -21
- package/docs/CONFIGURATION.md +263 -0
- package/docs/CONTINUITY.md +124 -0
- package/docs/DAEMON.md +46 -0
- package/docs/ENTITIES.md +245 -0
- package/docs/LOCAL_MODEL.md +222 -0
- package/docs/SEMANTIC.md +256 -0
- package/docs/SLEEP.md +164 -0
- package/docs/STORAGE.md +89 -0
- package/lib/api-standalone.js +673 -572
- package/lib/api.js +1437 -1433
- package/lib/bootstrap.js +211 -211
- package/lib/client.js +5444 -5295
- package/lib/commands.js +95 -95
- package/lib/config.js +880 -836
- package/lib/content-hash.js +54 -54
- package/lib/continuity.js +220 -220
- package/lib/document-index.js +94 -94
- package/lib/document.js +303 -303
- package/lib/dream/clustering.js +118 -118
- package/lib/dream/decisions.js +674 -674
- package/lib/dream/narratives.js +44 -44
- package/lib/dream/sleep.js +819 -819
- package/lib/dream.js +1501 -1501
- package/lib/embedding.js +177 -177
- package/lib/entities/extractor.js +252 -252
- package/lib/graph/anchoring.js +179 -179
- package/lib/guide.js +74 -74
- package/lib/heat.js +144 -144
- package/lib/hot-memory.js +46 -46
- package/lib/index.js +535 -671
- package/lib/inject.js +464 -450
- package/lib/lang.js +248 -229
- package/lib/local-embedder.js +358 -358
- package/lib/maintenance.js +142 -142
- package/lib/mirror.js +330 -330
- package/lib/organize.js +450 -450
- package/lib/peak-hours.js +117 -117
- package/lib/placement.js +13 -0
- package/lib/pre-inject-gate.js +340 -0
- package/lib/quality-filter.js +126 -126
- package/lib/recall-stats.js +175 -175
- package/lib/reranker.js +269 -269
- package/lib/runtime/adopt.js +176 -176
- package/lib/runtime/closure.js +172 -172
- package/lib/runtime/download.js +247 -247
- package/lib/runtime/layout.js +265 -265
- package/lib/runtime/loader.js +252 -252
- package/lib/runtime/model-fetch.js +361 -361
- package/lib/runtime/provision.js +237 -237
- package/lib/runtime/tarball.js +130 -130
- package/lib/runtime/verify.js +223 -204
- package/lib/scope.js +146 -146
- package/lib/search/adaptive.js +60 -60
- package/lib/search/bm25.js +96 -96
- package/lib/semantic.js +229 -0
- package/lib/sensitive-scan.js +209 -209
- package/lib/serve.js +215 -0
- package/lib/service.js +2663 -2596
- package/lib/settings.js +517 -509
- package/lib/store.js +3007 -3007
- package/lib/summarize.js +891 -856
- package/lib/tools.js +743 -737
- package/lib/vector-index.js +110 -110
- package/lib/version-check.js +122 -122
- package/lib/write-admission.js +402 -402
- package/locale/en.json +6 -6
- package/locale/zh.json +6 -6
- package/package.json +8 -4
- package/runtime-manifest.json +526 -526
- package/scripts/benchmark-embed.js +201 -201
- package/scripts/benchmark-recall.js +249 -249
- package/scripts/benchmark-rerank.js +166 -166
- package/scripts/build-runtime-manifest.mjs +224 -224
- package/scripts/changelog-prep.mjs +36 -0
- package/scripts/check-sync.js +42 -42
- package/scripts/e2e-dsh.js +220 -220
- package/scripts/mneme-runtime.mjs +277 -277
- package/scripts/stress-dsh.js +255 -255
- package/scripts/sync-lib.js +47 -47
- package/scripts/sync-test-badge.mjs +69 -69
- package/scripts/test-count-sync.mjs +44 -44
- package/src/api-standalone.js +673 -572
- package/src/api.js +1437 -1433
- package/src/bootstrap.js +211 -211
- package/src/commands.js +95 -95
- package/src/config.js +880 -836
- package/src/content-hash.js +54 -54
- package/src/continuity.js +220 -220
- package/src/document-index.js +94 -94
- package/src/document.js +303 -303
- package/src/dream/clustering.js +118 -118
- package/src/dream/decisions.js +674 -674
- package/src/dream/narratives.js +44 -44
- package/src/dream/sleep.js +819 -819
- package/src/dream.js +1501 -1501
- package/src/embedding.js +177 -177
- package/src/entities/extractor.js +252 -252
- package/src/graph/anchoring.js +179 -179
- package/src/guide.js +74 -74
- package/src/heat.js +144 -144
- package/src/hot-memory.js +46 -46
- package/src/index.js +535 -671
- package/src/inject.js +464 -450
- package/src/lang.js +248 -229
- package/src/local-embedder.js +358 -358
- package/src/maintenance.js +142 -142
- package/src/mirror.js +330 -330
- package/src/organize.js +450 -450
- package/src/peak-hours.js +117 -117
- package/src/placement.js +13 -0
- package/src/pre-inject-gate.js +340 -0
- package/src/quality-filter.js +126 -126
- package/src/recall-stats.js +175 -175
- package/src/reranker.js +269 -269
- package/src/runtime/adopt.js +176 -176
- package/src/runtime/closure.js +172 -172
- package/src/runtime/download.js +247 -247
- package/src/runtime/layout.js +265 -265
- package/src/runtime/loader.js +252 -252
- package/src/runtime/model-fetch.js +361 -361
- package/src/runtime/provision.js +237 -237
- package/src/runtime/tarball.js +130 -130
- package/src/runtime/verify.js +223 -204
- package/src/scope.js +146 -146
- package/src/search/adaptive.js +60 -60
- package/src/search/bm25.js +96 -96
- package/src/semantic.js +229 -0
- package/src/sensitive-scan.js +209 -209
- package/src/serve.js +215 -0
- package/src/service.js +2663 -2596
- package/src/settings.js +517 -509
- package/src/store.js +3007 -3007
- package/src/summarize.js +891 -856
- package/src/tools.js +743 -737
- package/src/vector-index.js +110 -110
- package/src/version-check.js +122 -122
- package/src/write-admission.js +402 -402
- package/test/adaptive-inject-budget.test.js +85 -85
- package/test/api-routes-gaps.test.js +218 -218
- package/test/api.test.js +1702 -1688
- package/test/audit.test.js +448 -448
- package/test/baseline-persist.test.js +248 -248
- package/test/benchmark.test.js +94 -94
- package/test/bootstrap.test.js +220 -220
- package/test/changelog-prep.test.js +60 -0
- package/test/client.test.js +1230 -1169
- package/test/clustering.test.js +100 -100
- package/test/commands.test.js +106 -106
- package/test/config.test.js +66 -66
- package/test/conflict-freeze.test.js +300 -300
- package/test/content-hash.test.js +204 -204
- package/test/continuity.test.js +333 -333
- package/test/document-dir.test.js +233 -233
- package/test/document.test.js +671 -671
- package/test/dream-archive-guardrail.test.js +116 -116
- package/test/dream-candidate-hybrid.test.js +281 -281
- package/test/dream-coverage-degrade.test.js +86 -86
- package/test/dream-failure-backoff.test.js +173 -173
- package/test/dream-peak-hours.test.js +194 -194
- package/test/dream.test.js +1385 -1383
- package/test/embedding-ready.test.js +196 -196
- package/test/entities.test.js +627 -547
- package/test/entity-recall.test.js +179 -179
- package/test/epistemic.test.js +325 -325
- package/test/fnew-0112.test.js +311 -311
- package/test/fnew-03.test.js +422 -422
- package/test/graph-anchoring.test.js +184 -184
- package/test/graph-api.test.js +175 -175
- package/test/graph-inject-budget.test.js +103 -103
- package/test/graph-passive-confirm.test.js +91 -91
- package/test/graph-weight-evolution.test.js +104 -104
- package/test/heat.test.js +175 -175
- package/test/helpers/dream-mock.js +82 -82
- package/test/helpers/peer-worker.mjs +25 -25
- package/test/helpers/runtime-source.js +60 -60
- package/test/helpers/tar-builder.js +80 -80
- package/test/helpers/write-admission-samples.js +308 -308
- package/test/hot-memory.test.js +145 -145
- package/test/inject-content-cap.test.js +91 -91
- package/test/inject-first-round-bm25.test.js +89 -89
- package/test/inject-heat.test.js +77 -61
- package/test/inject-parent-gate.test.js +106 -106
- package/test/inject-pools.test.js +194 -194
- package/test/inject-recall.test.js +105 -105
- package/test/inject-rotation.test.js +161 -161
- package/test/inject.test.js +420 -420
- package/test/lang.test.js +87 -87
- package/test/lastrunat-persist.test.js +101 -101
- package/test/lib-smoke.test.js +96 -96
- package/test/llm-audit.test.js +598 -598
- package/test/llm-message-source.test.js +207 -207
- package/test/local-embedder.test.js +292 -292
- package/test/maintenance.test.js +391 -391
- package/test/mcp-stdio.test.js +385 -385
- package/test/mirror-dirty.test.js +424 -424
- package/test/mirror-edit-digest.test.js +187 -187
- package/test/mirror-generation.test.js +499 -499
- package/test/mirror-types.test.js +208 -208
- package/test/mirror.test.js +249 -249
- package/test/narrative-bars.test.js +162 -162
- package/test/organize.test.js +418 -418
- package/test/peer-blockers.test.js +193 -193
- package/test/placement.test.js +36 -0
- package/test/policy-epoch.test.js +259 -259
- package/test/pre-inject-gate.test.js +434 -0
- package/test/quality-filter.test.js +186 -186
- package/test/reasoning-effort.test.js +752 -752
- package/test/recall-anti-update.test.js +82 -82
- package/test/recall-evals.test.js +253 -253
- package/test/recall-layer.test.js +326 -326
- package/test/recall-stats.test.js +189 -189
- package/test/receipt-chain.test.js +451 -451
- package/test/reflection.test.js +226 -226
- package/test/reindex-backfill.test.js +62 -62
- package/test/reranker-single-logit.test.js +73 -73
- package/test/reranker.test.js +197 -197
- package/test/resident-status.test.js +83 -83
- package/test/runtime-adopt.test.js +206 -206
- package/test/runtime-cli.test.js +89 -89
- package/test/runtime-closure.test.js +193 -193
- package/test/runtime-download.test.js +302 -302
- package/test/runtime-layout.test.js +163 -163
- package/test/runtime-loader.test.js +255 -255
- package/test/runtime-manifest.test.js +159 -159
- package/test/runtime-model-fetch.test.js +348 -348
- package/test/runtime-provision.test.js +101 -101
- package/test/runtime-tarball.test.js +81 -81
- package/test/runtime-verify.test.js +319 -279
- package/test/scope-candidates.test.js +239 -239
- package/test/scope-explicit.test.js +452 -452
- package/test/scope-inject-soft.test.js +106 -106
- package/test/scope-retrieval.test.js +197 -197
- package/test/scope-storage.test.js +255 -255
- package/test/scope-strict.test.js +367 -343
- package/test/search-fusion.test.js +90 -90
- package/test/semantic.test.js +124 -124
- package/test/sensitive-scan.test.js +207 -207
- package/test/serve-bin.test.js +121 -0
- package/test/serve.test.js +196 -0
- package/test/service-search.test.js +174 -174
- package/test/service.test.js +405 -405
- package/test/settings.test.js +261 -261
- package/test/skip-invalid-restore.test.js +262 -262
- package/test/sleep-action-set.test.js +422 -422
- package/test/sleep-heat.test.js +126 -126
- package/test/sleep-scheduler-rearm.test.js +89 -89
- package/test/sleep.test.js +527 -527
- package/test/standalone-api.test.js +751 -544
- package/test/store-vector-cache.test.js +98 -98
- package/test/store.test.js +396 -396
- package/test/stress.test.js +209 -209
- package/test/summarize-occurred-at.test.js +127 -0
- package/test/summarize-reasoning-effort.test.js +189 -189
- package/test/summarize.test.js +1465 -1465
- package/test/supersede-state-tracking.test.js +394 -0
- package/test/test-count-sync.test.js +60 -60
- package/test/tools-dto-schema-matrix.test.js +325 -325
- package/test/tools-memory-get-schema.test.js +65 -65
- package/test/tools.test.js +428 -374
- package/test/updated-at-semantics.test.js +113 -113
- package/test/vector-index.test.js +221 -221
- package/test/version-check.test.js +152 -152
- package/test/webServer-optional.test.js +82 -82
- package/test/write-admission.test.js +668 -668
|
@@ -1,82 +1,82 @@
|
|
|
1
|
-
// Anti-update 回归集(E6,#280 链;StatemenBench anti-update probes)。
|
|
2
|
-
//
|
|
3
|
-
// 锁的回归类:**supersede 后旧值复现**。取代关系没有专属列——落库状态完全
|
|
4
|
-
// 由 applySupersede(dream/decisions.js)的产物表达:loser 的 archived=1 +
|
|
5
|
-
// 正文 (superseded by: …) 注记。四条默认检索路径各自过滤 archived(keyword
|
|
6
|
-
// store.js:1528、vector store.js:1564、BM25 池 service.js bm25Recall、entity
|
|
7
|
-
// service.js:470),本文件把它们钉成「排除,而非降权」:
|
|
8
|
-
// - 任何一路让归档旧条回到候选集 → 红;
|
|
9
|
-
// - 归档行被误删/误遗忘(取代是归档不是删除)→ 红;
|
|
10
|
-
// - includeArchived 显式口子仍在(排除发生在检索层,不是行消失)→ 红。
|
|
11
|
-
// seedService 已种好取代对(winner = mem_rust_switch,loser = mem_rust_switch_old)。
|
|
12
|
-
import test from "node:test";
|
|
13
|
-
import assert from "node:assert/strict";
|
|
14
|
-
import { runBenchmark, runFusionBenchmark, seedService } from "../scripts/benchmark-recall.js";
|
|
15
|
-
|
|
16
|
-
const WINNER = "mem_rust_switch";
|
|
17
|
-
const LOSER = "mem_rust_switch_old";
|
|
18
|
-
|
|
19
|
-
test("benchmark 里带 forbidden 的 anti-update 用例在 legacy/fused 两配置下都零泄露", async () => {
|
|
20
|
-
const report = await runBenchmark({ topK: 5 });
|
|
21
|
-
for (const run of report.runs) {
|
|
22
|
-
const probes = run.rows.filter((r) => r.forbidden?.length);
|
|
23
|
-
assert.ok(probes.length >= 2, `${run.config}: anti-update probes present`);
|
|
24
|
-
for (const r of probes) {
|
|
25
|
-
assert.equal(r.forbiddenHit, 0, `${run.config} "${r.query}": superseded old row leaked`);
|
|
26
|
-
assert.equal(r.recall, 1, `${run.config} "${r.query}": winner must still answer the topic`);
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
test("fusion 三个配方同样零泄露(排除发生在融合上游,与配方无关)", async () => {
|
|
32
|
-
const report = await runFusionBenchmark({ topK: 5 });
|
|
33
|
-
for (const run of report.runs) {
|
|
34
|
-
for (const r of run.rows) {
|
|
35
|
-
if (!r.forbidden?.length) continue;
|
|
36
|
-
assert.equal(r.forbiddenHit, 0, `${run.config} "${r.query}": leaked via fusion recipe`);
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
test("mode=keyword / vector / auto 三条路径都不再返回被取代旧值", async () => {
|
|
42
|
-
const svc = seedService();
|
|
43
|
-
// 查询用 winner 标题「语言迁移决策」:toy 哈希嵌入按连续 CJK 段整段分词,
|
|
44
|
-
// 只有这个串在 keyword(LIKE 子串)与 vector(token 精确重叠)两条路上都
|
|
45
|
-
// 能命中 winner。注意 loser 的 superseded-by 注记里恰好也含这个串——
|
|
46
|
-
// 归档排除一旦失效,注记自己就会把旧值钓回来,本用例立即红。
|
|
47
|
-
for (const mode of ["keyword", "vector", "auto"]) {
|
|
48
|
-
const rows = await svc.searchMemories("语言迁移决策", { mode, topK: 10, useRerank: false });
|
|
49
|
-
const ids = rows.map((r) => r.id);
|
|
50
|
-
assert.ok(!ids.includes(LOSER), `mode=${mode}: archived loser resurfaced`);
|
|
51
|
-
assert.ok(ids.includes(WINNER), `mode=${mode}: winner missing from default recall`);
|
|
52
|
-
}
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
test("BM25 的候选池(store.list)不含归档行——降权化改动在这里最先红", async () => {
|
|
56
|
-
const svc = seedService();
|
|
57
|
-
// bm25Recall 的取数源就是 store.list({limit, includeForgotten:false}) 再
|
|
58
|
-
// filter !m.archived;list 层面归档行本就不回来,这里锁「池子是干净的」。
|
|
59
|
-
const pool = svc.list({ limit: 500, includeForgotten: false });
|
|
60
|
-
assert.ok(pool.length > 0, "pool non-empty");
|
|
61
|
-
assert.ok(!pool.some((m) => m.id === LOSER), "archived loser must not enter the bm25 pool");
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
test("取代是归档不是删除:旧行还在、未遗忘、带 superseded-by 注记", async () => {
|
|
65
|
-
const svc = seedService();
|
|
66
|
-
const loser = svc.getById(LOSER);
|
|
67
|
-
assert.ok(loser, "loser row must still exist");
|
|
68
|
-
assert.equal(loser.archived, true, "loser must be archived (supersede semantics)");
|
|
69
|
-
assert.equal(loser.forgotten, false, "supersede must not forget the row (heat/forget 是另一条线)");
|
|
70
|
-
assert.ok(loser.content.includes("(superseded by: 语言迁移决策)"), "content carries the superseded-by note");
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
test("includeArchived 显式口子仍在:排除发生在检索层,不是行消失", async () => {
|
|
74
|
-
const svc = seedService();
|
|
75
|
-
// store.search 是整串 LIKE 子串扫描(不切词),所以这里用 loser 正文的
|
|
76
|
-
// 连续子串作探针;被取代旧条不在默认结果里、但在 includeArchived 下回来。
|
|
77
|
-
const probe = "选型理由是部署简单";
|
|
78
|
-
const without = svc.search(probe, { limit: 50 });
|
|
79
|
-
const withArchived = svc.search(probe, { limit: 50, includeArchived: true });
|
|
80
|
-
assert.ok(!without.some((m) => m.id === LOSER), "default search excludes the archived loser");
|
|
81
|
-
assert.ok(withArchived.some((m) => m.id === LOSER), "includeArchived must still surface it (row exists)");
|
|
82
|
-
});
|
|
1
|
+
// Anti-update 回归集(E6,#280 链;StatemenBench anti-update probes)。
|
|
2
|
+
//
|
|
3
|
+
// 锁的回归类:**supersede 后旧值复现**。取代关系没有专属列——落库状态完全
|
|
4
|
+
// 由 applySupersede(dream/decisions.js)的产物表达:loser 的 archived=1 +
|
|
5
|
+
// 正文 (superseded by: …) 注记。四条默认检索路径各自过滤 archived(keyword
|
|
6
|
+
// store.js:1528、vector store.js:1564、BM25 池 service.js bm25Recall、entity
|
|
7
|
+
// service.js:470),本文件把它们钉成「排除,而非降权」:
|
|
8
|
+
// - 任何一路让归档旧条回到候选集 → 红;
|
|
9
|
+
// - 归档行被误删/误遗忘(取代是归档不是删除)→ 红;
|
|
10
|
+
// - includeArchived 显式口子仍在(排除发生在检索层,不是行消失)→ 红。
|
|
11
|
+
// seedService 已种好取代对(winner = mem_rust_switch,loser = mem_rust_switch_old)。
|
|
12
|
+
import test from "node:test";
|
|
13
|
+
import assert from "node:assert/strict";
|
|
14
|
+
import { runBenchmark, runFusionBenchmark, seedService } from "../scripts/benchmark-recall.js";
|
|
15
|
+
|
|
16
|
+
const WINNER = "mem_rust_switch";
|
|
17
|
+
const LOSER = "mem_rust_switch_old";
|
|
18
|
+
|
|
19
|
+
test("benchmark 里带 forbidden 的 anti-update 用例在 legacy/fused 两配置下都零泄露", async () => {
|
|
20
|
+
const report = await runBenchmark({ topK: 5 });
|
|
21
|
+
for (const run of report.runs) {
|
|
22
|
+
const probes = run.rows.filter((r) => r.forbidden?.length);
|
|
23
|
+
assert.ok(probes.length >= 2, `${run.config}: anti-update probes present`);
|
|
24
|
+
for (const r of probes) {
|
|
25
|
+
assert.equal(r.forbiddenHit, 0, `${run.config} "${r.query}": superseded old row leaked`);
|
|
26
|
+
assert.equal(r.recall, 1, `${run.config} "${r.query}": winner must still answer the topic`);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
test("fusion 三个配方同样零泄露(排除发生在融合上游,与配方无关)", async () => {
|
|
32
|
+
const report = await runFusionBenchmark({ topK: 5 });
|
|
33
|
+
for (const run of report.runs) {
|
|
34
|
+
for (const r of run.rows) {
|
|
35
|
+
if (!r.forbidden?.length) continue;
|
|
36
|
+
assert.equal(r.forbiddenHit, 0, `${run.config} "${r.query}": leaked via fusion recipe`);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test("mode=keyword / vector / auto 三条路径都不再返回被取代旧值", async () => {
|
|
42
|
+
const svc = seedService();
|
|
43
|
+
// 查询用 winner 标题「语言迁移决策」:toy 哈希嵌入按连续 CJK 段整段分词,
|
|
44
|
+
// 只有这个串在 keyword(LIKE 子串)与 vector(token 精确重叠)两条路上都
|
|
45
|
+
// 能命中 winner。注意 loser 的 superseded-by 注记里恰好也含这个串——
|
|
46
|
+
// 归档排除一旦失效,注记自己就会把旧值钓回来,本用例立即红。
|
|
47
|
+
for (const mode of ["keyword", "vector", "auto"]) {
|
|
48
|
+
const rows = await svc.searchMemories("语言迁移决策", { mode, topK: 10, useRerank: false });
|
|
49
|
+
const ids = rows.map((r) => r.id);
|
|
50
|
+
assert.ok(!ids.includes(LOSER), `mode=${mode}: archived loser resurfaced`);
|
|
51
|
+
assert.ok(ids.includes(WINNER), `mode=${mode}: winner missing from default recall`);
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
test("BM25 的候选池(store.list)不含归档行——降权化改动在这里最先红", async () => {
|
|
56
|
+
const svc = seedService();
|
|
57
|
+
// bm25Recall 的取数源就是 store.list({limit, includeForgotten:false}) 再
|
|
58
|
+
// filter !m.archived;list 层面归档行本就不回来,这里锁「池子是干净的」。
|
|
59
|
+
const pool = svc.list({ limit: 500, includeForgotten: false });
|
|
60
|
+
assert.ok(pool.length > 0, "pool non-empty");
|
|
61
|
+
assert.ok(!pool.some((m) => m.id === LOSER), "archived loser must not enter the bm25 pool");
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
test("取代是归档不是删除:旧行还在、未遗忘、带 superseded-by 注记", async () => {
|
|
65
|
+
const svc = seedService();
|
|
66
|
+
const loser = svc.getById(LOSER);
|
|
67
|
+
assert.ok(loser, "loser row must still exist");
|
|
68
|
+
assert.equal(loser.archived, true, "loser must be archived (supersede semantics)");
|
|
69
|
+
assert.equal(loser.forgotten, false, "supersede must not forget the row (heat/forget 是另一条线)");
|
|
70
|
+
assert.ok(loser.content.includes("(superseded by: 语言迁移决策)"), "content carries the superseded-by note");
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
test("includeArchived 显式口子仍在:排除发生在检索层,不是行消失", async () => {
|
|
74
|
+
const svc = seedService();
|
|
75
|
+
// store.search 是整串 LIKE 子串扫描(不切词),所以这里用 loser 正文的
|
|
76
|
+
// 连续子串作探针;被取代旧条不在默认结果里、但在 includeArchived 下回来。
|
|
77
|
+
const probe = "选型理由是部署简单";
|
|
78
|
+
const without = svc.search(probe, { limit: 50 });
|
|
79
|
+
const withArchived = svc.search(probe, { limit: 50, includeArchived: true });
|
|
80
|
+
assert.ok(!without.some((m) => m.id === LOSER), "default search excludes the archived loser");
|
|
81
|
+
assert.ok(withArchived.some((m) => m.id === LOSER), "includeArchived must still surface it (row exists)");
|
|
82
|
+
});
|