@modusensus/dsh-mneme 0.14.0-beta.1 → 0.14.0-beta.2
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 +830 -830
- package/assets/icon.svg +3 -3
- package/bin/cli.mjs +964 -964
- package/bin/dsh-mneme-choro.mjs +262 -262
- package/bin/dsh-mneme-mcp.mjs +510 -510
- package/bin/dsh-mneme-serve.mjs +151 -151
- package/cordis.patch.yml +21 -21
- package/docs/CONFIGURATION.md +407 -404
- package/docs/CONTINUITY.md +139 -139
- package/docs/DAEMON.md +57 -57
- package/docs/DIRECTORIES.md +114 -114
- package/docs/DISTILL.md +397 -371
- package/docs/DSH-GO.md +97 -97
- package/docs/ENTITIES.md +247 -247
- package/docs/FORGET.md +77 -77
- package/docs/KB.md +110 -110
- package/docs/LOCAL_MODEL.md +222 -222
- package/docs/SEMANTIC.md +256 -256
- package/docs/SHARE.md +552 -552
- package/docs/SLEEP.md +202 -202
- package/docs/STORAGE.md +122 -122
- package/docs/WORKFLOWS.md +366 -366
- package/lib/api-standalone.js +1007 -1007
- package/lib/api.js +3600 -3600
- package/lib/attachment-text.js +122 -122
- package/lib/bootstrap-http.js +105 -105
- package/lib/bootstrap.js +215 -215
- package/lib/candidates.js +454 -454
- package/lib/capabilities.js +819 -819
- package/lib/choro-command.js +111 -111
- package/lib/client.js +14420 -14322
- package/lib/commands.js +95 -95
- package/lib/config-keys.js +144 -144
- package/lib/config.js +1361 -1344
- package/lib/content-hash.js +54 -54
- package/lib/continuity.js +258 -258
- package/lib/date-hint.js +32 -0
- package/lib/diary/auto.js +190 -190
- package/lib/diary/compose.js +440 -440
- package/lib/diary/dir.js +59 -59
- package/lib/diary/export.js +97 -97
- package/lib/diary/index.js +26 -26
- package/lib/diary/library.js +189 -189
- package/lib/diary/qq-inbox.js +142 -142
- package/lib/diary/rollover.js +61 -61
- package/lib/diary/session-provider.js +122 -122
- package/lib/document-index.js +94 -94
- package/lib/document.js +322 -322
- package/lib/dream/clustering.js +123 -123
- package/lib/dream/decisions.js +945 -945
- package/lib/dream/narratives.js +44 -44
- package/lib/dream/sleep.js +2027 -2027
- package/lib/dream/triage.js +139 -139
- package/lib/dream.js +1879 -1879
- package/lib/dsh-go/host.js +162 -162
- package/lib/dsh-go/panel.js +2091 -2091
- package/lib/dsh-go/text.js +342 -342
- package/lib/dsh-go/views/common.js +68 -68
- package/lib/dsh-go/views/entity.js +48 -48
- package/lib/dsh-go/views/memory.js +198 -198
- package/lib/dsh-go/views/root.js +30 -30
- package/lib/dsh-go/views/settings.js +398 -398
- package/lib/dsh-go/views/status.js +51 -51
- package/lib/embedding.js +342 -342
- package/lib/entities/extractor.js +285 -285
- package/lib/fence.js +24 -24
- package/lib/graph/anchoring.js +184 -184
- package/lib/guide.js +74 -74
- package/lib/heat.js +144 -144
- package/lib/hot-memory.js +119 -119
- package/lib/image-describe.js +158 -0
- package/lib/index.js +852 -852
- package/lib/inject.js +656 -651
- package/lib/kb/index.js +309 -309
- package/lib/kb/routes.js +219 -219
- package/lib/kb/tools.js +148 -148
- package/lib/lang.js +607 -595
- package/lib/local-embedder.js +358 -358
- package/lib/maintenance.js +162 -162
- package/lib/memory-source.js +9 -9
- package/lib/mirror.js +330 -330
- package/lib/organize.js +456 -456
- package/lib/peak-hours.js +172 -172
- package/lib/pipeline-activities.js +178 -178
- package/lib/pipeline-attribution.js +165 -165
- package/lib/pipeline-command.js +279 -279
- package/lib/pipeline-confirm.js +142 -142
- package/lib/pipeline-exec.js +174 -174
- package/lib/pipeline-nudge-auto.js +129 -129
- package/lib/pipeline-nudge.js +1623 -1623
- package/lib/pipeline-observations.js +181 -181
- package/lib/pipeline-observe.js +133 -133
- package/lib/pipeline-runner.js +361 -361
- package/lib/pipeline-runs.js +442 -442
- package/lib/pipeline.js +716 -716
- package/lib/placement.js +13 -13
- package/lib/pre-inject-gate.js +354 -354
- package/lib/profile-extract.js +164 -164
- package/lib/profile-facts.js +356 -356
- package/lib/provenance-http.js +251 -251
- package/lib/quality-filter.js +162 -162
- package/lib/recall-stats.js +190 -190
- package/lib/reclaim-http.js +150 -150
- 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 -223
- package/lib/safe-slice.js +18 -18
- package/lib/scope-state.js +277 -277
- package/lib/scope.js +187 -187
- package/lib/search/adaptive.js +60 -60
- package/lib/search/bm25.js +96 -96
- package/lib/semantic.js +572 -572
- package/lib/sensitive-scan.js +209 -209
- package/lib/serve.js +228 -228
- package/lib/service.js +3268 -3268
- package/lib/session-gate.js +91 -91
- package/lib/session-usage.js +350 -350
- package/lib/settings.js +827 -817
- package/lib/share-http.js +281 -281
- package/lib/share-import.js +234 -234
- package/lib/share.js +335 -335
- package/lib/store.js +5895 -5895
- package/lib/summarize.js +1388 -1343
- package/lib/tools.js +1370 -1356
- package/lib/user-settings.js +230 -230
- package/lib/vector-index.js +180 -180
- package/lib/version-check.js +122 -122
- package/lib/wiki/claims.js +267 -267
- package/lib/wiki/cli-hint.js +90 -90
- package/lib/wiki/inject.js +164 -164
- package/lib/wiki/partition.js +200 -200
- package/lib/wiki/plan.js +214 -214
- package/lib/wiki/queries.js +640 -640
- package/lib/wiki/render.js +261 -261
- package/lib/wiki/scan.js +371 -371
- package/lib/wiki/t0.js +500 -500
- package/lib/wiki/t1-jobs.js +232 -232
- package/lib/wiki/t1.js +609 -609
- package/lib/write-admission.js +404 -404
- package/locale/en.json +6 -6
- package/locale/zh.json +6 -6
- package/package.json +1 -1
- 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 +91 -91
- package/scripts/check-sync.js +44 -44
- package/scripts/e2e-dsh.js +220 -220
- package/scripts/mneme-runtime.mjs +277 -277
- package/scripts/publish-gate.mjs +197 -197
- package/scripts/release-docs.mjs +210 -210
- package/scripts/release-notes.mjs +164 -164
- 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 +48 -48
- package/src/api-standalone.js +1007 -1007
- package/src/api.js +3600 -3600
- package/src/attachment-text.js +122 -122
- package/src/bootstrap-http.js +105 -105
- package/src/bootstrap.js +215 -215
- package/src/candidates.js +454 -454
- package/src/capabilities.js +819 -819
- package/src/choro-command.js +111 -111
- package/src/commands.js +95 -95
- package/src/config-keys.js +144 -144
- package/src/config.js +1361 -1344
- package/src/content-hash.js +54 -54
- package/src/continuity.js +258 -258
- package/src/date-hint.js +32 -0
- package/src/diary/auto.js +190 -190
- package/src/diary/compose.js +440 -440
- package/src/diary/dir.js +59 -59
- package/src/diary/export.js +97 -97
- package/src/diary/index.js +26 -26
- package/src/diary/library.js +189 -189
- package/src/diary/qq-inbox.js +142 -142
- package/src/diary/rollover.js +61 -61
- package/src/diary/session-provider.js +122 -122
- package/src/document-index.js +94 -94
- package/src/document.js +322 -322
- package/src/dream/clustering.js +123 -123
- package/src/dream/decisions.js +945 -945
- package/src/dream/narratives.js +44 -44
- package/src/dream/sleep.js +2027 -2027
- package/src/dream/triage.js +139 -139
- package/src/dream.js +1879 -1879
- package/src/dsh-go/host.js +162 -162
- package/src/dsh-go/panel.js +2091 -2091
- package/src/dsh-go/text.js +342 -342
- package/src/dsh-go/views/common.js +68 -68
- package/src/dsh-go/views/entity.js +48 -48
- package/src/dsh-go/views/memory.js +198 -198
- package/src/dsh-go/views/root.js +30 -30
- package/src/dsh-go/views/settings.js +398 -398
- package/src/dsh-go/views/status.js +51 -51
- package/src/embedding.js +342 -342
- package/src/entities/extractor.js +285 -285
- package/src/fence.js +24 -24
- package/src/graph/anchoring.js +184 -184
- package/src/guide.js +74 -74
- package/src/heat.js +144 -144
- package/src/hot-memory.js +119 -119
- package/src/image-describe.js +158 -0
- package/src/index.js +852 -852
- package/src/inject.js +656 -651
- package/src/kb/index.js +309 -309
- package/src/kb/routes.js +219 -219
- package/src/kb/tools.js +148 -148
- package/src/lang.js +607 -595
- package/src/local-embedder.js +358 -358
- package/src/maintenance.js +162 -162
- package/src/memory-source.js +9 -9
- package/src/mirror.js +330 -330
- package/src/organize.js +456 -456
- package/src/peak-hours.js +172 -172
- package/src/pipeline-activities.js +178 -178
- package/src/pipeline-attribution.js +165 -165
- package/src/pipeline-command.js +279 -279
- package/src/pipeline-confirm.js +142 -142
- package/src/pipeline-exec.js +174 -174
- package/src/pipeline-nudge-auto.js +129 -129
- package/src/pipeline-nudge.js +1623 -1623
- package/src/pipeline-observations.js +181 -181
- package/src/pipeline-observe.js +133 -133
- package/src/pipeline-runner.js +361 -361
- package/src/pipeline-runs.js +442 -442
- package/src/pipeline.js +716 -716
- package/src/placement.js +13 -13
- package/src/pre-inject-gate.js +354 -354
- package/src/profile-extract.js +164 -164
- package/src/profile-facts.js +356 -356
- package/src/provenance-http.js +251 -251
- package/src/quality-filter.js +162 -162
- package/src/recall-stats.js +190 -190
- package/src/reclaim-http.js +150 -150
- 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 -223
- package/src/safe-slice.js +18 -18
- package/src/scope-state.js +277 -277
- package/src/scope.js +187 -187
- package/src/search/adaptive.js +60 -60
- package/src/search/bm25.js +96 -96
- package/src/semantic.js +572 -572
- package/src/sensitive-scan.js +209 -209
- package/src/serve.js +228 -228
- package/src/service.js +3268 -3268
- package/src/session-gate.js +91 -91
- package/src/session-usage.js +350 -350
- package/src/settings.js +827 -817
- package/src/share-http.js +281 -281
- package/src/share-import.js +234 -234
- package/src/share.js +335 -335
- package/src/store.js +5895 -5895
- package/src/summarize.js +1388 -1343
- package/src/tools.js +1370 -1356
- package/src/user-settings.js +230 -230
- package/src/vector-index.js +180 -180
- package/src/version-check.js +122 -122
- package/src/wiki/claims.js +267 -267
- package/src/wiki/cli-hint.js +90 -90
- package/src/wiki/inject.js +164 -164
- package/src/wiki/partition.js +200 -200
- package/src/wiki/plan.js +214 -214
- package/src/wiki/queries.js +640 -640
- package/src/wiki/render.js +261 -261
- package/src/wiki/scan.js +371 -371
- package/src/wiki/t0.js +500 -500
- package/src/wiki/t1-jobs.js +232 -232
- package/src/wiki/t1.js +609 -609
- package/src/write-admission.js +404 -404
- package/test/adaptive-inject-budget.test.js +85 -85
- package/test/api-hardening.test.js +173 -173
- package/test/api-nudge-route.test.js +259 -259
- package/test/api-routes-gaps.test.js +1092 -1092
- package/test/api.test.js +3007 -2998
- package/test/attachment-text.test.js +139 -139
- package/test/audit.test.js +448 -448
- package/test/baseline-persist.test.js +248 -248
- package/test/benchmark.test.js +94 -94
- package/test/bm25-corpus-cache.test.js +145 -145
- package/test/bootstrap.test.js +228 -228
- package/test/candidates.test.js +345 -345
- package/test/capabilities.test.js +577 -577
- package/test/capacity-warning.test.js +262 -262
- package/test/changelog-prep.test.js +125 -125
- package/test/choro-command.test.js +218 -218
- package/test/cli-enums.test.js +37 -37
- package/test/cli-flag-value.test.js +86 -86
- package/test/cli-receive.test.js +390 -390
- package/test/cli-share.test.js +437 -437
- package/test/client-share.test.js +225 -225
- package/test/client.test.js +4595 -4425
- package/test/clustering.test.js +100 -100
- package/test/commands.test.js +106 -106
- package/test/config-keys-routes.test.js +233 -233
- package/test/config.test.js +153 -153
- package/test/conflict-freeze.test.js +344 -344
- package/test/conflict-ledger-enforce.test.js +452 -452
- package/test/conflict-ledger.test.js +242 -242
- package/test/conflict-reviewed.test.js +79 -79
- package/test/content-hash.test.js +204 -204
- package/test/continuity.test.js +452 -452
- package/test/contrast.test.js +432 -432
- package/test/date-hint.test.js +156 -0
- package/test/dedupe-candidate-window.test.js +180 -180
- package/test/dependency-overrides.test.js +78 -78
- package/test/diary-library.test.js +187 -187
- package/test/diary.test.js +576 -576
- package/test/differentiate-feedback.test.js +248 -248
- package/test/distill-tx-embed-gap.test.js +110 -110
- package/test/docs-config-keys.test.js +65 -65
- package/test/document-dir.test.js +233 -233
- package/test/document.test.js +703 -703
- package/test/dream-archive-guardrail.test.js +116 -116
- package/test/dream-baseline-clamp.test.js +174 -174
- package/test/dream-candidate-hybrid.test.js +281 -281
- package/test/dream-coverage-degrade.test.js +86 -86
- package/test/dream-decision-evidence.test.js +314 -314
- package/test/dream-failure-backoff.test.js +173 -173
- package/test/dream-fence.test.js +207 -207
- package/test/dream-maxtokens-fallback.test.js +132 -132
- package/test/dream-merge-guardrail.test.js +219 -219
- package/test/dream-peak-hours.test.js +194 -194
- package/test/dream.test.js +1528 -1528
- package/test/dsh-go-host.test.js +134 -134
- package/test/dsh-go-panel-actions.test.js +854 -854
- package/test/dsh-go-panel.test.js +251 -251
- package/test/embed-ready-poll.test.js +94 -94
- package/test/embed-text-reasons.test.js +112 -112
- package/test/embedding-cache-f32.test.js +276 -276
- package/test/embedding-ready.test.js +226 -226
- package/test/entities.test.js +711 -711
- package/test/entity-recall.test.js +179 -179
- package/test/epistemic.test.js +325 -325
- package/test/failure-query.test.js +147 -147
- package/test/failure-retention.test.js +105 -105
- package/test/fnew-0112.test.js +311 -311
- package/test/fnew-03.test.js +422 -422
- package/test/forget-corrections.test.js +70 -70
- package/test/graph-anchoring.test.js +184 -184
- package/test/graph-api.test.js +333 -333
- package/test/graph-boosted-relations.test.js +189 -189
- package/test/graph-inject-budget.test.js +103 -103
- package/test/graph-multipath.test.js +269 -269
- 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 +86 -86
- 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 +312 -312
- package/test/hot-memory.test.js +259 -259
- package/test/image-description.test.js +271 -0
- package/test/image-memory.test.js +170 -170
- 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 -77
- package/test/inject-parent-gate.test.js +134 -109
- package/test/inject-pin-explicit.test.js +300 -300
- package/test/inject-pin-vector-crowd.test.js +225 -225
- package/test/inject-pools.test.js +195 -195
- package/test/inject-recall.test.js +172 -172
- package/test/inject-rotation.test.js +161 -161
- package/test/inject.test.js +553 -553
- package/test/issue-617-step1-query-and-fusion.test.js +361 -361
- package/test/kb-api-wiring.test.js +128 -128
- package/test/kb-collections.test.js +272 -272
- package/test/kb-routes.test.js +379 -379
- package/test/lang.test.js +98 -98
- package/test/lastrunat-persist.test.js +101 -101
- package/test/lib-smoke.test.js +116 -116
- package/test/llm-audit.test.js +815 -815
- package/test/llm-message-source.test.js +207 -207
- package/test/local-embedder.test.js +292 -292
- package/test/maintenance.test.js +501 -501
- package/test/mcp-stdio.test.js +385 -385
- package/test/mint-source.test.js +311 -311
- 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-tool.test.js +232 -232
- package/test/organize.test.js +418 -418
- package/test/panel-chart-scale.test.js +90 -90
- package/test/panel-tokens.test.js +245 -245
- package/test/peak-hours-observability.test.js +70 -70
- package/test/peer-blockers.test.js +193 -193
- package/test/persona-addressing.test.js +83 -83
- package/test/persona-api.test.js +172 -172
- package/test/persona-by-session.test.js +568 -568
- package/test/persona-flag.test.js +36 -36
- package/test/persona-inject.test.js +145 -145
- package/test/persona-settings.test.js +198 -198
- package/test/pipeline-activities.test.js +155 -155
- package/test/pipeline-attribution.test.js +175 -175
- package/test/pipeline-command.test.js +243 -243
- package/test/pipeline-confirm.test.js +148 -148
- package/test/pipeline-exec.test.js +172 -172
- package/test/pipeline-nudge-auto.test.js +248 -248
- package/test/pipeline-nudge.test.js +1278 -1278
- package/test/pipeline-observations.test.js +157 -157
- package/test/pipeline-observe.test.js +127 -127
- package/test/pipeline-r1r2.test.js +278 -278
- package/test/pipeline-runner.test.js +468 -468
- package/test/pipeline-runs.test.js +767 -767
- package/test/pipeline.test.js +636 -636
- package/test/placement.test.js +36 -36
- package/test/policy-epoch.test.js +259 -259
- package/test/pre-inject-gate.test.js +444 -444
- package/test/profile-extract.test.js +269 -269
- package/test/profile-facts.test.js +492 -492
- package/test/prompt-stance.test.js +128 -128
- package/test/provenance-http.test.js +367 -367
- package/test/publish-gate.test.js +272 -272
- package/test/quality-backfill.test.js +290 -290
- package/test/quality-filter.test.js +295 -295
- package/test/readme-claims.test.js +113 -113
- 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-retention.test.js +128 -128
- package/test/recall-stats.test.js +274 -274
- package/test/receipt-chain.test.js +451 -451
- package/test/reclaim-http.test.js +209 -209
- package/test/reflection-update-gate.test.js +129 -129
- package/test/reflection.test.js +226 -226
- package/test/reindex-backfill.test.js +475 -475
- package/test/reindex-scheduler.test.js +369 -369
- package/test/release-docs.test.js +156 -156
- package/test/release-notes.test.js +265 -265
- package/test/release-workflow.test.js +93 -93
- 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/route-policy.test.js +113 -113
- 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 -319
- package/test/scope-candidates.test.js +491 -491
- package/test/scope-distill-stamp.test.js +134 -134
- package/test/scope-explicit.test.js +452 -452
- package/test/scope-inject-soft.test.js +106 -106
- package/test/scope-optional-service.test.js +110 -110
- package/test/scope-retrieval.test.js +197 -197
- package/test/scope-state.test.js +610 -610
- package/test/scope-storage.test.js +255 -255
- package/test/scope-strict.test.js +367 -367
- package/test/search-fusion.test.js +90 -90
- package/test/security-doc.test.js +188 -188
- package/test/semantic.test.js +124 -124
- package/test/sensitive-scan.test.js +251 -251
- package/test/serve-bin.test.js +121 -121
- package/test/serve.test.js +258 -258
- package/test/service-search.test.js +174 -174
- package/test/service.test.js +410 -410
- package/test/session-gate.test.js +264 -264
- package/test/session-usage.test.js +317 -317
- package/test/settings.test.js +273 -273
- package/test/share-foreign-rows.test.js +144 -144
- package/test/share-http.test.js +144 -144
- package/test/share-import.test.js +577 -577
- package/test/share-imported-guard.test.js +311 -311
- package/test/share-imported-identity.test.js +111 -111
- package/test/share-pack.test.js +511 -511
- package/test/share-receive-http.test.js +756 -756
- package/test/share-routes.test.js +337 -337
- package/test/share-store.test.js +133 -133
- package/test/share-validate.test.js +101 -101
- package/test/skip-invalid-restore.test.js +262 -262
- package/test/sleep-action-set.test.js +441 -441
- package/test/sleep-catchup.test.js +315 -315
- package/test/sleep-failure-backoff.test.js +156 -156
- package/test/sleep-heat.test.js +174 -174
- package/test/sleep-scheduler-rearm.test.js +89 -89
- package/test/sleep.test.js +1455 -1455
- package/test/standalone-api.test.js +1344 -1344
- package/test/store-vector-cache.test.js +98 -98
- package/test/store-write-path-columns.test.js +191 -191
- package/test/store.test.js +744 -744
- package/test/stress.test.js +209 -209
- package/test/summarize-occurred-at.test.js +127 -127
- package/test/summarize-parse-diagnostics.test.js +75 -75
- package/test/summarize-reasoning-effort.test.js +189 -189
- package/test/summarize.test.js +2251 -2251
- package/test/supersede-state-tracking.test.js +394 -394
- package/test/test-count-sync.test.js +71 -71
- package/test/tool-state-ignore.test.js +65 -65
- package/test/tools-dto-schema-matrix.test.js +398 -398
- package/test/tools-memory-get-schema.test.js +65 -65
- package/test/tools.test.js +585 -585
- package/test/updated-at-semantics.test.js +113 -113
- package/test/user-settings-preview.test.js +127 -127
- package/test/user-settings-segments.test.js +527 -527
- package/test/vector-index.test.js +334 -334
- package/test/version-check.test.js +152 -152
- package/test/webServer-optional.test.js +82 -82
- package/test/wiki-api.test.js +734 -734
- package/test/wiki-claims.test.js +194 -194
- package/test/wiki-cli-hint.test.js +118 -118
- package/test/wiki-inject.test.js +265 -265
- package/test/wiki-partition.test.js +112 -112
- package/test/wiki-plan.test.js +80 -80
- package/test/wiki-provenance.test.js +202 -202
- package/test/wiki-queries.test.js +398 -398
- package/test/wiki-scan.test.js +208 -208
- package/test/wiki-store.test.js +190 -190
- package/test/wiki-t0.test.js +456 -456
- package/test/wiki-t1-jobs.test.js +125 -125
- package/test/wiki-t1.test.js +460 -460
- package/test/wiki-tools.test.js +176 -176
- package/test/workflow-guards.test.js +155 -155
- package/test/write-admission.test.js +668 -668
|
@@ -1,225 +1,225 @@
|
|
|
1
|
-
// 车道 D 的面板静态锁。为什么是静态锁:lib/client.js 由宿主 __ModuleLoader__ 加载,
|
|
2
|
-
// 本仓库没有可跑的宿主壳,渲染测试要拖进一份 React 测试栈才够得着那几行真代码。
|
|
3
|
-
// 锁的是「形状」而不是「长得像」:每一条都对应一个**会伤人的回归形态**,
|
|
4
|
-
// 注释里写清它防的是哪一种(AGENTS.md 纪律 8 对测试同样适用)。
|
|
5
|
-
import test from "node:test";
|
|
6
|
-
import assert from "node:assert/strict";
|
|
7
|
-
import { readFileSync } from "node:fs";
|
|
8
|
-
import { dirname, join } from "node:path";
|
|
9
|
-
import { fileURLToPath } from "node:url";
|
|
10
|
-
|
|
11
|
-
const here = dirname(fileURLToPath(import.meta.url));
|
|
12
|
-
const clientSource = readFileSync(join(here, "..", "lib/client.js"), "utf8");
|
|
13
|
-
const shareSource = readFileSync(join(here, "..", "src/share.js"), "utf8");
|
|
14
|
-
|
|
15
|
-
// 分享弹层最要紧的一条:**看见 report 之前不给下载**(spec §6 的支点)。
|
|
16
|
-
// 防的回归形态:有人为了「方便」把下载按钮改成常驻,或从别处(如列表工具栏)再开一条
|
|
17
|
-
// 直接落盘的口子——那一步停顿就没了,随手全库外发重新变成一次点击。
|
|
18
|
-
test("the share dialog gates the download on the report", () => {
|
|
19
|
-
assert.ok(clientSource.includes('apiFetch("/api/dsh-mneme/share"'),
|
|
20
|
-
"面板必须发这一枪(不许另造 fetch 绕过 apiFetch 的 token 注入)");
|
|
21
|
-
assert.ok(clientSource.includes("const canDownload = !!rep && rep.included > 0;"),
|
|
22
|
-
"下载按钮的唯一门:有报告且有内容。included 为 0 时对面吃的是 empty-pack(400),不该给一个必败的文件");
|
|
23
|
-
assert.ok(clientSource.includes("canDownload && h(\"div\""),
|
|
24
|
-
"下载那一行必须挂在 canDownload 上,而不是常驻(按钮本身在这样一个 div 里)");
|
|
25
|
-
// 413 与 200 是两份形状:413 没有 memories,只有 report + 字节数 + hint。
|
|
26
|
-
assert.ok(clientSource.includes("if (res.status === 413)"),
|
|
27
|
-
"超水位要走 tooLarge 那一支,不能落到通用失败");
|
|
28
|
-
assert.ok(clientSource.includes("new Blob([JSON.stringify(pack)]"),
|
|
29
|
-
"落盘的必须是服务端给的那一份字节:重拼字段或 pretty-print 都会让「量过的包」与「发出去的包」不是同一坨");
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
// docs/SHARE.md「车道 D 三笔账」之一:included 只数实际发出的条数,够格进包的是
|
|
33
|
-
// included + deduped。防的回归:有人把「合格条数」的标签挪到 included 上,读数是假的。
|
|
34
|
-
test("the report separates what shipped from what qualified", () => {
|
|
35
|
-
assert.ok(clientSource.includes("report.included + report.deduped"),
|
|
36
|
-
"折叠掉的那几条必须加回去,两个数同屏");
|
|
37
|
-
assert.ok(clientSource.includes('t("memory.explorer.shareDeduped")'),
|
|
38
|
-
"deduped 那一行要真的有渲染点,不是只在字典里躺着");
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
// 跨文件锁:面板的六个跳过原因与八个可分享类型都只许是服务端那份的投影。
|
|
42
|
-
// 防的回归:share.js 加/改一个 reason 键或一类可分享类型,面板静默少一格
|
|
43
|
-
// ——她的报告上就有一类跳过是看不见的。
|
|
44
|
-
test("the panel's skip-reason and type sets mirror src/share.js", () => {
|
|
45
|
-
const serverReasons = [...shareSource.matchAll(/reason:\s*"([a-z_]+)"/g)].map((m) => m[1]);
|
|
46
|
-
const uniqueReasons = [...new Set(serverReasons)];
|
|
47
|
-
const panelReasons = /const SHARE_SKIP_REASONS = \[([^\]]*)\]/.exec(clientSource)[1]
|
|
48
|
-
.split(",").map((s) => s.trim().replace(/"/g, "")).filter(Boolean);
|
|
49
|
-
assert.deepEqual(panelReasons, uniqueReasons,
|
|
50
|
-
"跳过原因集必须与 share.js 的判序一致(顺序也一致:报告按判序展示)");
|
|
51
|
-
|
|
52
|
-
const serverTypes = /SHARE_ALLOWED_TYPES = new Set\(\[([^\]]*)\]/.exec(shareSource)[1]
|
|
53
|
-
.split(",").map((s) => s.trim().replace(/"/g, "")).filter(Boolean);
|
|
54
|
-
const panelTypes = /const SHARE_TYPE_OPTIONS = \[([^\]]*)\]/.exec(clientSource)[1]
|
|
55
|
-
.split(",").map((s) => s.trim().replace(/"/g, "")).filter(Boolean);
|
|
56
|
-
assert.deepEqual(panelTypes.slice().sort(), serverTypes.slice().sort(),
|
|
57
|
-
"类型下拉只能列真能进包的那几类");
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
// 别名的尺:服务端 importedTagFor 把整串(含 from-)截到 IMPORTED_TAG_MAX。
|
|
61
|
-
// 防的回归:服务端改这个常量而面板还拦着旧长度,她打的字在对面被静默吃掉。
|
|
62
|
-
test("the alias input matches the server's tag budget", () => {
|
|
63
|
-
const max = Number(/IMPORTED_TAG_MAX = (\d+)/.exec(shareSource)[1]);
|
|
64
|
-
assert.ok(clientSource.includes(`maxLength: ${max - "from-".length}`),
|
|
65
|
-
"输入框长度必须等于服务端留给别名的字符数");
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
// 文件名以服务端 Content-Disposition 为准(拼法只住在 src/share-http.js 的 shareFilename)。
|
|
69
|
-
// 防的回归:面板自己抄一份「日期 + pack_id 前 8 位」的拼法,两处日后分叉。
|
|
70
|
-
test("the pack filename comes from the response header, with a pack_id fallback", () => {
|
|
71
|
-
assert.ok(clientSource.includes('res.headers.get("content-disposition")'));
|
|
72
|
-
assert.ok(clientSource.includes("const dispositionName = (value) =>"));
|
|
73
|
-
// 回落那一格只许出现一次前缀串,且不带日期拼法。
|
|
74
|
-
assert.equal(clientSource.split("dsh-mneme-share-").length - 1, 1,
|
|
75
|
-
"面板只许有一份包名前缀串(回落用),不许再拼第二套日期命名");
|
|
76
|
-
assert.equal(/pack_id[^;\n]{0,48}slice\(\s*0\s*,\s*8\s*\)/.test(clientSource), false,
|
|
77
|
-
"不许在 pack_id 后面截 8 位——那是 src/share-http.js 的 shareFilename 唯一的拼法");
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
// docs/SHARE.md「车道 D 三笔账」之二:干跑与真导入的答复同形状,区别只有
|
|
81
|
-
// withDryRun 加的 dry_run 那一格。防的回归形态:界面按「我有没有发 confirm」推断写了
|
|
82
|
-
// 没有——同一件事写两处,改一处就出现「其实没写、却说写了」。
|
|
83
|
-
test("the receive dialog reads dry_run instead of inferring from its own button", () => {
|
|
84
|
-
assert.ok(clientSource.includes('apiFetch("/api/dsh-mneme/receive"'),
|
|
85
|
-
"入库只许走这一枪(apiFetch 带 token 注入)");
|
|
86
|
-
assert.ok(clientSource.includes("post({ pack })"), "预览那一发不带 confirm——缺省即干跑");
|
|
87
|
-
assert.ok(clientSource.includes("post({ pack, confirm: true })"),
|
|
88
|
-
"真写的那一发必须带 confirm 这个真值,其他形状服务端都不认");
|
|
89
|
-
assert.ok(clientSource.includes('d.dry_run === false ? "memory.explorer.receiveDidCreate"'),
|
|
90
|
-
"「写进去了」这句只能由答复自己说——钉的是那个三元式本身,不是六个字面量都在");
|
|
91
|
-
assert.ok(clientSource.includes("result.dry_run !== false"),
|
|
92
|
-
"确认之后仍回干跑时要单独说「没有写库」,不许沉默,也不许挂在预览那一支上");
|
|
93
|
-
assert.ok(clientSource.includes('"memory.explorer.receiveStillDry"'),
|
|
94
|
-
"那一格要有自己的文案");
|
|
95
|
-
// 第二个旋钮一概不认:收侧只认 confirm(spec §5——reclaim 的 body 里就带 dryRun,
|
|
96
|
-
// 手熟的人会顺手发来,把它读成「要写」等于替她点头)。
|
|
97
|
-
// **切片而不是全文**:本文件里 reclaim/VACUUM 那一发合法地发 `dryRun: false, confirm: true`,
|
|
98
|
-
// 那是 reclaim 的形状不是 receive 的。全文反锁会一上来就红;谁把它「修」成全文锁,
|
|
99
|
-
// 就等于给 reclaim 那颗键定罪。所以按函数名切区段,并且**两个锚点都要验**:
|
|
100
|
-
// 结束锚点丢了的时候 indexOf 返回 -1,切片会一路长到文件尾而不是塌成空——
|
|
101
|
-
// 那时候这条锁看似更严,实际悄悄开始管整个文件(而 ReceiveDialog 之后今天没有 dryRun,
|
|
102
|
-
// 它会空过)。两头都钉住,锚点漂移只会红得明白。
|
|
103
|
-
const receiveStart = clientSource.indexOf("function ReceiveDialog(");
|
|
104
|
-
const receiveEnd = clientSource.indexOf("// #386 第 5 条", receiveStart);
|
|
105
|
-
assert.ok(receiveStart > 0 && receiveEnd > receiveStart,
|
|
106
|
-
"ReceiveDialog 的起止锚点都要在,且顺序对——否则这条锁在管的东西和它说的不是一件");
|
|
107
|
-
const receiveRegion = clientSource.slice(receiveStart, receiveEnd);
|
|
108
|
-
assert.ok(receiveRegion.length > 800, "ReceiveDialog 的区段短得不像话,先修锚再谈锁");
|
|
109
|
-
assert.equal(receiveRegion.includes("dryRun"), false,
|
|
110
|
-
"收侧不许发 dryRun——唯一的入口是 confirm");
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
// 三笔账之三:title_conflicts 是 advisory 读数,只对「本轮真正会新建的那些」判过,
|
|
114
|
-
// 重吃同一个包时恒空。防的回归:文案写成「现在没有冲突」。
|
|
115
|
-
test("the receive preview keeps the advisory reading honest", () => {
|
|
116
|
-
assert.ok(clientSource.includes("d.title_conflicts"), "撞名清单要显示");
|
|
117
|
-
assert.ok(clientSource.includes('"memory.explorer.receiveConflicts"'));
|
|
118
|
-
// 钉的是**文案**而不是注释:解释性的话必须能写「这不等于没有冲突」来提醒读者,
|
|
119
|
-
// 连注释一起 ban 等于把警告本身禁掉(这一轮就撞上了:新注释里的「没有撞名」先红的是这条锁)。
|
|
120
|
-
// 真正会伤人的是屏上那一句,所以只扫非注释行。
|
|
121
|
-
const copyLines = clientSource.split("\n").filter((line) => !/^\s*\/\//.test(line)).join("\n");
|
|
122
|
-
assert.equal(/现在没有冲突|没有撞名|无冲突|no conflicts|no title clashes/i.test(copyLines), false,
|
|
123
|
-
"不许出现「没有冲突」这类绝对断言(清单为空只说明本轮没有新的要报)");
|
|
124
|
-
// 先预览才允许确认:干跑是必经的一站,不是可选装饰。
|
|
125
|
-
assert.ok(clientSource.includes("disabled: busy || !preview"),
|
|
126
|
-
"确认按钮在拿到预览之前必须 disabled");
|
|
127
|
-
// advisory 读数的分母要说出来,否则「清单为空」与「什么也没比」在屏上长一个样。
|
|
128
|
-
// 注意它是**包内条数**而不是「比过了几条」,所以文案只许说「包内 N 条」。
|
|
129
|
-
assert.ok(clientSource.includes("d.conflicts_checked"),
|
|
130
|
-
"要把服务端给的包内条数显示出来");
|
|
131
|
-
assert.ok(clientSource.includes('t("memory.explorer.receivePackCount")'),
|
|
132
|
-
"那一行要有自己的渲染点,不是只在字典里躺着");
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
// D3 评审裁定补的锁:两种「按了没反应」的形态。
|
|
136
|
-
// a) 200 但正文读不出——曾经一路 return null,调用方把它读成「屏上已有提示」,
|
|
137
|
-
// 于是确认那一发悄悄没走完,而上一块的「将新建 N 条」还留在屏上;
|
|
138
|
-
// b) 预览还在飞的时候又能选一个文件——飞回来的数字说的是前一个包,确认写的却是后一个。
|
|
139
|
-
test("the receive dialog never goes silent, and never lets a superseded preview drive the write", () => {
|
|
140
|
-
assert.ok(clientSource.includes('if (!d || typeof d !== "object") {'),
|
|
141
|
-
"200 但没有可读正文也要 setErr,不许 return null 装成「已经处理过了」");
|
|
142
|
-
assert.ok(clientSource.includes("disabled: busy, onClick: () => fileRef.current?.click()"),
|
|
143
|
-
"预览/确认在飞的时候不许再选文件(那一块数字属于上一个包)");
|
|
144
|
-
assert.ok(clientSource.includes('typeof parsed !== "object"'),
|
|
145
|
-
"解得出 JSON 但不是对象,是另一种失败,得有它自己的一句话");
|
|
146
|
-
assert.ok(clientSource.includes('e.target.value = ""'),
|
|
147
|
-
"选完清空控件值:否则改过的同一个文件再选一次不发 change,面板拿着旧包预览");
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
// 外来区三件:筛选、来源徽标、整包退出。锁的是「每件事都挂在一个服务端真值上」,
|
|
151
|
-
// 而不是挂在一个界面自己造出来的判断上。
|
|
152
|
-
test("the foreign filter is a browse-state server filter and stays inert while searching", () => {
|
|
153
|
-
assert.ok(clientSource.includes('foreignOnly ? "&source=imported" : ""'),
|
|
154
|
-
"浏览态交给服务端筛:分页与 total 同口径,本地筛会切掉尾巴");
|
|
155
|
-
// **搜索态不做本地过滤**(本轮扫描改掉的裁定,不是漏做):/search 那张 DTO 的 source 是
|
|
156
|
-
// 召回渠道(keyword/vector/bm25,src/service.js:939),行的 provenance 被盖掉,「这条是不是
|
|
157
|
-
// 外来的」在这张 DTO 上压根读不出来。宁可让 chip 在搜索态明确失效并说清原因,也不要按一个
|
|
158
|
-
// 假信号筛——test/share-foreign-rows.test.js 的第三条用例锁着这个事实。
|
|
159
|
-
assert.ok(clientSource.includes("disabled: !!query.trim() && !foreignOnly"),
|
|
160
|
-
"有搜索词时不许**开启**外来筛选,但任何时候都许关掉——只给 disabled 会把她锁在一个她解不开的状态里");
|
|
161
|
-
assert.ok(clientSource.includes('"memory.explorer.filterForeignSearchHint"'),
|
|
162
|
-
"失效要有自己的文案,不能只靠 disabled 让人猜");
|
|
163
|
-
assert.equal(clientSource.includes('(!foreignOnly || m.source'), false,
|
|
164
|
-
"不许退回「搜索态本地按 source 筛」那条假信号");
|
|
165
|
-
// 服务端不吃 source 这件事,不许用「给 search 的 URL 也拼一个键」的方式绕过去:
|
|
166
|
-
// 那条路由压根不读它(src/api.js 的 search handler 里没有 source 这一档),拼上去
|
|
167
|
-
// 只会造出一个「看起来筛过了、其实没有」的形状。全文件只许有一处拼这个参数。
|
|
168
|
-
assert.equal(clientSource.split("&source=imported").length - 1, 1,
|
|
169
|
-
"只有浏览态那一条 URL 会拼 source=imported");
|
|
170
|
-
});
|
|
171
|
-
|
|
172
|
-
// 来源徽标与撤销都按服务端给的真值门控:徽标认 source + from- 标签,撤销认 pack_id。
|
|
173
|
-
test("the foreign badge and the withdraw action gate on server truth, not on guesswork", () => {
|
|
174
|
-
assert.ok(clientSource.includes('m.source === "imported"'),
|
|
175
|
-
"徽标先认 source,再认标签:人手工打的 from-* 标签不算外来行");
|
|
176
|
-
assert.ok(clientSource.includes('startsWith("from-")'),
|
|
177
|
-
"别名读数就是 importedTagFor 打的那条标签,面板不另找地方读别名");
|
|
178
|
-
assert.ok(clientSource.includes("memory.share_pack_id &&"),
|
|
179
|
-
"撤销按钮按包号自门控:/search 的 DTO 不带这一格,那种行按不了(已知代价,docs/SHARE.md 记着)");
|
|
180
|
-
assert.ok(clientSource.includes('apiFetch("/api/dsh-mneme/receive/undo"'),
|
|
181
|
-
"整包退出只许走这一枪");
|
|
182
|
-
assert.ok(clientSource.includes("{ packId: memory.share_pack_id, confirm: true }"),
|
|
183
|
-
"删除那一发要带 confirm 真值,且 packId 只从行上取");
|
|
184
|
-
// 两段式的**第一段**也要有锁:只钉 confirm 那一发等于没钉——把三元式删掉、每次都带
|
|
185
|
-
// confirm:true,十把锁一把都不会红,而「整包物删」变成一次点击就发生。
|
|
186
|
-
assert.ok(clientSource.includes(": { packId: memory.share_pack_id })"),
|
|
187
|
-
"清点那一发必须是不带 confirm 的形状");
|
|
188
|
-
assert.ok(clientSource.includes("onClick: () => undoPack(false)"),
|
|
189
|
-
"第一颗键走的是干跑那一支");
|
|
190
|
-
assert.ok(clientSource.includes("d.dry_run === false"),
|
|
191
|
-
"撤成功的读数同样只由答复的 dry_run 说");
|
|
192
|
-
assert.ok(clientSource.includes("undoPreview.sample"),
|
|
193
|
-
"删除前的确认要带前缀样例(答复的 sample 最多 20 条,deleted 才是全量)");
|
|
194
|
-
// 撤是**物删**(docs/SHARE.md:归档行会占住 UNIQUE 位,撤了导不回来),文案不许写「归档」。
|
|
195
|
-
assert.equal(/memory\.explorer\.undoPack[A-Za-z]*"\s*:\s*"[^"]*(归档|archive)/i.test(clientSource), false,
|
|
196
|
-
"撤销的文案不许出现「归档」——它是物理删除");
|
|
197
|
-
// 删除那一发的成功判据**只能是答复的 dry_run**。`confirm && d.dry_run === false` 那种合取
|
|
198
|
-
// 读的是「我刚才点的是哪一颗」:确认回来一个 dry_run:true 时会把预览重画一遍当作没发生——
|
|
199
|
-
// 接收弹层那一侧修过同一个形状,撤销这条更硬的路不许退回去。
|
|
200
|
-
assert.ok(clientSource.includes("if (d.dry_run === false) onUndone(d.deleted ?? 0)"),
|
|
201
|
-
"撤销的成功判据只看答复");
|
|
202
|
-
assert.equal(clientSource.includes("confirm && d.dry_run === false"), false,
|
|
203
|
-
"不许再出现按按钮形状推断的那种合取");
|
|
204
|
-
assert.ok(clientSource.includes('t("memory.explorer.undoPackGone")'),
|
|
205
|
-
"包已不在本库时要说那一句,而不是给一颗「确认删除 0 条」的键");
|
|
206
|
-
// 同一形状只许有一份:抽屉的退出清单与接收弹层的撞名清单都是 {type,title} 两列。
|
|
207
|
-
assert.ok(clientSource.includes("TypeRowList({ t, rows: undoPreview.sample })"),
|
|
208
|
-
"清单形状走公共组件,不许第三份手写 .map");
|
|
209
|
-
assert.ok(clientSource.includes('foreignOnly && !!query.trim() && h("div"'),
|
|
210
|
-
"「搜索时不按」要有一行看得见的说明,不能只挂在 disabled 的 title 上");
|
|
211
|
-
});
|
|
212
|
-
|
|
213
|
-
// 外来判据是这条车道唯一一处「面板自己写的字面量」:lib/client.js 是独立 bundle,
|
|
214
|
-
// 不能 import src/memory-source.js,所以用静态镜像把它钉回服务端那一份。
|
|
215
|
-
// 服务端改了那个值而这里没跟,面板的徽标与筛选会安静地筛不到东西——正是最难发现的一种坏。
|
|
216
|
-
test("the panel's foreign-ness literal mirrors the server's single definition", () => {
|
|
217
|
-
const sourceModule = readFileSync(join(here, "..", "src/memory-source.js"), "utf8");
|
|
218
|
-
const imported = /IMPORTED_SOURCE = "([^"]+)"/.exec(sourceModule);
|
|
219
|
-
assert.ok(imported, "src/memory-source.js 的导出形状变了,先修这条锁的锚");
|
|
220
|
-
const value = imported[1];
|
|
221
|
-
assert.ok(clientSource.includes(`m.source === "${value}"`),
|
|
222
|
-
"徽标读的行来源必须与服务端定义的那个值同字面");
|
|
223
|
-
assert.ok(clientSource.includes(`&source=${value}`),
|
|
224
|
-
"浏览态那条 URL 拼的来源值必须是同一个");
|
|
225
|
-
});
|
|
1
|
+
// 车道 D 的面板静态锁。为什么是静态锁:lib/client.js 由宿主 __ModuleLoader__ 加载,
|
|
2
|
+
// 本仓库没有可跑的宿主壳,渲染测试要拖进一份 React 测试栈才够得着那几行真代码。
|
|
3
|
+
// 锁的是「形状」而不是「长得像」:每一条都对应一个**会伤人的回归形态**,
|
|
4
|
+
// 注释里写清它防的是哪一种(AGENTS.md 纪律 8 对测试同样适用)。
|
|
5
|
+
import test from "node:test";
|
|
6
|
+
import assert from "node:assert/strict";
|
|
7
|
+
import { readFileSync } from "node:fs";
|
|
8
|
+
import { dirname, join } from "node:path";
|
|
9
|
+
import { fileURLToPath } from "node:url";
|
|
10
|
+
|
|
11
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
12
|
+
const clientSource = readFileSync(join(here, "..", "lib/client.js"), "utf8");
|
|
13
|
+
const shareSource = readFileSync(join(here, "..", "src/share.js"), "utf8");
|
|
14
|
+
|
|
15
|
+
// 分享弹层最要紧的一条:**看见 report 之前不给下载**(spec §6 的支点)。
|
|
16
|
+
// 防的回归形态:有人为了「方便」把下载按钮改成常驻,或从别处(如列表工具栏)再开一条
|
|
17
|
+
// 直接落盘的口子——那一步停顿就没了,随手全库外发重新变成一次点击。
|
|
18
|
+
test("the share dialog gates the download on the report", () => {
|
|
19
|
+
assert.ok(clientSource.includes('apiFetch("/api/dsh-mneme/share"'),
|
|
20
|
+
"面板必须发这一枪(不许另造 fetch 绕过 apiFetch 的 token 注入)");
|
|
21
|
+
assert.ok(clientSource.includes("const canDownload = !!rep && rep.included > 0;"),
|
|
22
|
+
"下载按钮的唯一门:有报告且有内容。included 为 0 时对面吃的是 empty-pack(400),不该给一个必败的文件");
|
|
23
|
+
assert.ok(clientSource.includes("canDownload && h(\"div\""),
|
|
24
|
+
"下载那一行必须挂在 canDownload 上,而不是常驻(按钮本身在这样一个 div 里)");
|
|
25
|
+
// 413 与 200 是两份形状:413 没有 memories,只有 report + 字节数 + hint。
|
|
26
|
+
assert.ok(clientSource.includes("if (res.status === 413)"),
|
|
27
|
+
"超水位要走 tooLarge 那一支,不能落到通用失败");
|
|
28
|
+
assert.ok(clientSource.includes("new Blob([JSON.stringify(pack)]"),
|
|
29
|
+
"落盘的必须是服务端给的那一份字节:重拼字段或 pretty-print 都会让「量过的包」与「发出去的包」不是同一坨");
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
// docs/SHARE.md「车道 D 三笔账」之一:included 只数实际发出的条数,够格进包的是
|
|
33
|
+
// included + deduped。防的回归:有人把「合格条数」的标签挪到 included 上,读数是假的。
|
|
34
|
+
test("the report separates what shipped from what qualified", () => {
|
|
35
|
+
assert.ok(clientSource.includes("report.included + report.deduped"),
|
|
36
|
+
"折叠掉的那几条必须加回去,两个数同屏");
|
|
37
|
+
assert.ok(clientSource.includes('t("memory.explorer.shareDeduped")'),
|
|
38
|
+
"deduped 那一行要真的有渲染点,不是只在字典里躺着");
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
// 跨文件锁:面板的六个跳过原因与八个可分享类型都只许是服务端那份的投影。
|
|
42
|
+
// 防的回归:share.js 加/改一个 reason 键或一类可分享类型,面板静默少一格
|
|
43
|
+
// ——她的报告上就有一类跳过是看不见的。
|
|
44
|
+
test("the panel's skip-reason and type sets mirror src/share.js", () => {
|
|
45
|
+
const serverReasons = [...shareSource.matchAll(/reason:\s*"([a-z_]+)"/g)].map((m) => m[1]);
|
|
46
|
+
const uniqueReasons = [...new Set(serverReasons)];
|
|
47
|
+
const panelReasons = /const SHARE_SKIP_REASONS = \[([^\]]*)\]/.exec(clientSource)[1]
|
|
48
|
+
.split(",").map((s) => s.trim().replace(/"/g, "")).filter(Boolean);
|
|
49
|
+
assert.deepEqual(panelReasons, uniqueReasons,
|
|
50
|
+
"跳过原因集必须与 share.js 的判序一致(顺序也一致:报告按判序展示)");
|
|
51
|
+
|
|
52
|
+
const serverTypes = /SHARE_ALLOWED_TYPES = new Set\(\[([^\]]*)\]/.exec(shareSource)[1]
|
|
53
|
+
.split(",").map((s) => s.trim().replace(/"/g, "")).filter(Boolean);
|
|
54
|
+
const panelTypes = /const SHARE_TYPE_OPTIONS = \[([^\]]*)\]/.exec(clientSource)[1]
|
|
55
|
+
.split(",").map((s) => s.trim().replace(/"/g, "")).filter(Boolean);
|
|
56
|
+
assert.deepEqual(panelTypes.slice().sort(), serverTypes.slice().sort(),
|
|
57
|
+
"类型下拉只能列真能进包的那几类");
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
// 别名的尺:服务端 importedTagFor 把整串(含 from-)截到 IMPORTED_TAG_MAX。
|
|
61
|
+
// 防的回归:服务端改这个常量而面板还拦着旧长度,她打的字在对面被静默吃掉。
|
|
62
|
+
test("the alias input matches the server's tag budget", () => {
|
|
63
|
+
const max = Number(/IMPORTED_TAG_MAX = (\d+)/.exec(shareSource)[1]);
|
|
64
|
+
assert.ok(clientSource.includes(`maxLength: ${max - "from-".length}`),
|
|
65
|
+
"输入框长度必须等于服务端留给别名的字符数");
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
// 文件名以服务端 Content-Disposition 为准(拼法只住在 src/share-http.js 的 shareFilename)。
|
|
69
|
+
// 防的回归:面板自己抄一份「日期 + pack_id 前 8 位」的拼法,两处日后分叉。
|
|
70
|
+
test("the pack filename comes from the response header, with a pack_id fallback", () => {
|
|
71
|
+
assert.ok(clientSource.includes('res.headers.get("content-disposition")'));
|
|
72
|
+
assert.ok(clientSource.includes("const dispositionName = (value) =>"));
|
|
73
|
+
// 回落那一格只许出现一次前缀串,且不带日期拼法。
|
|
74
|
+
assert.equal(clientSource.split("dsh-mneme-share-").length - 1, 1,
|
|
75
|
+
"面板只许有一份包名前缀串(回落用),不许再拼第二套日期命名");
|
|
76
|
+
assert.equal(/pack_id[^;\n]{0,48}slice\(\s*0\s*,\s*8\s*\)/.test(clientSource), false,
|
|
77
|
+
"不许在 pack_id 后面截 8 位——那是 src/share-http.js 的 shareFilename 唯一的拼法");
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
// docs/SHARE.md「车道 D 三笔账」之二:干跑与真导入的答复同形状,区别只有
|
|
81
|
+
// withDryRun 加的 dry_run 那一格。防的回归形态:界面按「我有没有发 confirm」推断写了
|
|
82
|
+
// 没有——同一件事写两处,改一处就出现「其实没写、却说写了」。
|
|
83
|
+
test("the receive dialog reads dry_run instead of inferring from its own button", () => {
|
|
84
|
+
assert.ok(clientSource.includes('apiFetch("/api/dsh-mneme/receive"'),
|
|
85
|
+
"入库只许走这一枪(apiFetch 带 token 注入)");
|
|
86
|
+
assert.ok(clientSource.includes("post({ pack })"), "预览那一发不带 confirm——缺省即干跑");
|
|
87
|
+
assert.ok(clientSource.includes("post({ pack, confirm: true })"),
|
|
88
|
+
"真写的那一发必须带 confirm 这个真值,其他形状服务端都不认");
|
|
89
|
+
assert.ok(clientSource.includes('d.dry_run === false ? "memory.explorer.receiveDidCreate"'),
|
|
90
|
+
"「写进去了」这句只能由答复自己说——钉的是那个三元式本身,不是六个字面量都在");
|
|
91
|
+
assert.ok(clientSource.includes("result.dry_run !== false"),
|
|
92
|
+
"确认之后仍回干跑时要单独说「没有写库」,不许沉默,也不许挂在预览那一支上");
|
|
93
|
+
assert.ok(clientSource.includes('"memory.explorer.receiveStillDry"'),
|
|
94
|
+
"那一格要有自己的文案");
|
|
95
|
+
// 第二个旋钮一概不认:收侧只认 confirm(spec §5——reclaim 的 body 里就带 dryRun,
|
|
96
|
+
// 手熟的人会顺手发来,把它读成「要写」等于替她点头)。
|
|
97
|
+
// **切片而不是全文**:本文件里 reclaim/VACUUM 那一发合法地发 `dryRun: false, confirm: true`,
|
|
98
|
+
// 那是 reclaim 的形状不是 receive 的。全文反锁会一上来就红;谁把它「修」成全文锁,
|
|
99
|
+
// 就等于给 reclaim 那颗键定罪。所以按函数名切区段,并且**两个锚点都要验**:
|
|
100
|
+
// 结束锚点丢了的时候 indexOf 返回 -1,切片会一路长到文件尾而不是塌成空——
|
|
101
|
+
// 那时候这条锁看似更严,实际悄悄开始管整个文件(而 ReceiveDialog 之后今天没有 dryRun,
|
|
102
|
+
// 它会空过)。两头都钉住,锚点漂移只会红得明白。
|
|
103
|
+
const receiveStart = clientSource.indexOf("function ReceiveDialog(");
|
|
104
|
+
const receiveEnd = clientSource.indexOf("// #386 第 5 条", receiveStart);
|
|
105
|
+
assert.ok(receiveStart > 0 && receiveEnd > receiveStart,
|
|
106
|
+
"ReceiveDialog 的起止锚点都要在,且顺序对——否则这条锁在管的东西和它说的不是一件");
|
|
107
|
+
const receiveRegion = clientSource.slice(receiveStart, receiveEnd);
|
|
108
|
+
assert.ok(receiveRegion.length > 800, "ReceiveDialog 的区段短得不像话,先修锚再谈锁");
|
|
109
|
+
assert.equal(receiveRegion.includes("dryRun"), false,
|
|
110
|
+
"收侧不许发 dryRun——唯一的入口是 confirm");
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
// 三笔账之三:title_conflicts 是 advisory 读数,只对「本轮真正会新建的那些」判过,
|
|
114
|
+
// 重吃同一个包时恒空。防的回归:文案写成「现在没有冲突」。
|
|
115
|
+
test("the receive preview keeps the advisory reading honest", () => {
|
|
116
|
+
assert.ok(clientSource.includes("d.title_conflicts"), "撞名清单要显示");
|
|
117
|
+
assert.ok(clientSource.includes('"memory.explorer.receiveConflicts"'));
|
|
118
|
+
// 钉的是**文案**而不是注释:解释性的话必须能写「这不等于没有冲突」来提醒读者,
|
|
119
|
+
// 连注释一起 ban 等于把警告本身禁掉(这一轮就撞上了:新注释里的「没有撞名」先红的是这条锁)。
|
|
120
|
+
// 真正会伤人的是屏上那一句,所以只扫非注释行。
|
|
121
|
+
const copyLines = clientSource.split("\n").filter((line) => !/^\s*\/\//.test(line)).join("\n");
|
|
122
|
+
assert.equal(/现在没有冲突|没有撞名|无冲突|no conflicts|no title clashes/i.test(copyLines), false,
|
|
123
|
+
"不许出现「没有冲突」这类绝对断言(清单为空只说明本轮没有新的要报)");
|
|
124
|
+
// 先预览才允许确认:干跑是必经的一站,不是可选装饰。
|
|
125
|
+
assert.ok(clientSource.includes("disabled: busy || !preview"),
|
|
126
|
+
"确认按钮在拿到预览之前必须 disabled");
|
|
127
|
+
// advisory 读数的分母要说出来,否则「清单为空」与「什么也没比」在屏上长一个样。
|
|
128
|
+
// 注意它是**包内条数**而不是「比过了几条」,所以文案只许说「包内 N 条」。
|
|
129
|
+
assert.ok(clientSource.includes("d.conflicts_checked"),
|
|
130
|
+
"要把服务端给的包内条数显示出来");
|
|
131
|
+
assert.ok(clientSource.includes('t("memory.explorer.receivePackCount")'),
|
|
132
|
+
"那一行要有自己的渲染点,不是只在字典里躺着");
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
// D3 评审裁定补的锁:两种「按了没反应」的形态。
|
|
136
|
+
// a) 200 但正文读不出——曾经一路 return null,调用方把它读成「屏上已有提示」,
|
|
137
|
+
// 于是确认那一发悄悄没走完,而上一块的「将新建 N 条」还留在屏上;
|
|
138
|
+
// b) 预览还在飞的时候又能选一个文件——飞回来的数字说的是前一个包,确认写的却是后一个。
|
|
139
|
+
test("the receive dialog never goes silent, and never lets a superseded preview drive the write", () => {
|
|
140
|
+
assert.ok(clientSource.includes('if (!d || typeof d !== "object") {'),
|
|
141
|
+
"200 但没有可读正文也要 setErr,不许 return null 装成「已经处理过了」");
|
|
142
|
+
assert.ok(clientSource.includes("disabled: busy, onClick: () => fileRef.current?.click()"),
|
|
143
|
+
"预览/确认在飞的时候不许再选文件(那一块数字属于上一个包)");
|
|
144
|
+
assert.ok(clientSource.includes('typeof parsed !== "object"'),
|
|
145
|
+
"解得出 JSON 但不是对象,是另一种失败,得有它自己的一句话");
|
|
146
|
+
assert.ok(clientSource.includes('e.target.value = ""'),
|
|
147
|
+
"选完清空控件值:否则改过的同一个文件再选一次不发 change,面板拿着旧包预览");
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
// 外来区三件:筛选、来源徽标、整包退出。锁的是「每件事都挂在一个服务端真值上」,
|
|
151
|
+
// 而不是挂在一个界面自己造出来的判断上。
|
|
152
|
+
test("the foreign filter is a browse-state server filter and stays inert while searching", () => {
|
|
153
|
+
assert.ok(clientSource.includes('foreignOnly ? "&source=imported" : ""'),
|
|
154
|
+
"浏览态交给服务端筛:分页与 total 同口径,本地筛会切掉尾巴");
|
|
155
|
+
// **搜索态不做本地过滤**(本轮扫描改掉的裁定,不是漏做):/search 那张 DTO 的 source 是
|
|
156
|
+
// 召回渠道(keyword/vector/bm25,src/service.js:939),行的 provenance 被盖掉,「这条是不是
|
|
157
|
+
// 外来的」在这张 DTO 上压根读不出来。宁可让 chip 在搜索态明确失效并说清原因,也不要按一个
|
|
158
|
+
// 假信号筛——test/share-foreign-rows.test.js 的第三条用例锁着这个事实。
|
|
159
|
+
assert.ok(clientSource.includes("disabled: !!query.trim() && !foreignOnly"),
|
|
160
|
+
"有搜索词时不许**开启**外来筛选,但任何时候都许关掉——只给 disabled 会把她锁在一个她解不开的状态里");
|
|
161
|
+
assert.ok(clientSource.includes('"memory.explorer.filterForeignSearchHint"'),
|
|
162
|
+
"失效要有自己的文案,不能只靠 disabled 让人猜");
|
|
163
|
+
assert.equal(clientSource.includes('(!foreignOnly || m.source'), false,
|
|
164
|
+
"不许退回「搜索态本地按 source 筛」那条假信号");
|
|
165
|
+
// 服务端不吃 source 这件事,不许用「给 search 的 URL 也拼一个键」的方式绕过去:
|
|
166
|
+
// 那条路由压根不读它(src/api.js 的 search handler 里没有 source 这一档),拼上去
|
|
167
|
+
// 只会造出一个「看起来筛过了、其实没有」的形状。全文件只许有一处拼这个参数。
|
|
168
|
+
assert.equal(clientSource.split("&source=imported").length - 1, 1,
|
|
169
|
+
"只有浏览态那一条 URL 会拼 source=imported");
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
// 来源徽标与撤销都按服务端给的真值门控:徽标认 source + from- 标签,撤销认 pack_id。
|
|
173
|
+
test("the foreign badge and the withdraw action gate on server truth, not on guesswork", () => {
|
|
174
|
+
assert.ok(clientSource.includes('m.source === "imported"'),
|
|
175
|
+
"徽标先认 source,再认标签:人手工打的 from-* 标签不算外来行");
|
|
176
|
+
assert.ok(clientSource.includes('startsWith("from-")'),
|
|
177
|
+
"别名读数就是 importedTagFor 打的那条标签,面板不另找地方读别名");
|
|
178
|
+
assert.ok(clientSource.includes("memory.share_pack_id &&"),
|
|
179
|
+
"撤销按钮按包号自门控:/search 的 DTO 不带这一格,那种行按不了(已知代价,docs/SHARE.md 记着)");
|
|
180
|
+
assert.ok(clientSource.includes('apiFetch("/api/dsh-mneme/receive/undo"'),
|
|
181
|
+
"整包退出只许走这一枪");
|
|
182
|
+
assert.ok(clientSource.includes("{ packId: memory.share_pack_id, confirm: true }"),
|
|
183
|
+
"删除那一发要带 confirm 真值,且 packId 只从行上取");
|
|
184
|
+
// 两段式的**第一段**也要有锁:只钉 confirm 那一发等于没钉——把三元式删掉、每次都带
|
|
185
|
+
// confirm:true,十把锁一把都不会红,而「整包物删」变成一次点击就发生。
|
|
186
|
+
assert.ok(clientSource.includes(": { packId: memory.share_pack_id })"),
|
|
187
|
+
"清点那一发必须是不带 confirm 的形状");
|
|
188
|
+
assert.ok(clientSource.includes("onClick: () => undoPack(false)"),
|
|
189
|
+
"第一颗键走的是干跑那一支");
|
|
190
|
+
assert.ok(clientSource.includes("d.dry_run === false"),
|
|
191
|
+
"撤成功的读数同样只由答复的 dry_run 说");
|
|
192
|
+
assert.ok(clientSource.includes("undoPreview.sample"),
|
|
193
|
+
"删除前的确认要带前缀样例(答复的 sample 最多 20 条,deleted 才是全量)");
|
|
194
|
+
// 撤是**物删**(docs/SHARE.md:归档行会占住 UNIQUE 位,撤了导不回来),文案不许写「归档」。
|
|
195
|
+
assert.equal(/memory\.explorer\.undoPack[A-Za-z]*"\s*:\s*"[^"]*(归档|archive)/i.test(clientSource), false,
|
|
196
|
+
"撤销的文案不许出现「归档」——它是物理删除");
|
|
197
|
+
// 删除那一发的成功判据**只能是答复的 dry_run**。`confirm && d.dry_run === false` 那种合取
|
|
198
|
+
// 读的是「我刚才点的是哪一颗」:确认回来一个 dry_run:true 时会把预览重画一遍当作没发生——
|
|
199
|
+
// 接收弹层那一侧修过同一个形状,撤销这条更硬的路不许退回去。
|
|
200
|
+
assert.ok(clientSource.includes("if (d.dry_run === false) onUndone(d.deleted ?? 0)"),
|
|
201
|
+
"撤销的成功判据只看答复");
|
|
202
|
+
assert.equal(clientSource.includes("confirm && d.dry_run === false"), false,
|
|
203
|
+
"不许再出现按按钮形状推断的那种合取");
|
|
204
|
+
assert.ok(clientSource.includes('t("memory.explorer.undoPackGone")'),
|
|
205
|
+
"包已不在本库时要说那一句,而不是给一颗「确认删除 0 条」的键");
|
|
206
|
+
// 同一形状只许有一份:抽屉的退出清单与接收弹层的撞名清单都是 {type,title} 两列。
|
|
207
|
+
assert.ok(clientSource.includes("TypeRowList({ t, rows: undoPreview.sample })"),
|
|
208
|
+
"清单形状走公共组件,不许第三份手写 .map");
|
|
209
|
+
assert.ok(clientSource.includes('foreignOnly && !!query.trim() && h("div"'),
|
|
210
|
+
"「搜索时不按」要有一行看得见的说明,不能只挂在 disabled 的 title 上");
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
// 外来判据是这条车道唯一一处「面板自己写的字面量」:lib/client.js 是独立 bundle,
|
|
214
|
+
// 不能 import src/memory-source.js,所以用静态镜像把它钉回服务端那一份。
|
|
215
|
+
// 服务端改了那个值而这里没跟,面板的徽标与筛选会安静地筛不到东西——正是最难发现的一种坏。
|
|
216
|
+
test("the panel's foreign-ness literal mirrors the server's single definition", () => {
|
|
217
|
+
const sourceModule = readFileSync(join(here, "..", "src/memory-source.js"), "utf8");
|
|
218
|
+
const imported = /IMPORTED_SOURCE = "([^"]+)"/.exec(sourceModule);
|
|
219
|
+
assert.ok(imported, "src/memory-source.js 的导出形状变了,先修这条锁的锚");
|
|
220
|
+
const value = imported[1];
|
|
221
|
+
assert.ok(clientSource.includes(`m.source === "${value}"`),
|
|
222
|
+
"徽标读的行来源必须与服务端定义的那个值同字面");
|
|
223
|
+
assert.ok(clientSource.includes(`&source=${value}`),
|
|
224
|
+
"浏览态那条 URL 拼的来源值必须是同一个");
|
|
225
|
+
});
|