@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,245 +1,245 @@
|
|
|
1
|
-
import test from "node:test";
|
|
2
|
-
import assert from "node:assert/strict";
|
|
3
|
-
import { readFileSync } from "node:fs";
|
|
4
|
-
import { dirname, join } from "node:path";
|
|
5
|
-
import { fileURLToPath } from "node:url";
|
|
6
|
-
|
|
7
|
-
// 面板几何刻度层的回归锁(视觉系统 P0+P1a,docs/plans/2026-11-10-panel-visual-system.md)。
|
|
8
|
-
//
|
|
9
|
-
// 它咬五类漂移,每一类都真的发生过:
|
|
10
|
-
// ① 间距靠手写:padding 曾长出 107 种取值、只有 41.7% 落 4 网格、29% 是奇数像素。
|
|
11
|
-
// 现在 CSS 里每一处 px 都必须在「刻度集」或「已登记的存量」里,且存量**只许减
|
|
12
|
-
// 不许增**——新加一个 padding:11px 会红,把一处 11px 改成 8px 不会红(那是迁移)。
|
|
13
|
-
// ② 刻度定义散架:token 少了、值改了、挂的渲染根从五个变成四个,都会红。
|
|
14
|
-
// ③ 原语被绕过:GEOMETRY 之外的键(9 / 10 / "8px")必须当场抛错,宁可渲染炸掉,
|
|
15
|
-
// 也不要多出第 108 种间距——后者没人会注意,前者一进测试就红。
|
|
16
|
-
// ④ CSS 与 GEOMETRY 各说一套:两边的键必须一一对应,刻度加了 CSS 没加(或反过来)也红。
|
|
17
|
-
// ⑤ 迁移做一半:P1b 之后状态页的间距/圆角必须整页来自刻度,混回一个 padding:10px
|
|
18
|
-
// 就红——半迁移比不迁移更难读,因为看代码的人分不清哪个数是准的。
|
|
19
|
-
//
|
|
20
|
-
// 变异自证(PR 里要附):把 client.js 里任一处 `padding:0 16px` 改成 `padding:0 17px`
|
|
21
|
-
// → 本文件第 2 条用例红;把 GEOMETRY.space 里的 12 删掉但保留 --mneme-space-12
|
|
22
|
-
// → 第 4 条红;把 .mneme-paneliglyph 从 token 规则的选择器里删掉 → 第 3 条红;
|
|
23
|
-
// 把状态页任一处 var(--mneme-space-12) 改回 12px → 第 6 条红。
|
|
24
|
-
|
|
25
|
-
const root = join(dirname(fileURLToPath(import.meta.url)), "..");
|
|
26
|
-
const clientSource = readFileSync(join(root, "lib/client.js"), "utf8");
|
|
27
|
-
|
|
28
|
-
/* ---------- 切片:与 test/contrast.test.js 同一对锚点 ---------- */
|
|
29
|
-
const cssStart = clientSource.indexOf("const css = [");
|
|
30
|
-
const cssEnd = clientSource.indexOf('].join("\\n");', cssStart);
|
|
31
|
-
assert.ok(cssStart > 0 && cssEnd > cssStart, "lib/client.js 里找不到 CSS 数组(切片锚点变了?)");
|
|
32
|
-
const cssBlock = clientSource.slice(cssStart, cssEnd);
|
|
33
|
-
|
|
34
|
-
// 注释里的 px 是散文(「隐掉 14px 的 tab 图标」),不是声明;只量真声明。
|
|
35
|
-
const declaredCss = cssBlock.split("\n").filter((l) => !/^\s*\/\//.test(l)).join("\n");
|
|
36
|
-
const joinedCss = [...cssBlock.matchAll(/"((?:[^"\\]|\\.)*)"/g)].map((m) => m[1].replace(/\\"/g, '"')).join("");
|
|
37
|
-
|
|
38
|
-
/* ---------- 刻度集:与 client.js 的 GEOMETRY 表成对出现 ---------- */
|
|
39
|
-
const EXPECTED = {
|
|
40
|
-
space: { 0: "0", 2: "2px", 4: "4px", 6: "6px", 8: "8px", 10: "10px", 12: "12px", 16: "16px", 20: "20px", 24: "24px", 40: "40px" },
|
|
41
|
-
radius: { 4: "4px", 8: "8px", 12: "12px", pill: "999px" },
|
|
42
|
-
type: { 12: "12px", 13: "13px", 15: "15px" },
|
|
43
|
-
weight: { 400: "400", 600: "600" }
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
test("几何刻度定义完整且取值未漂移", () => {
|
|
47
|
-
const found = {};
|
|
48
|
-
for (const m of joinedCss.matchAll(/--mneme-(space|radius|type|weight)-([a-z0-9]+)\s*:\s*([^;}]+)/g)) {
|
|
49
|
-
(found[m[1]] ||= {})[m[2]] = m[3].trim();
|
|
50
|
-
}
|
|
51
|
-
for (const [group, table] of Object.entries(EXPECTED)) {
|
|
52
|
-
assert.deepEqual(found[group] || {}, table, `--mneme-${group}-* 刻度集与 GEOMETRY 不一致`);
|
|
53
|
-
}
|
|
54
|
-
// 反向:不许有表外的 --mneme-<group>-*。加了新档必须同时进这里和 GEOMETRY。
|
|
55
|
-
for (const group of Object.keys(EXPECTED)) {
|
|
56
|
-
const extra = Object.keys(found[group] || {}).filter((k) => !(k in EXPECTED[group]));
|
|
57
|
-
assert.deepEqual(extra, [], `--mneme-${group}-* 出现表外档位:${extra.join(",")}`);
|
|
58
|
-
}
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
test("几何刻度挂满五个渲染根(少挂一个就等于没定义)", () => {
|
|
62
|
-
const m = /([^{}]*)\{[^{}]*--mneme-space-8\s*:/.exec(joinedCss);
|
|
63
|
-
assert.ok(m, "找不到定义几何刻度的规则");
|
|
64
|
-
const sel = m[1].split("}").pop().trim();
|
|
65
|
-
assert.equal(
|
|
66
|
-
sel,
|
|
67
|
-
".mneme-x,.mneme-overlay,.mneme-triggerwrap,.mneme-topentry,.mneme-paneliglyph",
|
|
68
|
-
"几何刻度必须和语义色层挂同一组五个根:overlay / 侧栏插槽 / 顶部入口 / 宿主 panelGlyph 都不在 .mneme-x 子树里"
|
|
69
|
-
);
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
/* ---------- 存量棘轮:px 只许减少,不许新增 ---------- */
|
|
73
|
-
// 逐值计数,冻结在「本 PR 合进来的那棵树」上:ef8f9ce 之后 main 又合了 #642(情感归属可见),
|
|
74
|
-
// 它自己在 client.js 里带进 21 处 px(1px +3 / 2px +2 / 3px +1 / 6px +5 / 7px +1 / 8px +2 /
|
|
75
|
-
// 11px +4 / 14px +1 / 16px +2),所以下表 = ef8f9ce 基线 − 本分支的迁移 + #642 的增量。
|
|
76
|
-
// 本分支相对 main 净减 87 处(减 89、加 2,那 2 处是 15px 字阶与 999px pill 半径这两个
|
|
77
|
-
// 刻度定义本身,不是手写间距)。迁移时**把数字往下改**,不要为了让测试变绿去加值——
|
|
78
|
-
// 加值等于把漂移写进法律;下表这次往上动,是因为 main 先涨了。
|
|
79
|
-
// 2026-11-11 又一次上调(#647 与视觉系统合并时):#618 的地志 wiki 页在 main 上
|
|
80
|
-
// 带进了新 px(1px +3 / 3px +1 / 5px +1 / 6px +7 / 10px +3 / 11px +3 / 12px +3 /
|
|
81
|
-
// 13px +1 / 14px +2 / 16px +4 / 17px +1 / 18px +1 / 11.5px +2 / 10.5px +1),基线
|
|
82
|
-
// 实测于它合入之前。wiki 页的刻度化是地志车道的跟进任务,本棘轮从当前树重新
|
|
83
|
-
// 冻结——下一只动基线的手必须先证明自己在做迁移。
|
|
84
|
-
const BASELINE = {
|
|
85
|
-
1: 137, 1.5: 8, 2: 72, 2.5: 1, 3: 28, 4: 46,
|
|
86
|
-
5: 22, 6: 95, 7: 21, 8: 160, 9: 18, 9.5: 4,
|
|
87
|
-
10: 118, 10.5: 5, 11: 58, 11.5: 11, 12: 144, 12.5: 15,
|
|
88
|
-
13: 54, 13.5: 6, 14: 57, 15: 12, 16: 74, 17: 28,
|
|
89
|
-
18: 46, 19: 14, 20: 20, 21: 4, 22: 14, 23: 1,
|
|
90
|
-
24: 21, 25: 2, 26: 12, 27: 1, 28: 10, 30: 7,
|
|
91
|
-
32: 11, 34: 1, 36: 5, 40: 5, 42: 1, 44: 2,
|
|
92
|
-
46: 3, 48: 3, 52: 2, 64: 2, 76: 1, 84: 2,
|
|
93
|
-
88: 1, 96: 4, 108: 1, 110: 1, 120: 1, 132: 1,
|
|
94
|
-
148: 2, 150: 2, 180: 6, 190: 1, 200: 4, 216: 1,
|
|
95
|
-
220: 4, 224: 1, 230: 1, 232: 1, 240: 3, 252: 1,
|
|
96
|
-
260: 1, 264: 1, 280: 1, 300: 4, 320: 1, 340: 2,
|
|
97
|
-
360: 1, 432: 1, 452: 2, 480: 3, 500: 1, 560: 3,
|
|
98
|
-
620: 1, 640: 3, 680: 1, 683: 1, 720: 3, 860: 1,
|
|
99
|
-
920: 1, 960: 1, 999: 1, 1000: 7, 1240: 1
|
|
100
|
-
};
|
|
101
|
-
|
|
102
|
-
// 合法例外:不登记就等于「随便写」。每条都要说清为什么它不该进刻度。
|
|
103
|
-
const WHITELIST = {
|
|
104
|
-
1: "1px 描边/分隔线(发丝线,缩放后仍是线而不是块)",
|
|
105
|
-
2: "2px 焦点环与 tab 下划线(视觉系统 half-step,非布局间距)"
|
|
106
|
-
};
|
|
107
|
-
|
|
108
|
-
test("CSS 里的 px 只能来自刻度或存量,存量只许减不许增", () => {
|
|
109
|
-
const current = {};
|
|
110
|
-
for (const m of declaredCss.matchAll(/(?<![\w.-])(\d+(?:\.\d+)?)px/g)) {
|
|
111
|
-
const v = Number(m[1]);
|
|
112
|
-
current[v] = (current[v] || 0) + 1;
|
|
113
|
-
}
|
|
114
|
-
const grew = [];
|
|
115
|
-
const intruders = [];
|
|
116
|
-
for (const [v, count] of Object.entries(current)) {
|
|
117
|
-
const key = Number(v);
|
|
118
|
-
if (key in BASELINE) {
|
|
119
|
-
if (count > BASELINE[key]) grew.push(`${v}px: ${BASELINE[key]} → ${count}`);
|
|
120
|
-
} else if (!(key in WHITELIST)) {
|
|
121
|
-
intruders.push(`${v}px × ${count}`);
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
assert.deepEqual(intruders, [], "出现刻度外的 px 取值(先问它该不该是一个新档):" + intruders.join(", "));
|
|
125
|
-
assert.deepEqual(grew, [], "存量 px 变多了(迁移应让它变少;确实要加请先想清楚是不是该用刻度):" + grew.join(", "));
|
|
126
|
-
// 白名单条目失效要报红:值在代码里消失了说明刻度或写法变了,清单没跟着走就是僵尸条目。
|
|
127
|
-
for (const [v, why] of Object.entries(WHITELIST)) {
|
|
128
|
-
assert.ok(Number(v) in current, `白名单里的 ${v}px 已经不在 CSS 里了(${why})——清单该删该改?`);
|
|
129
|
-
}
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
/* ---------- 迁移进度锁:状态页的几何全部来自刻度(P1b) ---------- */
|
|
133
|
-
// 状态视图 = 状态卡网格 + Token 三屏 + 冲突队列 + 工作台。这一片此前是「最像手写
|
|
134
|
-
// 排版」的一片:padding 有 16/14/10/8 四档混用,圆角有 12/10/8/3 四种,分组间距
|
|
135
|
-
// 26px、页尾留白 40px 都不在任何刻度上。现在全部走 token,并且这条锁不允许回退。
|
|
136
|
-
//
|
|
137
|
-
// 两个例外,都写在这里而不是放任:
|
|
138
|
-
// · 1px —— 光学微调(.mneme-conflict-mark 的左右 padding 这么宽),
|
|
139
|
-
// 它的作用是「贴着」而不是「隔着一段」,进刻度会让文字挤在一起;
|
|
140
|
-
// · 断点档(@container / @media)整体跳过:那一档是「压到正好不溢出」的贴合值
|
|
141
|
-
// (14px / 32px / 30px),不是节奏;把它收进刻度等于每次调断点都要改刻度表。
|
|
142
|
-
const STATUS_NS = /^\.mneme-(status|stat|tokenflow|kpiband|donut|sink|legacy|wb|conflict|pending|heatbar)/;
|
|
143
|
-
const GEOM_PROP = /^(padding|margin|gap|row-gap|column-gap)(-(top|right|bottom|left))?$/;
|
|
144
|
-
|
|
145
|
-
test("状态页的间距与圆角全部走刻度(P1b 不回退)", () => {
|
|
146
|
-
const violations = [];
|
|
147
|
-
let coveredRules = 0;
|
|
148
|
-
let coveredDecls = 0;
|
|
149
|
-
for (const raw of declaredCss.split("\n")) {
|
|
150
|
-
const line = raw.trim();
|
|
151
|
-
if (line.startsWith("@")) continue;
|
|
152
|
-
const m = /^"([^"]*)\{(.*)\}"?,?$/.exec(line);
|
|
153
|
-
if (!m) continue;
|
|
154
|
-
const selector = m[1];
|
|
155
|
-
if (!selector.split(",").some((s) => STATUS_NS.test(s.trim()))) continue;
|
|
156
|
-
coveredRules += 1;
|
|
157
|
-
for (const decl of m[2].split(";")) {
|
|
158
|
-
const at = decl.indexOf(":");
|
|
159
|
-
if (at < 0) continue;
|
|
160
|
-
const prop = decl.slice(0, at).trim();
|
|
161
|
-
const value = decl.slice(at + 1).trim();
|
|
162
|
-
const isSpacing = GEOM_PROP.test(prop);
|
|
163
|
-
const isRadius = prop === "border-radius";
|
|
164
|
-
if (!isSpacing && !isRadius) continue;
|
|
165
|
-
coveredDecls += 1;
|
|
166
|
-
// 先把 token 抠出来查「档位存不存在、组对不对」:var(--mneme-space-13) 这种
|
|
167
|
-
// 未定义的键不会报错、只是渲染成空值,光看前缀是查不出来的。
|
|
168
|
-
const keys = [...value.matchAll(/var\(--mneme-(space|radius)-([a-z0-9]+)\)/g)];
|
|
169
|
-
for (const k of keys) {
|
|
170
|
-
const table = EXPECTED[k[1]];
|
|
171
|
-
if (!(k[2] in table)) {
|
|
172
|
-
violations.push(selector + " { " + prop + ": " + value + " } ← " + k[1] + " 没有档位 " + k[2]);
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
const rest = value.replace(/var\(--mneme-(?:space|radius)-[a-z0-9]+\)/g, " ").split(/\s+/).filter(Boolean);
|
|
176
|
-
const leftoversOk = isSpacing
|
|
177
|
-
? rest.every((x) => x === "0" || x === "auto" || x === "1px")
|
|
178
|
-
: rest.every((x) => x === "0" || x === "50%");
|
|
179
|
-
const groupOk = isSpacing ? keys.every((k) => k[1] === "space") : keys.every((k) => k[1] === "radius");
|
|
180
|
-
// 注意:不能要求「必须有 token」——auto / 1px / 50% 是刻意的例外,
|
|
181
|
-
// 本身就已经是合法值(leftoversOk 会放它们过),要求 token 只会把它们误判成违规。
|
|
182
|
-
if (!leftoversOk || !groupOk) {
|
|
183
|
-
violations.push(selector + " { " + prop + ": " + value + " }");
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
assert.deepEqual(violations, [], "状态页出现刻度外的间距/圆角:" + violations.join(" ; "));
|
|
188
|
-
// 反向:不许因为「一个状态类都没匹配上」而空转通过(改类名、改切片锚点都会走到这里)。
|
|
189
|
-
assert.ok(coveredRules >= 80, "扫到的状态页规则只有 " + coveredRules + " 条(预期 ≥80),锁空转了?");
|
|
190
|
-
assert.ok(coveredDecls >= 60, "扫到的状态页几何声明只有 " + coveredDecls + " 条(预期 ≥60),锁空转了?");
|
|
191
|
-
});
|
|
192
|
-
|
|
193
|
-
/* ---------- 原语层:抽出来真跑,不靠读源码猜 ---------- */
|
|
194
|
-
const gStart = clientSource.indexOf(" const GEOMETRY = {");
|
|
195
|
-
const gEnd = clientSource.indexOf("/* panel-geometry:end */", gStart);
|
|
196
|
-
assert.ok(gStart > 0 && gEnd > gStart, "找不到 panel-geometry 标记块(原语层被删或标记丢了)");
|
|
197
|
-
const geometrySource = clientSource.slice(gStart, gEnd);
|
|
198
|
-
|
|
199
|
-
const makeApi = () =>
|
|
200
|
-
new Function(
|
|
201
|
-
"const h = arguments[0];\n" +
|
|
202
|
-
geometrySource +
|
|
203
|
-
"\nreturn { GEOMETRY, space, radius, fontOf, weightOf, Stack, Inline, Card, Section, Grid };"
|
|
204
|
-
)((type, props, ...kids) => ({ type, props, kids }));
|
|
205
|
-
|
|
206
|
-
test("原语层只收刻度键,表外值当场抛错(这是这条锁存在的理由)", () => {
|
|
207
|
-
const api = makeApi();
|
|
208
|
-
assert.equal(api.space(8), "var(--mneme-space-8)");
|
|
209
|
-
assert.equal(api.space(0), "var(--mneme-space-0)");
|
|
210
|
-
assert.equal(api.space(24), "var(--mneme-space-24)");
|
|
211
|
-
assert.equal(api.radius("pill"), "var(--mneme-radius-pill)");
|
|
212
|
-
assert.equal(api.fontOf(13), "var(--mneme-type-13)");
|
|
213
|
-
assert.equal(api.weightOf(600), "var(--mneme-weight-600)");
|
|
214
|
-
for (const bad of [9, 11, 14, 25, "8px", "8 ", null, undefined]) {
|
|
215
|
-
assert.throws(() => api.space(bad), /只收刻度/, `space(${JSON.stringify(bad)}) 必须抛错`);
|
|
216
|
-
}
|
|
217
|
-
assert.throws(() => api.radius(6), /只收刻度/, "radius 不共享 space 的档位");
|
|
218
|
-
assert.throws(() => api.space("16"), /只收刻度/, "字符串键同样要查表(16 不是档位)");
|
|
219
|
-
});
|
|
220
|
-
|
|
221
|
-
test("原语真的把刻度写进 style,且默认值都在刻度内", () => {
|
|
222
|
-
const api = makeApi();
|
|
223
|
-
const stack = api.Stack({ gap: 12, pad: 8, align: "center", children: "x" });
|
|
224
|
-
assert.equal(stack.props.style.gap, "var(--mneme-space-12)");
|
|
225
|
-
assert.equal(stack.props.style.padding, "var(--mneme-space-8)");
|
|
226
|
-
assert.equal(stack.props.style.flexDirection, "column");
|
|
227
|
-
assert.throws(() => api.Stack({ gap: 9 }), /只收刻度/);
|
|
228
|
-
|
|
229
|
-
assert.equal(api.Inline({}).props.style.gap, "var(--mneme-space-6)");
|
|
230
|
-
assert.equal(api.Inline({}).props.style.flexDirection, "row");
|
|
231
|
-
assert.equal(api.Card({}).props.style.padding, "var(--mneme-space-12)");
|
|
232
|
-
assert.equal(api.Card({ dense: true }).props.style.padding, "var(--mneme-space-8)");
|
|
233
|
-
assert.equal(api.Card({}).props.style.borderRadius, "var(--mneme-radius-8)");
|
|
234
|
-
assert.match(String(api.Grid({}).props.style.gridTemplateColumns), /^repeat\(auto-fill,minmax\(220px,1fr\)\)$/);
|
|
235
|
-
assert.equal(api.Grid({ gap: 16 }).props.style.gap, "var(--mneme-space-16)");
|
|
236
|
-
assert.throws(() => api.Grid({ gap: 11 }), /只收刻度/);
|
|
237
|
-
assert.equal(api.Section({}).props.style.gap, "var(--mneme-space-8)");
|
|
238
|
-
});
|
|
239
|
-
|
|
240
|
-
test("原语层内部不许出现裸 px 字面量(1px 发丝线与栅格下限除外)", () => {
|
|
241
|
-
// 两个例外写在这里而不是放任:1px 是发丝线(缩放后仍是线),栅格 min 是
|
|
242
|
-
// 「内容多宽才排下一列」的阈值,不是间距——它进刻度反而会让栅格跟着间距漂。
|
|
243
|
-
const literal = (geometrySource.match(/(?<![\w.-])\d+px/g) || []).filter((v) => v !== "1px");
|
|
244
|
-
assert.deepEqual(literal, [], "原语层写了 px 字面量,绕过了刻度:" + literal.join(", "));
|
|
245
|
-
});
|
|
1
|
+
import test from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { readFileSync } from "node:fs";
|
|
4
|
+
import { dirname, join } from "node:path";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
|
|
7
|
+
// 面板几何刻度层的回归锁(视觉系统 P0+P1a,docs/plans/2026-11-10-panel-visual-system.md)。
|
|
8
|
+
//
|
|
9
|
+
// 它咬五类漂移,每一类都真的发生过:
|
|
10
|
+
// ① 间距靠手写:padding 曾长出 107 种取值、只有 41.7% 落 4 网格、29% 是奇数像素。
|
|
11
|
+
// 现在 CSS 里每一处 px 都必须在「刻度集」或「已登记的存量」里,且存量**只许减
|
|
12
|
+
// 不许增**——新加一个 padding:11px 会红,把一处 11px 改成 8px 不会红(那是迁移)。
|
|
13
|
+
// ② 刻度定义散架:token 少了、值改了、挂的渲染根从五个变成四个,都会红。
|
|
14
|
+
// ③ 原语被绕过:GEOMETRY 之外的键(9 / 10 / "8px")必须当场抛错,宁可渲染炸掉,
|
|
15
|
+
// 也不要多出第 108 种间距——后者没人会注意,前者一进测试就红。
|
|
16
|
+
// ④ CSS 与 GEOMETRY 各说一套:两边的键必须一一对应,刻度加了 CSS 没加(或反过来)也红。
|
|
17
|
+
// ⑤ 迁移做一半:P1b 之后状态页的间距/圆角必须整页来自刻度,混回一个 padding:10px
|
|
18
|
+
// 就红——半迁移比不迁移更难读,因为看代码的人分不清哪个数是准的。
|
|
19
|
+
//
|
|
20
|
+
// 变异自证(PR 里要附):把 client.js 里任一处 `padding:0 16px` 改成 `padding:0 17px`
|
|
21
|
+
// → 本文件第 2 条用例红;把 GEOMETRY.space 里的 12 删掉但保留 --mneme-space-12
|
|
22
|
+
// → 第 4 条红;把 .mneme-paneliglyph 从 token 规则的选择器里删掉 → 第 3 条红;
|
|
23
|
+
// 把状态页任一处 var(--mneme-space-12) 改回 12px → 第 6 条红。
|
|
24
|
+
|
|
25
|
+
const root = join(dirname(fileURLToPath(import.meta.url)), "..");
|
|
26
|
+
const clientSource = readFileSync(join(root, "lib/client.js"), "utf8");
|
|
27
|
+
|
|
28
|
+
/* ---------- 切片:与 test/contrast.test.js 同一对锚点 ---------- */
|
|
29
|
+
const cssStart = clientSource.indexOf("const css = [");
|
|
30
|
+
const cssEnd = clientSource.indexOf('].join("\\n");', cssStart);
|
|
31
|
+
assert.ok(cssStart > 0 && cssEnd > cssStart, "lib/client.js 里找不到 CSS 数组(切片锚点变了?)");
|
|
32
|
+
const cssBlock = clientSource.slice(cssStart, cssEnd);
|
|
33
|
+
|
|
34
|
+
// 注释里的 px 是散文(「隐掉 14px 的 tab 图标」),不是声明;只量真声明。
|
|
35
|
+
const declaredCss = cssBlock.split("\n").filter((l) => !/^\s*\/\//.test(l)).join("\n");
|
|
36
|
+
const joinedCss = [...cssBlock.matchAll(/"((?:[^"\\]|\\.)*)"/g)].map((m) => m[1].replace(/\\"/g, '"')).join("");
|
|
37
|
+
|
|
38
|
+
/* ---------- 刻度集:与 client.js 的 GEOMETRY 表成对出现 ---------- */
|
|
39
|
+
const EXPECTED = {
|
|
40
|
+
space: { 0: "0", 2: "2px", 4: "4px", 6: "6px", 8: "8px", 10: "10px", 12: "12px", 16: "16px", 20: "20px", 24: "24px", 40: "40px" },
|
|
41
|
+
radius: { 4: "4px", 8: "8px", 12: "12px", pill: "999px" },
|
|
42
|
+
type: { 12: "12px", 13: "13px", 15: "15px" },
|
|
43
|
+
weight: { 400: "400", 600: "600" }
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
test("几何刻度定义完整且取值未漂移", () => {
|
|
47
|
+
const found = {};
|
|
48
|
+
for (const m of joinedCss.matchAll(/--mneme-(space|radius|type|weight)-([a-z0-9]+)\s*:\s*([^;}]+)/g)) {
|
|
49
|
+
(found[m[1]] ||= {})[m[2]] = m[3].trim();
|
|
50
|
+
}
|
|
51
|
+
for (const [group, table] of Object.entries(EXPECTED)) {
|
|
52
|
+
assert.deepEqual(found[group] || {}, table, `--mneme-${group}-* 刻度集与 GEOMETRY 不一致`);
|
|
53
|
+
}
|
|
54
|
+
// 反向:不许有表外的 --mneme-<group>-*。加了新档必须同时进这里和 GEOMETRY。
|
|
55
|
+
for (const group of Object.keys(EXPECTED)) {
|
|
56
|
+
const extra = Object.keys(found[group] || {}).filter((k) => !(k in EXPECTED[group]));
|
|
57
|
+
assert.deepEqual(extra, [], `--mneme-${group}-* 出现表外档位:${extra.join(",")}`);
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
test("几何刻度挂满五个渲染根(少挂一个就等于没定义)", () => {
|
|
62
|
+
const m = /([^{}]*)\{[^{}]*--mneme-space-8\s*:/.exec(joinedCss);
|
|
63
|
+
assert.ok(m, "找不到定义几何刻度的规则");
|
|
64
|
+
const sel = m[1].split("}").pop().trim();
|
|
65
|
+
assert.equal(
|
|
66
|
+
sel,
|
|
67
|
+
".mneme-x,.mneme-overlay,.mneme-triggerwrap,.mneme-topentry,.mneme-paneliglyph",
|
|
68
|
+
"几何刻度必须和语义色层挂同一组五个根:overlay / 侧栏插槽 / 顶部入口 / 宿主 panelGlyph 都不在 .mneme-x 子树里"
|
|
69
|
+
);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
/* ---------- 存量棘轮:px 只许减少,不许新增 ---------- */
|
|
73
|
+
// 逐值计数,冻结在「本 PR 合进来的那棵树」上:ef8f9ce 之后 main 又合了 #642(情感归属可见),
|
|
74
|
+
// 它自己在 client.js 里带进 21 处 px(1px +3 / 2px +2 / 3px +1 / 6px +5 / 7px +1 / 8px +2 /
|
|
75
|
+
// 11px +4 / 14px +1 / 16px +2),所以下表 = ef8f9ce 基线 − 本分支的迁移 + #642 的增量。
|
|
76
|
+
// 本分支相对 main 净减 87 处(减 89、加 2,那 2 处是 15px 字阶与 999px pill 半径这两个
|
|
77
|
+
// 刻度定义本身,不是手写间距)。迁移时**把数字往下改**,不要为了让测试变绿去加值——
|
|
78
|
+
// 加值等于把漂移写进法律;下表这次往上动,是因为 main 先涨了。
|
|
79
|
+
// 2026-11-11 又一次上调(#647 与视觉系统合并时):#618 的地志 wiki 页在 main 上
|
|
80
|
+
// 带进了新 px(1px +3 / 3px +1 / 5px +1 / 6px +7 / 10px +3 / 11px +3 / 12px +3 /
|
|
81
|
+
// 13px +1 / 14px +2 / 16px +4 / 17px +1 / 18px +1 / 11.5px +2 / 10.5px +1),基线
|
|
82
|
+
// 实测于它合入之前。wiki 页的刻度化是地志车道的跟进任务,本棘轮从当前树重新
|
|
83
|
+
// 冻结——下一只动基线的手必须先证明自己在做迁移。
|
|
84
|
+
const BASELINE = {
|
|
85
|
+
1: 137, 1.5: 8, 2: 72, 2.5: 1, 3: 28, 4: 46,
|
|
86
|
+
5: 22, 6: 95, 7: 21, 8: 160, 9: 18, 9.5: 4,
|
|
87
|
+
10: 118, 10.5: 5, 11: 58, 11.5: 11, 12: 144, 12.5: 15,
|
|
88
|
+
13: 54, 13.5: 6, 14: 57, 15: 12, 16: 74, 17: 28,
|
|
89
|
+
18: 46, 19: 14, 20: 20, 21: 4, 22: 14, 23: 1,
|
|
90
|
+
24: 21, 25: 2, 26: 12, 27: 1, 28: 10, 30: 7,
|
|
91
|
+
32: 11, 34: 1, 36: 5, 40: 5, 42: 1, 44: 2,
|
|
92
|
+
46: 3, 48: 3, 52: 2, 64: 2, 76: 1, 84: 2,
|
|
93
|
+
88: 1, 96: 4, 108: 1, 110: 1, 120: 1, 132: 1,
|
|
94
|
+
148: 2, 150: 2, 180: 6, 190: 1, 200: 4, 216: 1,
|
|
95
|
+
220: 4, 224: 1, 230: 1, 232: 1, 240: 3, 252: 1,
|
|
96
|
+
260: 1, 264: 1, 280: 1, 300: 4, 320: 1, 340: 2,
|
|
97
|
+
360: 1, 432: 1, 452: 2, 480: 3, 500: 1, 560: 3,
|
|
98
|
+
620: 1, 640: 3, 680: 1, 683: 1, 720: 3, 860: 1,
|
|
99
|
+
920: 1, 960: 1, 999: 1, 1000: 7, 1240: 1
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
// 合法例外:不登记就等于「随便写」。每条都要说清为什么它不该进刻度。
|
|
103
|
+
const WHITELIST = {
|
|
104
|
+
1: "1px 描边/分隔线(发丝线,缩放后仍是线而不是块)",
|
|
105
|
+
2: "2px 焦点环与 tab 下划线(视觉系统 half-step,非布局间距)"
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
test("CSS 里的 px 只能来自刻度或存量,存量只许减不许增", () => {
|
|
109
|
+
const current = {};
|
|
110
|
+
for (const m of declaredCss.matchAll(/(?<![\w.-])(\d+(?:\.\d+)?)px/g)) {
|
|
111
|
+
const v = Number(m[1]);
|
|
112
|
+
current[v] = (current[v] || 0) + 1;
|
|
113
|
+
}
|
|
114
|
+
const grew = [];
|
|
115
|
+
const intruders = [];
|
|
116
|
+
for (const [v, count] of Object.entries(current)) {
|
|
117
|
+
const key = Number(v);
|
|
118
|
+
if (key in BASELINE) {
|
|
119
|
+
if (count > BASELINE[key]) grew.push(`${v}px: ${BASELINE[key]} → ${count}`);
|
|
120
|
+
} else if (!(key in WHITELIST)) {
|
|
121
|
+
intruders.push(`${v}px × ${count}`);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
assert.deepEqual(intruders, [], "出现刻度外的 px 取值(先问它该不该是一个新档):" + intruders.join(", "));
|
|
125
|
+
assert.deepEqual(grew, [], "存量 px 变多了(迁移应让它变少;确实要加请先想清楚是不是该用刻度):" + grew.join(", "));
|
|
126
|
+
// 白名单条目失效要报红:值在代码里消失了说明刻度或写法变了,清单没跟着走就是僵尸条目。
|
|
127
|
+
for (const [v, why] of Object.entries(WHITELIST)) {
|
|
128
|
+
assert.ok(Number(v) in current, `白名单里的 ${v}px 已经不在 CSS 里了(${why})——清单该删该改?`);
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
/* ---------- 迁移进度锁:状态页的几何全部来自刻度(P1b) ---------- */
|
|
133
|
+
// 状态视图 = 状态卡网格 + Token 三屏 + 冲突队列 + 工作台。这一片此前是「最像手写
|
|
134
|
+
// 排版」的一片:padding 有 16/14/10/8 四档混用,圆角有 12/10/8/3 四种,分组间距
|
|
135
|
+
// 26px、页尾留白 40px 都不在任何刻度上。现在全部走 token,并且这条锁不允许回退。
|
|
136
|
+
//
|
|
137
|
+
// 两个例外,都写在这里而不是放任:
|
|
138
|
+
// · 1px —— 光学微调(.mneme-conflict-mark 的左右 padding 这么宽),
|
|
139
|
+
// 它的作用是「贴着」而不是「隔着一段」,进刻度会让文字挤在一起;
|
|
140
|
+
// · 断点档(@container / @media)整体跳过:那一档是「压到正好不溢出」的贴合值
|
|
141
|
+
// (14px / 32px / 30px),不是节奏;把它收进刻度等于每次调断点都要改刻度表。
|
|
142
|
+
const STATUS_NS = /^\.mneme-(status|stat|tokenflow|kpiband|donut|sink|legacy|wb|conflict|pending|heatbar)/;
|
|
143
|
+
const GEOM_PROP = /^(padding|margin|gap|row-gap|column-gap)(-(top|right|bottom|left))?$/;
|
|
144
|
+
|
|
145
|
+
test("状态页的间距与圆角全部走刻度(P1b 不回退)", () => {
|
|
146
|
+
const violations = [];
|
|
147
|
+
let coveredRules = 0;
|
|
148
|
+
let coveredDecls = 0;
|
|
149
|
+
for (const raw of declaredCss.split("\n")) {
|
|
150
|
+
const line = raw.trim();
|
|
151
|
+
if (line.startsWith("@")) continue;
|
|
152
|
+
const m = /^"([^"]*)\{(.*)\}"?,?$/.exec(line);
|
|
153
|
+
if (!m) continue;
|
|
154
|
+
const selector = m[1];
|
|
155
|
+
if (!selector.split(",").some((s) => STATUS_NS.test(s.trim()))) continue;
|
|
156
|
+
coveredRules += 1;
|
|
157
|
+
for (const decl of m[2].split(";")) {
|
|
158
|
+
const at = decl.indexOf(":");
|
|
159
|
+
if (at < 0) continue;
|
|
160
|
+
const prop = decl.slice(0, at).trim();
|
|
161
|
+
const value = decl.slice(at + 1).trim();
|
|
162
|
+
const isSpacing = GEOM_PROP.test(prop);
|
|
163
|
+
const isRadius = prop === "border-radius";
|
|
164
|
+
if (!isSpacing && !isRadius) continue;
|
|
165
|
+
coveredDecls += 1;
|
|
166
|
+
// 先把 token 抠出来查「档位存不存在、组对不对」:var(--mneme-space-13) 这种
|
|
167
|
+
// 未定义的键不会报错、只是渲染成空值,光看前缀是查不出来的。
|
|
168
|
+
const keys = [...value.matchAll(/var\(--mneme-(space|radius)-([a-z0-9]+)\)/g)];
|
|
169
|
+
for (const k of keys) {
|
|
170
|
+
const table = EXPECTED[k[1]];
|
|
171
|
+
if (!(k[2] in table)) {
|
|
172
|
+
violations.push(selector + " { " + prop + ": " + value + " } ← " + k[1] + " 没有档位 " + k[2]);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
const rest = value.replace(/var\(--mneme-(?:space|radius)-[a-z0-9]+\)/g, " ").split(/\s+/).filter(Boolean);
|
|
176
|
+
const leftoversOk = isSpacing
|
|
177
|
+
? rest.every((x) => x === "0" || x === "auto" || x === "1px")
|
|
178
|
+
: rest.every((x) => x === "0" || x === "50%");
|
|
179
|
+
const groupOk = isSpacing ? keys.every((k) => k[1] === "space") : keys.every((k) => k[1] === "radius");
|
|
180
|
+
// 注意:不能要求「必须有 token」——auto / 1px / 50% 是刻意的例外,
|
|
181
|
+
// 本身就已经是合法值(leftoversOk 会放它们过),要求 token 只会把它们误判成违规。
|
|
182
|
+
if (!leftoversOk || !groupOk) {
|
|
183
|
+
violations.push(selector + " { " + prop + ": " + value + " }");
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
assert.deepEqual(violations, [], "状态页出现刻度外的间距/圆角:" + violations.join(" ; "));
|
|
188
|
+
// 反向:不许因为「一个状态类都没匹配上」而空转通过(改类名、改切片锚点都会走到这里)。
|
|
189
|
+
assert.ok(coveredRules >= 80, "扫到的状态页规则只有 " + coveredRules + " 条(预期 ≥80),锁空转了?");
|
|
190
|
+
assert.ok(coveredDecls >= 60, "扫到的状态页几何声明只有 " + coveredDecls + " 条(预期 ≥60),锁空转了?");
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
/* ---------- 原语层:抽出来真跑,不靠读源码猜 ---------- */
|
|
194
|
+
const gStart = clientSource.indexOf(" const GEOMETRY = {");
|
|
195
|
+
const gEnd = clientSource.indexOf("/* panel-geometry:end */", gStart);
|
|
196
|
+
assert.ok(gStart > 0 && gEnd > gStart, "找不到 panel-geometry 标记块(原语层被删或标记丢了)");
|
|
197
|
+
const geometrySource = clientSource.slice(gStart, gEnd);
|
|
198
|
+
|
|
199
|
+
const makeApi = () =>
|
|
200
|
+
new Function(
|
|
201
|
+
"const h = arguments[0];\n" +
|
|
202
|
+
geometrySource +
|
|
203
|
+
"\nreturn { GEOMETRY, space, radius, fontOf, weightOf, Stack, Inline, Card, Section, Grid };"
|
|
204
|
+
)((type, props, ...kids) => ({ type, props, kids }));
|
|
205
|
+
|
|
206
|
+
test("原语层只收刻度键,表外值当场抛错(这是这条锁存在的理由)", () => {
|
|
207
|
+
const api = makeApi();
|
|
208
|
+
assert.equal(api.space(8), "var(--mneme-space-8)");
|
|
209
|
+
assert.equal(api.space(0), "var(--mneme-space-0)");
|
|
210
|
+
assert.equal(api.space(24), "var(--mneme-space-24)");
|
|
211
|
+
assert.equal(api.radius("pill"), "var(--mneme-radius-pill)");
|
|
212
|
+
assert.equal(api.fontOf(13), "var(--mneme-type-13)");
|
|
213
|
+
assert.equal(api.weightOf(600), "var(--mneme-weight-600)");
|
|
214
|
+
for (const bad of [9, 11, 14, 25, "8px", "8 ", null, undefined]) {
|
|
215
|
+
assert.throws(() => api.space(bad), /只收刻度/, `space(${JSON.stringify(bad)}) 必须抛错`);
|
|
216
|
+
}
|
|
217
|
+
assert.throws(() => api.radius(6), /只收刻度/, "radius 不共享 space 的档位");
|
|
218
|
+
assert.throws(() => api.space("16"), /只收刻度/, "字符串键同样要查表(16 不是档位)");
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
test("原语真的把刻度写进 style,且默认值都在刻度内", () => {
|
|
222
|
+
const api = makeApi();
|
|
223
|
+
const stack = api.Stack({ gap: 12, pad: 8, align: "center", children: "x" });
|
|
224
|
+
assert.equal(stack.props.style.gap, "var(--mneme-space-12)");
|
|
225
|
+
assert.equal(stack.props.style.padding, "var(--mneme-space-8)");
|
|
226
|
+
assert.equal(stack.props.style.flexDirection, "column");
|
|
227
|
+
assert.throws(() => api.Stack({ gap: 9 }), /只收刻度/);
|
|
228
|
+
|
|
229
|
+
assert.equal(api.Inline({}).props.style.gap, "var(--mneme-space-6)");
|
|
230
|
+
assert.equal(api.Inline({}).props.style.flexDirection, "row");
|
|
231
|
+
assert.equal(api.Card({}).props.style.padding, "var(--mneme-space-12)");
|
|
232
|
+
assert.equal(api.Card({ dense: true }).props.style.padding, "var(--mneme-space-8)");
|
|
233
|
+
assert.equal(api.Card({}).props.style.borderRadius, "var(--mneme-radius-8)");
|
|
234
|
+
assert.match(String(api.Grid({}).props.style.gridTemplateColumns), /^repeat\(auto-fill,minmax\(220px,1fr\)\)$/);
|
|
235
|
+
assert.equal(api.Grid({ gap: 16 }).props.style.gap, "var(--mneme-space-16)");
|
|
236
|
+
assert.throws(() => api.Grid({ gap: 11 }), /只收刻度/);
|
|
237
|
+
assert.equal(api.Section({}).props.style.gap, "var(--mneme-space-8)");
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
test("原语层内部不许出现裸 px 字面量(1px 发丝线与栅格下限除外)", () => {
|
|
241
|
+
// 两个例外写在这里而不是放任:1px 是发丝线(缩放后仍是线),栅格 min 是
|
|
242
|
+
// 「内容多宽才排下一列」的阈值,不是间距——它进刻度反而会让栅格跟着间距漂。
|
|
243
|
+
const literal = (geometrySource.match(/(?<![\w.-])\d+px/g) || []).filter((v) => v !== "1px");
|
|
244
|
+
assert.deepEqual(literal, [], "原语层写了 px 字面量,绕过了刻度:" + literal.join(", "));
|
|
245
|
+
});
|