@a9i5k4/dsh-auto-memory 3.1.5 → 3.1.6

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.
Files changed (92) hide show
  1. package/docs/CONTINUITY-FLOW.md +6 -6
  2. package/docs/HANDBOOK.md +54 -53
  3. package/docs/M-CM-PLAN.md +1 -1
  4. package/docs/M-CM-STATE.md +1 -1
  5. package/docs/M-CM7-HANDOFF-LAYERED-RETRIEVAL.md +1 -1
  6. package/docs/M3B-CONTRACT.md +7 -7
  7. package/docs/M7-AUTONOMOUS-STATE.md +1 -1
  8. package/docs/M7-TASKSET-DISPATCH.md +1 -1
  9. package/docs/PROMPT-PACK-LAYERED-RECALL.md +3 -3
  10. package/docs/PROMPT-SET-STRICT.md +3 -3
  11. package/docs/RELEASE-GO-NOGO.md +1 -1
  12. package/docs/STATUS-BOARD.md +1 -1
  13. package/docs/USER-GUIDE.en.md +21 -21
  14. package/docs/USER-GUIDE.zh-CN.md +21 -21
  15. package/docs/WHITEPAPER.md +1 -1
  16. package/docs/prompts/FIX-AGENT-P12-FULL-REGRESSION.md +1 -1
  17. package/docs/prompts/LIVE-VERIFY-ZCODE.md +2 -2
  18. package/docs/prompts/P2-semantic-recall.md +2 -2
  19. package/docs/prompts/P7-write-fix.md +1 -1
  20. package/docs/prompts/ZCODE-DROPIN.md +1 -1
  21. package/lib/acceptance.js +6 -6
  22. package/lib/activation-host.js +15 -14
  23. package/lib/activation-inbox-state.js +4 -4
  24. package/lib/activation-inbox.js +44 -44
  25. package/lib/board-mode.js +3 -3
  26. package/lib/client.js +193 -34
  27. package/lib/config-io.js +12 -12
  28. package/lib/context-bridge.js +22 -22
  29. package/lib/context-host.js +24 -23
  30. package/lib/datadir.js +95 -0
  31. package/lib/degrade.js +21 -21
  32. package/lib/dsh-home.js +8 -8
  33. package/lib/engine-identity.js +12 -12
  34. package/lib/engine-switch.js +3 -3
  35. package/lib/episodic-store.js +13 -13
  36. package/lib/evidence-agg.js +4 -4
  37. package/lib/evidence-store.js +12 -12
  38. package/lib/fact-store.js +47 -47
  39. package/lib/handoff-anchor.js +4 -4
  40. package/lib/hub-io.js +356 -3
  41. package/lib/index-sync.js +5 -5
  42. package/lib/index.js +381 -236
  43. package/lib/intent-clean-safe.js +3 -3
  44. package/lib/intent-clean.js +1 -1
  45. package/lib/l0-extract.js +15 -15
  46. package/lib/l0-index-sync.js +8 -8
  47. package/lib/l0-index.js +11 -11
  48. package/lib/ledger-criteria.js +8 -8
  49. package/lib/m4-corpus.js +1 -1
  50. package/lib/m7-index-sync-host.js +2 -2
  51. package/lib/m7-wire.js +24 -24
  52. package/lib/memory-anchor.js +1 -1
  53. package/lib/memory-envelope.js +15 -15
  54. package/lib/memory-hub.js +95 -50
  55. package/lib/memory-importance.js +7 -7
  56. package/lib/memory-mutation.js +5 -5
  57. package/lib/note-status-apply.js +2 -2
  58. package/lib/note-status.js +7 -7
  59. package/lib/policies/activation_policy_v2.json +2 -2
  60. package/lib/policies/recall_intent_lr_v1.json +1 -1
  61. package/lib/procedure-observation.js +2 -2
  62. package/lib/procedure-store.js +117 -28
  63. package/lib/python-setup.js +6 -5
  64. package/lib/python-sidecar-client.js +26 -26
  65. package/lib/recall-fusion.js +12 -12
  66. package/lib/recall-stats.js +36 -1
  67. package/lib/rerank-host.js +11 -11
  68. package/lib/rules-edit.js +9 -9
  69. package/lib/rules-layer.js +26 -26
  70. package/lib/semantic-decide.js +7 -7
  71. package/lib/semantic-js.js +13 -13
  72. package/lib/shadow-host.js +7 -6
  73. package/lib/shadow-retrieval.js +40 -40
  74. package/lib/skill-export-host.js +7 -7
  75. package/lib/skill-export.js +6 -6
  76. package/lib/state-commit.js +10 -10
  77. package/lib/storage-manage.js +6 -6
  78. package/lib/tier-layer-inject.js +28 -28
  79. package/lib/tier0-catalog.js +4 -4
  80. package/lib/wb-contract.js +45 -45
  81. package/lib/wb-sidecar.js +11 -11
  82. package/package.json +3 -4
  83. package/python/m7_activation_features_v2.py +7 -7
  84. package/python/m7_embedding_v1.py +12 -12
  85. package/python/policies/activation_policy_v2.json +2 -2
  86. package/python/policies/decision-record-activation-v2-delta-exp-override-20260824.json +1 -1
  87. package/python/policies/decision-record-reasoning-kind-admission-20260826.json +2 -2
  88. package/python/policies/decision-record-stale-gate-per-candidate-20260825.json +2 -2
  89. package/python/policies/recall_intent_lr_v1.json +1 -1
  90. package/python/verify_policy_artifact.py +3 -3
  91. package/python/worker_semantic_v1.py +18 -18
  92. package/python/worker_v1.py +18 -18
@@ -4,12 +4,12 @@
4
4
  * 不写 audit、不产生任何模型可见副作用(M4 唯一模型行为=不影响模型行为)。
5
5
  *
6
6
  * 组成:
7
- * 1) 策略常量(SHADOW_GATE_POLICY_V1 / SHADOW_LEXICAL_BUDGET_V1 / 版本化词典)
7
+ * 1) 策略常量(SHADOW_GATE_POLICY_PRE_V1 / SHADOW_LEXICAL_BUDGET_PRE_V1 / 版本化词典)
8
8
  * 2) RetrievalContextSnapshot validator + 快照纯校验
9
- * 3) memoryIndexVersion(canonical corpus tuples → idx_ + sha256)
10
- * 4) GateSignals/GateDecision + gate_v1(硬抑制顺序 + 权重滞回 + cooldown)
9
+ * 3) memoryIndexVersion(canonical corpus tuples → idx_pre_ + sha256)
10
+ * 4) GateSignals/GateDecision + gate_pre_v1(硬抑制顺序 + 权重滞回 + cooldown)
11
11
  * 5) 确定性 tokenizer(NFKC/lowercase/词形保留/CJK 2-gram/stop words) + QueryPlan + queryDigest
12
- * 6) lexical_v1(term/heading coverage、phrase、recency、total、排序/去重/预算/drop)
12
+ * 6) lexical_pre_v1(term/heading coverage、phrase、recency、total、排序/去重/预算/drop)
13
13
  * 7) Candidate 身份(retrievalId/candidateId 确定性) + ShadowCandidate 构造
14
14
  * 8) replay pure core(canonical 结果排除 recordedAt/latency/runtimeTag)
15
15
  * 全部函数对同输入逐字段确定;所有新增文本 UTF-8 无 BOM。
@@ -21,16 +21,16 @@ const sha256Str = (s) => sha256Hex(Buffer.from(String(s), 'utf8'))
21
21
  const first32 = (h) => h.slice(0, 32)
22
22
  const clamp01 = (x) => Math.max(0, Math.min(1, Number(x) || 0))
23
23
 
24
- export const STOPWORDS_HIT_V2 = Object.freeze(['一一', 'sub', 'exp', 'sup', 'Lex', '第二', '一番', '一直', '一个', '一些', '许多', '种', '有的是', '也就是说', '啊', '阿', '哎', '哎呀', '哎哟', '唉', '俺', '俺们', '按', '按照', '吧', '吧哒', '把', '罢了', '被', '本', '本着', '比', '比方', '比如', '鄙人', '彼', '彼此', '边', '别', '别的', '别说', '并', '并且', '不比', '不成', '不单', '不但', '不独', '不管', '不光', '不过', '不仅', '不拘', '不论', '不怕', '不然', '不如', '不特', '不惟', '不问', '不只', '朝', '朝着', '趁', '趁着', '乘', '冲', '除', '除此之外', '除非', '除了', '此', '此间', '此外', '从', '从而', '打', '待', '但', '但是', '当', '当着', '到', '得', '的', '的话', '等', '等等', '地', '第', '叮咚', '对', '对于', '多', '多少', '而', '而况', '而且', '而是', '而外', '而言', '而已', '尔后', '反过来', '反过来说', '反之', '非但', '非徒', '否则', '嘎', '嘎登', '该', '赶', '个', '各', '各个', '各位', '各种', '各自', '给', '根据', '跟', '故', '故此', '固然', '关于', '管', '归', '果然', '果真', '过', '哈', '哈哈', '呵', '和', '何', '何处', '何况', '何时', '嘿', '哼', '哼唷', '呼哧', '乎', '哗', '还是', '还有', '换句话说', '换言之', '或', '或是', '或者', '极了', '及', '及其', '及至', '即', '即便', '即或', '即令', '即若', '即使', '几', '几时', '己', '既', '既然', '既是', '继而', '加之', '假如', '假若', '假使', '鉴于', '将', '较', '较之', '叫', '接着', '结果', '借', '紧接着', '进而', '尽', '尽管', '经', '经过', '就', '就是', '就是说', '据', '具体地说', '具体说来', '开始', '开外', '靠', '咳', '可', '可见', '可是', '可以', '况且', '啦', '来', '来着', '离', '例如', '哩', '连', '连同', '两者', '了', '临', '另', '另外', '另一方面', '论', '嘛', '吗', '慢说', '漫说', '冒', '么', '每', '每当', '们', '莫若', '某', '某个', '某些', '拿', '哪', '哪边', '哪儿', '哪个', '哪里', '哪年', '哪怕', '哪天', '哪些', '哪样', '那', '那边', '那儿', '那个', '那会儿', '那里', '那么', '那么些', '那么样', '那时', '那些', '那样', '乃', '乃至', '呢', '能', '你', '你们', '您', '宁', '宁可', '宁肯', '宁愿', '哦', '呕', '啪达', '旁人', '呸', '凭', '凭借', '其', '其次', '其二', '其他', '其它', '其一', '其余', '其中', '起', '起见', '岂但', '恰恰相反', '前后', '前者', '且', '然而', '然后', '然则', '让', '人家', '任', '任何', '任凭', '如', '如此', '如果', '如何', '如其', '如若', '如上所述', '若', '若非', '若是', '啥', '上下', '尚且', '设若', '设使', '甚而', '甚么', '甚至', '省得', '时候', '什么', '什么样', '使得', '是', '是的', '首先', '谁', '谁知', '顺', '顺着', '似的', '虽', '虽然', '虽说', '虽则', '随', '随着', '所', '所以', '他', '他们', '他人', '它', '它们', '她', '她们', '倘', '倘或', '倘然', '倘若', '倘使', '腾', '替', '通过', '同', '同时', '哇', '万一', '往', '望', '为', '为何', '为了', '为什么', '为着', '喂', '嗡嗡', '我', '我们', '呜', '呜呼', '乌乎', '无论', '无宁', '毋宁', '嘻', '吓', '相对而言', '像', '向', '向着', '嘘', '呀', '焉', '沿', '沿着', '要', '要不', '要不然', '要不是', '要么', '要是', '也', '也罢', '也好', '一', '一般', '一旦', '一方面', '一来', '一切', '一样', '一则', '依', '依照', '矣', '以', '以便', '以及', '以免', '以至', '以至于', '以致', '抑或', '因', '因此', '因而', '因为', '哟', '用', '由', '由此可见', '由于', '有', '有的', '有关', '有些', '又', '于', '于是', '于是乎', '与', '与此同时', '与否', '与其', '越是', '云云', '哉', '再说', '再者', '在', '在下', '咱', '咱们', '则', '怎', '怎么', '怎么办', '怎么样', '怎样', '咋', '照', '照着', '者', '这', '这边', '这儿', '这个', '这会儿', '这就是说', '这里', '这么', '这么点儿', '这么些', '这么样', '这时', '这些', '这样', '正如', '吱', '之', '之类', '之所以', '之一', '只是', '只限', '只要', '只有', '至', '至于', '诸位', '着', '着呢', '自', '自从', '自个儿', '自各儿', '自己', '自家', '自身', '综上所述', '总的来看', '总的来说', '总的说来', '总而言之', '总之', '纵', '纵令', '纵然', '纵使', '遵照', '作为', '兮', '呃', '呗', '咚', '咦', '喏', '啐', '喔唷', '嗬', '嗯', '嗳']);
25
- export const GATE_POLICY_VERSION = 'gate_v1'
26
- export const LEXICAL_POLICY_VERSION = 'lexical_v2'
27
- export const INDEX_PREFIX = 'idx_'
28
- export const RETRIEVAL_PREFIX = 'ret_'
29
- export const CANDIDATE_PREFIX = 'cand_'
30
- export const NAMESPACE = 'dsh-auto-memory'
24
+ export const STOPWORDS_HIT_PRE_V2 = Object.freeze(['一一', 'sub', 'exp', 'sup', 'Lex', '第二', '一番', '一直', '一个', '一些', '许多', '种', '有的是', '也就是说', '啊', '阿', '哎', '哎呀', '哎哟', '唉', '俺', '俺们', '按', '按照', '吧', '吧哒', '把', '罢了', '被', '本', '本着', '比', '比方', '比如', '鄙人', '彼', '彼此', '边', '别', '别的', '别说', '并', '并且', '不比', '不成', '不单', '不但', '不独', '不管', '不光', '不过', '不仅', '不拘', '不论', '不怕', '不然', '不如', '不特', '不惟', '不问', '不只', '朝', '朝着', '趁', '趁着', '乘', '冲', '除', '除此之外', '除非', '除了', '此', '此间', '此外', '从', '从而', '打', '待', '但', '但是', '当', '当着', '到', '得', '的', '的话', '等', '等等', '地', '第', '叮咚', '对', '对于', '多', '多少', '而', '而况', '而且', '而是', '而外', '而言', '而已', '尔后', '反过来', '反过来说', '反之', '非但', '非徒', '否则', '嘎', '嘎登', '该', '赶', '个', '各', '各个', '各位', '各种', '各自', '给', '根据', '跟', '故', '故此', '固然', '关于', '管', '归', '果然', '果真', '过', '哈', '哈哈', '呵', '和', '何', '何处', '何况', '何时', '嘿', '哼', '哼唷', '呼哧', '乎', '哗', '还是', '还有', '换句话说', '换言之', '或', '或是', '或者', '极了', '及', '及其', '及至', '即', '即便', '即或', '即令', '即若', '即使', '几', '几时', '己', '既', '既然', '既是', '继而', '加之', '假如', '假若', '假使', '鉴于', '将', '较', '较之', '叫', '接着', '结果', '借', '紧接着', '进而', '尽', '尽管', '经', '经过', '就', '就是', '就是说', '据', '具体地说', '具体说来', '开始', '开外', '靠', '咳', '可', '可见', '可是', '可以', '况且', '啦', '来', '来着', '离', '例如', '哩', '连', '连同', '两者', '了', '临', '另', '另外', '另一方面', '论', '嘛', '吗', '慢说', '漫说', '冒', '么', '每', '每当', '们', '莫若', '某', '某个', '某些', '拿', '哪', '哪边', '哪儿', '哪个', '哪里', '哪年', '哪怕', '哪天', '哪些', '哪样', '那', '那边', '那儿', '那个', '那会儿', '那里', '那么', '那么些', '那么样', '那时', '那些', '那样', '乃', '乃至', '呢', '能', '你', '你们', '您', '宁', '宁可', '宁肯', '宁愿', '哦', '呕', '啪达', '旁人', '呸', '凭', '凭借', '其', '其次', '其二', '其他', '其它', '其一', '其余', '其中', '起', '起见', '岂但', '恰恰相反', '前后', '前者', '且', '然而', '然后', '然则', '让', '人家', '任', '任何', '任凭', '如', '如此', '如果', '如何', '如其', '如若', '如上所述', '若', '若非', '若是', '啥', '上下', '尚且', '设若', '设使', '甚而', '甚么', '甚至', '省得', '时候', '什么', '什么样', '使得', '是', '是的', '首先', '谁', '谁知', '顺', '顺着', '似的', '虽', '虽然', '虽说', '虽则', '随', '随着', '所', '所以', '他', '他们', '他人', '它', '它们', '她', '她们', '倘', '倘或', '倘然', '倘若', '倘使', '腾', '替', '通过', '同', '同时', '哇', '万一', '往', '望', '为', '为何', '为了', '为什么', '为着', '喂', '嗡嗡', '我', '我们', '呜', '呜呼', '乌乎', '无论', '无宁', '毋宁', '嘻', '吓', '相对而言', '像', '向', '向着', '嘘', '呀', '焉', '沿', '沿着', '要', '要不', '要不然', '要不是', '要么', '要是', '也', '也罢', '也好', '一', '一般', '一旦', '一方面', '一来', '一切', '一样', '一则', '依', '依照', '矣', '以', '以便', '以及', '以免', '以至', '以至于', '以致', '抑或', '因', '因此', '因而', '因为', '哟', '用', '由', '由此可见', '由于', '有', '有的', '有关', '有些', '又', '于', '于是', '于是乎', '与', '与此同时', '与否', '与其', '越是', '云云', '哉', '再说', '再者', '在', '在下', '咱', '咱们', '则', '怎', '怎么', '怎么办', '怎么样', '怎样', '咋', '照', '照着', '者', '这', '这边', '这儿', '这个', '这会儿', '这就是说', '这里', '这么', '这么点儿', '这么些', '这么样', '这时', '这些', '这样', '正如', '吱', '之', '之类', '之所以', '之一', '只是', '只限', '只要', '只有', '至', '至于', '诸位', '着', '着呢', '自', '自从', '自个儿', '自各儿', '自己', '自家', '自身', '综上所述', '总的来看', '总的来说', '总的说来', '总而言之', '总之', '纵', '纵令', '纵然', '纵使', '遵照', '作为', '兮', '呃', '呗', '咚', '咦', '喏', '啐', '喔唷', '嗬', '嗯', '嗳']);
25
+ export const GATE_POLICY_VERSION = 'gate_pre_v1'
26
+ export const LEXICAL_POLICY_VERSION = 'lexical_pre_v2'
27
+ export const INDEX_PREFIX = 'idx_pre_'
28
+ export const RETRIEVAL_PREFIX = 'ret_pre_'
29
+ export const CANDIDATE_PREFIX = 'cand_pre_'
30
+ export const NAMESPACE = 'dsh-auto-memory-pre'
31
31
 
32
32
  /** §9.4 冻结权重(变更必须升级 policyVersion)。 */
33
- export const SHADOW_GATE_POLICY_V1 = Object.freeze({
33
+ export const SHADOW_GATE_POLICY_PRE_V1 = Object.freeze({
34
34
  schemaVersion: 1,
35
35
  policyVersion: GATE_POLICY_VERSION,
36
36
  weights: Object.freeze({
@@ -54,14 +54,14 @@ export const SHADOW_GATE_POLICY_V1 = Object.freeze({
54
54
  stopWords: Object.freeze(['的', '了', '是', '和', '与', '及', '在', '我', '你', '它', '这', '那', '就', '都', '也', 'the', 'a', 'an', 'and', 'or', 'of', 'to', 'in', 'on', 'for', 'with', 'is', 'are', 'was', 'were', 'be', 'it', 'this', 'that']),
55
55
  // §6:inputSource plugin allowlist(v1 为空=不按 inputSource 归类 plugin-generated;仅 sourcePlugin 非空才判定)
56
56
  pluginInputAllowlist: Object.freeze([]),
57
- // lexical_v2:BM25 参数(Lucene 经典默认)+停用词来源
57
+ // lexical_pre_v2:BM25 参数(Lucene 经典默认)+停用词来源
58
58
  bm25: Object.freeze({ k1: 1.2, b: 0.75 }),
59
59
  stopwordsSource: 'hit_stopwords.txt (github leiyusi123/stopwords), filtered',
60
60
  }),
61
61
  })
62
62
 
63
63
  /** §12.5 硬预算。 */
64
- export const SHADOW_LEXICAL_BUDGET_V1 = Object.freeze({
64
+ export const SHADOW_LEXICAL_BUDGET_PRE_V1 = Object.freeze({
65
65
  windowSegments: 8,
66
66
  windowChars: 4096,
67
67
  queryTerms: 32,
@@ -124,7 +124,7 @@ export function validateSnapshot(snap) {
124
124
  }
125
125
  if (!Array.isArray(snap.window)) problems.push('window')
126
126
  else {
127
- if (snap.window.length > SHADOW_LEXICAL_BUDGET_V1.windowSegments) problems.push('window-exceeds-8')
127
+ if (snap.window.length > SHADOW_LEXICAL_BUDGET_PRE_V1.windowSegments) problems.push('window-exceeds-8')
128
128
  let chars = 0
129
129
  for (const w of snap.window) {
130
130
  if (!w || typeof w !== 'object') { problems.push('window-entry'); continue }
@@ -135,13 +135,13 @@ export function validateSnapshot(snap) {
135
135
  if (!Number.isInteger(w.contextVersion)) problems.push('window.contextVersion')
136
136
  if (typeof w.ts !== 'number') problems.push('window.ts')
137
137
  }
138
- if (chars > SHADOW_LEXICAL_BUDGET_V1.windowChars) problems.push('window-exceeds-4096-chars')
138
+ if (chars > SHADOW_LEXICAL_BUDGET_PRE_V1.windowChars) problems.push('window-exceeds-4096-chars')
139
139
  }
140
140
  if (problems.length) return { ok: false, reason: 'invalid:' + problems.join(',') }
141
141
  return { ok: true, snapshot: snap }
142
142
  }
143
143
 
144
- /** §8 memoryIndexVersion:canonical corpus tuples → idx_ + first32hex(sha256)。 */
144
+ /** §8 memoryIndexVersion:canonical corpus tuples → idx_pre_ + first32hex(sha256)。 */
145
145
  export function memoryIndexVersion(sources) {
146
146
  // source tuple = [scope, sourceRef, sourceEpoch, sourceVersion, fileDigest]
147
147
  const tuples = (Array.isArray(sources) ? sources : []).map((s) => [
@@ -209,11 +209,11 @@ export function tokenize(text, opts = {}) {
209
209
  return out
210
210
  }
211
211
 
212
- /** 版本化 stop words 过滤(§10.2#5):lexical_v2 起用哈工大停用词表(STOPWORDS_HIT_V2,507 词)+原版本化小表兜底;错误码/文件名/长度>1 的标识符不可移除。 */
213
- const STOPWORDS_HIT_SET = new Set(STOPWORDS_HIT_V2)
212
+ /** 版本化 stop words 过滤(§10.2#5):lexical_pre_v2 起用哈工大停用词表(STOPWORDS_HIT_PRE_V2,507 词)+原版本化小表兜底;错误码/文件名/长度>1 的标识符不可移除。 */
213
+ const STOPWORDS_HIT_SET = new Set(STOPWORDS_HIT_PRE_V2)
214
214
  export function isStopWord(term) {
215
215
  if (STOPWORDS_HIT_SET.has(term)) return true
216
- const d = SHADOW_GATE_POLICY_V1.dictionaries
216
+ const d = SHADOW_GATE_POLICY_PRE_V1.dictionaries
217
217
  return d.stopWords.includes(term)
218
218
  }
219
219
 
@@ -235,7 +235,7 @@ export function buildQueryPlan(snapshot, opts = {}) {
235
235
  if (isStopWord(t)) continue
236
236
  const cur = seenTerms.get(t)
237
237
  const wgt = Math.max(cur ? cur.weight : 0, weights[origin] || 0)
238
- if (Buffer.byteLength(t, 'utf8') > SHADOW_LEXICAL_BUDGET_V1.termBytes) {
238
+ if (Buffer.byteLength(t, 'utf8') > SHADOW_LEXICAL_BUDGET_PRE_V1.termBytes) {
239
239
  oversize.push(t)
240
240
  continue
241
241
  }
@@ -245,14 +245,14 @@ export function buildQueryPlan(snapshot, opts = {}) {
245
245
  // P12 排序:weight 降序优先(截断保高权重词),term 字典序升序作稳定 tiebreak;截断逻辑与 truncated 标记不变
246
246
  const terms = [...seenTerms.values()].sort((a, b) => (b.weight - a.weight) || (a.term < b.term ? -1 : a.term > b.term ? 1 : 0))
247
247
  let truncated = false
248
- if (terms.length > SHADOW_LEXICAL_BUDGET_V1.queryTerms) {
249
- terms.length = SHADOW_LEXICAL_BUDGET_V1.queryTerms
248
+ if (terms.length > SHADOW_LEXICAL_BUDGET_PRE_V1.queryTerms) {
249
+ terms.length = SHADOW_LEXICAL_BUDGET_PRE_V1.queryTerms
250
250
  truncated = true
251
251
  }
252
252
  for (const t of terms) termBytes += Buffer.byteLength(t.term, 'utf8')
253
- if (termBytes > SHADOW_LEXICAL_BUDGET_V1.queryBytes) {
253
+ if (termBytes > SHADOW_LEXICAL_BUDGET_PRE_V1.queryBytes) {
254
254
  // 从尾部丢弃直到 ≤2048
255
- while (termBytes > SHADOW_LEXICAL_BUDGET_V1.queryBytes && terms.length) {
255
+ while (termBytes > SHADOW_LEXICAL_BUDGET_PRE_V1.queryBytes && terms.length) {
256
256
  termBytes -= Buffer.byteLength(terms.pop().term, 'utf8')
257
257
  truncated = true
258
258
  }
@@ -278,7 +278,7 @@ export function buildQueryPlan(snapshot, opts = {}) {
278
278
 
279
279
  // ========== §9 Gate ==========
280
280
 
281
- const D = () => SHADOW_GATE_POLICY_V1.dictionaries
281
+ const D = () => SHADOW_GATE_POLICY_PRE_V1.dictionaries
282
282
 
283
283
  function hasAny(text, list) {
284
284
  const norm = normalizeText(text)
@@ -344,9 +344,9 @@ export function computeSignals(snapshot, queryPlan, recentHits = []) {
344
344
  return signals
345
345
  }
346
346
 
347
- /** §9.4 gate_v1:硬抑制 → 权重滞回 → 决策。同步纯函数,零 IO。 */
347
+ /** §9.4 gate_pre_v1:硬抑制 → 权重滞回 → 决策。同步纯函数,零 IO。 */
348
348
  export function gatePreV1(snapshot, opts = {}) {
349
- const policy = SHADOW_GATE_POLICY_V1
349
+ const policy = SHADOW_GATE_POLICY_PRE_V1
350
350
  const latchedPrev = opts.previousLatch === true
351
351
  const cooldownRemaining = Math.max(0, Number(opts.cooldownRemaining) || 0)
352
352
  const hard = opts.hardSuppress || {}
@@ -396,20 +396,20 @@ export function gatePreV1(snapshot, opts = {}) {
396
396
 
397
397
 
398
398
 
399
- // ========== §11 Candidate 身份 + §12 lexical_v1 ==========
399
+ // ========== §11 Candidate 身份 + §12 lexical_pre_v1 ==========
400
400
 
401
401
  const MEMORY_ID_STRICT = /^mem_[0-9a-f]{32}$/
402
402
 
403
403
  /** §11 retrievalId(确定性,可重放;非长期内容身份)。 */
404
404
  export function buildRetrievalId(sessionId, contextVersion, triggerSegmentId, memoryIndexVersion) {
405
- const sessionIdHash = first32(sha256Str('retrieval-v1\u0000' + sessionId))
406
- const parts = ['retrieval-v1', sessionIdHash, contextVersion, triggerSegmentId, memoryIndexVersion, GATE_POLICY_VERSION, LEXICAL_POLICY_VERSION]
405
+ const sessionIdHash = first32(sha256Str('retrieval-pre-v1\u0000' + sessionId))
406
+ const parts = ['retrieval-pre-v1', sessionIdHash, contextVersion, triggerSegmentId, memoryIndexVersion, GATE_POLICY_VERSION, LEXICAL_POLICY_VERSION]
407
407
  return RETRIEVAL_PREFIX + first32(sha256Str(JSON.stringify(parts)))
408
408
  }
409
409
 
410
410
  /** §11 candidateId:同 memoryId 内容变化后 candidateId 因版本/digest 改变。 */
411
411
  export function buildCandidateId(retrievalId, memoryId, sourceEpoch, sourceVersion, recordDigest) {
412
- const parts = ['candidate-v1', retrievalId, memoryId, sourceEpoch, sourceVersion, recordDigest]
412
+ const parts = ['candidate-pre-v1', retrievalId, memoryId, sourceEpoch, sourceVersion, recordDigest]
413
413
  return CANDIDATE_PREFIX + first32(sha256Str(JSON.stringify(parts)))
414
414
  }
415
415
 
@@ -450,18 +450,18 @@ function phraseHit(normText, normPhrase) {
450
450
  export function sanitizeExcerpt(text) {
451
451
  const cleaned = String(text == null ? '' : text).replace(/[\u0000-\u0008\u000B\u000C\u000E-\u001F]/g, '')
452
452
  const buf = Buffer.from(cleaned, 'utf8')
453
- if (buf.length <= SHADOW_LEXICAL_BUDGET_V1.excerptBytes) return cleaned
454
- return buf.subarray(0, SHADOW_LEXICAL_BUDGET_V1.excerptBytes).toString('utf8').replace(/[\uFFFD]+$/, '')
453
+ if (buf.length <= SHADOW_LEXICAL_BUDGET_PRE_V1.excerptBytes) return cleaned
454
+ return buf.subarray(0, SHADOW_LEXICAL_BUDGET_PRE_V1.excerptBytes).toString('utf8').replace(/[\uFFFD]+$/, '')
455
455
  }
456
456
 
457
457
  /**
458
- * lexical_v1 检索核心(§10-§13):纯函数,零 IO。
458
+ * lexical_pre_v1 检索核心(§10-§13):纯函数,零 IO。
459
459
  * @param {{memoryIndexVersion?:string,sources:Array,records:Array}} corpus 纯内存 fixture
460
460
  * @param {object} queryPlan buildQueryPlan 输出
461
461
  * @param {{triggerTs?:number,mode?:'retrieve'|'prefetch',dayBoundaryMinutes?:number}} opts
462
462
  */
463
463
  export function lexicalSearch(corpus, queryPlan, opts = {}) {
464
- const B = SHADOW_LEXICAL_BUDGET_V1
464
+ const B = SHADOW_LEXICAL_BUDGET_PRE_V1
465
465
  const dropped = []
466
466
  const drop = (stage, reason, extra) => dropped.push(Object.assign({ stage, reason }, extra || {}))
467
467
  const records = Array.isArray(corpus && corpus.records) ? corpus.records : []
@@ -478,8 +478,8 @@ export function lexicalSearch(corpus, queryPlan, opts = {}) {
478
478
  const termTotalWeight = queryPlan.terms.reduce((a, t) => a + t.weight, 0) || 1
479
479
  const normPhrases = (queryPlan.phrases || []).map((p) => normalizeText(p))
480
480
 
481
- // lexical_v2:BM25 语料统计(df/avgdl;Okapi 经典参数 k1=1.2 b=0.75)
482
- const bm25 = SHADOW_GATE_POLICY_V1.dictionaries.bm25 || { k1: 1.2, b: 0.75 }
481
+ // lexical_pre_v2:BM25 语料统计(df/avgdl;Okapi 经典参数 k1=1.2 b=0.75)
482
+ const bm25 = SHADOW_GATE_POLICY_PRE_V1.dictionaries.bm25 || { k1: 1.2, b: 0.75 }
483
483
  let totalTokens = 0
484
484
  const docTokensList = []
485
485
  const DF = new Map() // term → 出现该词的记录数(document frequency)
@@ -504,7 +504,7 @@ export function lexicalSearch(corpus, queryPlan, opts = {}) {
504
504
  }
505
505
  const normHeading = normalizeText(rec.heading || '')
506
506
  const normBody = normalizeText(text)
507
- // lexical_v2:BM25 覆盖率——idf 加权(稀有词贡献大)+ tf 饱和(k1)+ 文档长度归一(b)
507
+ // lexical_pre_v2:BM25 覆盖率——idf 加权(稀有词贡献大)+ tf 饱和(k1)+ 文档长度归一(b)
508
508
  const docTokens = tokenize(normHeading + ' ' + normBody)
509
509
  const tfMap = new Map()
510
510
  for (const tk of docTokens) tfMap.set(tk, (tfMap.get(tk) || 0) + 1)
@@ -667,7 +667,7 @@ export function replay({ contextSnapshots, corpusSnapshot, gatePolicy, lexicalPo
667
667
  },
668
668
  })
669
669
  latch = dec.latched
670
- cooldown = dec.action === 'retrieve' ? SHADOW_GATE_POLICY_V1.cooldownSegments : Math.max(0, cooldown - 1)
670
+ cooldown = dec.action === 'retrieve' ? SHADOW_GATE_POLICY_PRE_V1.cooldownSegments : Math.max(0, cooldown - 1)
671
671
  }
672
672
  return results
673
673
  }
@@ -20,9 +20,9 @@ import { join } from 'node:path'
20
20
  import { renderSkillMarkdownPre, validateSkillMarkdownPre } from './skill-export.js'
21
21
 
22
22
  /** 导出目录前缀(用于识别"这是本插件的产物",避免误覆盖用户手写技能)。 */
23
- export const SKILL_EXPORT_PREFIX_V1 = 'mem-skill-'
23
+ export const SKILL_EXPORT_PREFIX_PRE_V1 = 'mem-skill-'
24
24
  /** 自检标记:出现在 SKILL.md 里即证明是本插件导出物。 */
25
- export const SKILL_EXPORT_STAMP_V1 = 'mem-skill-export-v1'
25
+ export const SKILL_EXPORT_STAMP_PRE_V1 = 'mem-skill-export-pre-v1'
26
26
 
27
27
  /**
28
28
  * 解析技能导出根目录。
@@ -94,7 +94,7 @@ export function exportSkillForPre(procedure, opts = {}) {
94
94
  if (!r.ok) return { ok: false, reason: r.reason }
95
95
 
96
96
  // 自带标记 + 完整性自检(判据 = 用户 ⑪-1/⑪-3 的硬要求)
97
- const content = r.content + '\n<!-- ' + SKILL_EXPORT_STAMP_V1 + ' -->\n'
97
+ const content = r.content + '\n<!-- ' + SKILL_EXPORT_STAMP_PRE_V1 + ' -->\n'
98
98
  const v = validateSkillMarkdownPre(content, { projectName, hasPrograms: programs.length > 0 })
99
99
  if (!v.ok) return { ok: false, reason: 'notice-incomplete:' + v.problems.join('|') }
100
100
 
@@ -103,8 +103,8 @@ export function exportSkillForPre(procedure, opts = {}) {
103
103
  if (existsSync(dir) && !opts.force) {
104
104
  const old = join(dir, 'SKILL.md')
105
105
  const prev = readIfExists(old)
106
- const isOurs = prev != null && prev.includes(SKILL_EXPORT_STAMP_V1)
107
- const isOursByName = r.dirName.startsWith(SKILL_EXPORT_PREFIX_V1)
106
+ const isOurs = prev != null && prev.includes(SKILL_EXPORT_STAMP_PRE_V1)
107
+ const isOursByName = r.dirName.startsWith(SKILL_EXPORT_PREFIX_PRE_V1)
108
108
  if (!isOurs && isOursByName) {
109
109
  // 按名字是我们的,但内容不是 ⇒ 用户手写过,别动
110
110
  if (prev != null) return { ok: false, reason: 'dir-occupied-by-foreign' }
@@ -132,11 +132,11 @@ export function listExportedSkillsPre(skillsRoot) {
132
132
  let entries = []
133
133
  try { entries = readdirSync(root) } catch (_) { return [] }
134
134
  for (const name of entries) {
135
- if (!name.startsWith(SKILL_EXPORT_PREFIX_V1)) continue
135
+ if (!name.startsWith(SKILL_EXPORT_PREFIX_PRE_V1)) continue
136
136
  const dir = join(root, name)
137
137
  try { if (!statSync(dir).isDirectory()) continue } catch (_) { continue }
138
138
  const prev = readIfExists(join(dir, 'SKILL.md'))
139
- if (prev == null || !prev.includes(SKILL_EXPORT_STAMP_V1)) continue
139
+ if (prev == null || !prev.includes(SKILL_EXPORT_STAMP_PRE_V1)) continue
140
140
  const nameLine = /^name:\s*(.+)$/m.exec(prev)
141
141
  const projLine = /适用项目\*\*:`([^`]+)`/.exec(prev)
142
142
  out.push({
@@ -12,7 +12,7 @@
12
12
  * 本模块是**纯渲染层**(无 IO、无状态),便于直接单测:
13
13
  * - `renderSkillMarkdownPre(procedure, opts)` → SKILL.md 文本
14
14
  * - `skillDirNamePre(procedure)` → 目录名(稳定、可预测)
15
- * - `SKILL_USAGE_NOTICE_V1` → 使用约束条款(导出物必须原样包含)
15
+ * - `SKILL_USAGE_NOTICE_PRE_V1` → 使用约束条款(导出物必须原样包含)
16
16
  *
17
17
  * 设计纪律(与 procedure 引擎同源):
18
18
  * ① **不新增状态源** —— 导出物是 procedure 的**派生物**,不是新的事实来源;
@@ -25,7 +25,7 @@
25
25
  import { createHash } from 'node:crypto'
26
26
 
27
27
  /** 导出物的使用约束条款 —— **必须原样出现在每个 SKILL.md 中**(用户 ⑪-1 硬要求)。 */
28
- export const SKILL_USAGE_NOTICE_V1 = [
28
+ export const SKILL_USAGE_NOTICE_PRE_V1 = [
29
29
  '> **⚠️ 使用约束(必读)**',
30
30
  '>',
31
31
  '> 本技能由 **dsh-auto-memory** 从一次真实工作过程**自动沉淀**而来,**不是通用最佳实践**。',
@@ -40,7 +40,7 @@ export const SKILL_USAGE_NOTICE_V1 = [
40
40
  ].join('\n')
41
41
 
42
42
  /** SKILL.md 中必须出现的约束锚点(供套件断言,防止被后续改动悄悄删掉)。 */
43
- export const SKILL_NOTICE_ANCHORS_V1 = Object.freeze([
43
+ export const SKILL_NOTICE_ANCHORS_PRE_V1 = Object.freeze([
44
44
  '使用约束(必读)',
45
45
  '附带程序仅供参考',
46
46
  '只做迁移,不要直接运行',
@@ -76,7 +76,7 @@ const MAX_TITLE_LEN = 80
76
76
  */
77
77
  export function skillDirNamePre(procedure) {
78
78
  const id = String((procedure && procedure.procedureId) || '')
79
- const short = id.replace(/^proc_/, '').slice(0, 12)
79
+ const short = id.replace(/^proc_pre_/, '').slice(0, 12)
80
80
  const title = String((procedure && procedure.title) || '')
81
81
  const slug = title.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '').slice(0, 40)
82
82
  // 纯中文(或纯符号)标题:ASCII slug 为空 ⇒ 用标题哈希代替,绝不留 'untitled'
@@ -130,7 +130,7 @@ export function renderSkillMarkdownPre(procedure, opts = {}) {
130
130
  L.push('')
131
131
  L.push('# ' + String(p.title).trim())
132
132
  L.push('')
133
- L.push(SKILL_USAGE_NOTICE_V1)
133
+ L.push(SKILL_USAGE_NOTICE_PRE_V1)
134
134
  L.push('')
135
135
 
136
136
  // ── 来源(⑪-3:必须标注适用于哪个项目)──
@@ -224,7 +224,7 @@ export function validateSkillMarkdownPre(content, expect = {}) {
224
224
  const s = String(content == null ? '' : content)
225
225
  const problems = []
226
226
  if (!s.trim()) problems.push('empty')
227
- for (const a of SKILL_NOTICE_ANCHORS_V1) if (!s.includes(a)) problems.push('missing-notice:' + a)
227
+ for (const a of SKILL_NOTICE_ANCHORS_PRE_V1) if (!s.includes(a)) problems.push('missing-notice:' + a)
228
228
  if (!/^---\r?\n[\s\S]*?\r?\n---/.test(s)) problems.push('no-frontmatter')
229
229
  if (!/^name:\s*\S+/m.test(s)) problems.push('no-name')
230
230
  if (!/^description:\s*\S+/m.test(s)) problems.push('no-description')
@@ -1,5 +1,5 @@
1
1
  /**
2
- * 统一状态提交契约(state_commit_v1)—— P1 主体(2026-09-15)。
2
+ * 统一状态提交契约(state_commit_pre_v1)—— P1 主体(2026-09-15)。
3
3
  *
4
4
  * 依据:`docs/internal/DESIGN-P1-STATE-COMMIT-20260915.md`(已获用户批准)§2.1 / §2.2 / §2.3,
5
5
  * `MASTER-PLAN-3.0.md` Phase 1、`TODO-GRAPH.html` 卡 V2-P1。
@@ -25,10 +25,10 @@
25
25
  */
26
26
  import { createHash } from 'node:crypto'
27
27
 
28
- export const STATE_COMMIT_VERSION_PRE = 'state_commit_v1'
28
+ export const STATE_COMMIT_VERSION_PRE = 'state_commit_pre_v1'
29
29
 
30
- /** miv 前缀(与 `shadow-retrieval.js:144` 既有口径一致:`idx_` + first32hex)。 */
31
- export const MIV_PREFIX_PRE = 'idx_'
30
+ /** miv 前缀(与 `shadow-retrieval.js:144` 既有口径一致:`idx_pre_` + first32hex)。 */
31
+ export const MIV_PREFIX_PRE = 'idx_pre_'
32
32
 
33
33
  /**
34
34
  * 三种状态的**命名隔离**(卡内要求"三种状态不能混")。
@@ -40,10 +40,10 @@ export const TASK_STATE_PRE = Object.freeze(['open', 'done', 'passed'])
40
40
  export const ARCHIVE_STATE_PRE = Object.freeze(['active', 'archived'])
41
41
 
42
42
  /** 提交单据必填字段(缺任一 ⇒ fail-closed 拒绝,不猜测)。 */
43
- export const COMMIT_REQUIRED_V1 = Object.freeze(['workspaceKey', 'boardId', 'txId', 'actor'])
43
+ export const COMMIT_REQUIRED_PRE_V1 = Object.freeze(['workspaceKey', 'boardId', 'txId', 'actor'])
44
44
 
45
45
  /** 冲突/拒绝原因码 → 可读中文。 */
46
- export const COMMIT_REASONS_V1 = Object.freeze({
46
+ export const COMMIT_REASONS_PRE_V1 = Object.freeze({
47
47
  'not-object': '传入的不是对象',
48
48
  'missing-field': '提交单据缺必填字段',
49
49
  'invalid-writes': 'writes 形状非法(必须是数组)',
@@ -55,7 +55,7 @@ export const COMMIT_REASONS_V1 = Object.freeze({
55
55
  /** 原因码 → 可读中文(未知码原样返回)。 */
56
56
  export function describeCommitReasonPre(code) {
57
57
  const k = String(code == null ? '' : code)
58
- return COMMIT_REASONS_V1[k] || k || '未知原因'
58
+ return COMMIT_REASONS_PRE_V1[k] || k || '未知原因'
59
59
  }
60
60
 
61
61
  function asNonEmptyString(v) {
@@ -86,13 +86,13 @@ function sha256HexPre(text) {
86
86
  * 它**不替换**契约 §8 的 `shadow-retrieval.js:145 memoryIndexVersion(sources)` ——
87
87
  * 那个吃的是 **source tuples**(scope/sourceRef/epoch/version/fileDigest),是**建索引**侧的真源。
88
88
  * 两者**输入不同、用途不同**,不可互相替换;本函数是"提交边界侧"的口径,
89
- * 且**刻意保持与 §8 相同的前缀与长度**(`idx_` + 32 hex),使二者在外部看来同形。
89
+ * 且**刻意保持与 §8 相同的前缀与长度**(`idx_pre_` + 32 hex),使二者在外部看来同形。
90
90
  *
91
91
  * ⚠️ 本函数**不负责**收敛 `index.js:4188 tierCurrentMivPre()`(那处的缓存/指纹语义属 T0-2 已交付内容),
92
92
  * P1 不动它 —— 见设计稿 §2.2 边界说明。任何"用本函数替换 tier 自造版"的改动都**不在 P1 范围**。
93
93
  *
94
94
  * 输入:`{ records: [{id, status, l0?}], boardCards?: [{id, status}], scope? }`
95
- * 输出:`'idx_' + first32hex(sha256(canonical))`
95
+ * 输出:`'idx_pre_' + first32hex(sha256(canonical))`
96
96
  *
97
97
  * canonical 构成(按 id 升序,换行拼接,无尾随空白):
98
98
  * 每条 → `<id>\t<status>\t<contentDigest>`;`contentDigest` = 内容摘要(无内容时取空串)。
@@ -135,7 +135,7 @@ export function boardIdPre(workspaceKey, scope) {
135
135
  const ws = asNonEmptyString(workspaceKey)
136
136
  if (!ws) return null
137
137
  const sc = asNonEmptyString(scope) || 'Workspace'
138
- return 'board_' + sha256HexPre(ws + '|' + sc).slice(0, 24)
138
+ return 'board_pre_' + sha256HexPre(ws + '|' + sc).slice(0, 24)
139
139
  }
140
140
 
141
141
  /**
@@ -19,11 +19,11 @@
19
19
  import { buildSourceCatalog, loadCorpusSnapshot } from './m4-corpus.js'
20
20
  import { parseAnchors } from './memory-anchor.js'
21
21
 
22
- export const STORAGE_MANAGE_VERSION_V1 = 'storage_manage_v1'
22
+ export const STORAGE_MANAGE_VERSION_PRE_V1 = 'storage_manage_pre_v1'
23
23
  /** 审计环形缓冲上限(最小投影,不记正文)。 */
24
24
  const AUDIT_MAX = 64
25
25
  /** 可通过「重建 sidecar」自愈的失效分类(与 loadCorpusSnapshot 的 dropped.reason 对齐)。 */
26
- export const REPAIRABLE_REASONS_V1 = Object.freeze(['sidecar-missing', 'sidecar-invalid', 'stale-source', 'record-stale'])
26
+ export const REPAIRABLE_REASONS_PRE_V1 = Object.freeze(['sidecar-missing', 'sidecar-invalid', 'stale-source', 'record-stale'])
27
27
 
28
28
  export function createStorageManagerPre(opts = {}) {
29
29
  // #15 追加:docStore 必须活读——apply 同步阶段 memoryAnchorEnabled 尚未生效,常量解构会把
@@ -73,7 +73,7 @@ export function createStorageManagerPre(opts = {}) {
73
73
  status: 'ok', reasons: [], repairable: [],
74
74
  }))
75
75
  return {
76
- ok: true, indexEnabled: false, scannedAt: now(), version: STORAGE_MANAGE_VERSION_V1,
76
+ ok: true, indexEnabled: false, scannedAt: now(), version: STORAGE_MANAGE_VERSION_PRE_V1,
77
77
  sources, stale: [],
78
78
  counts: { total: sources.length, ok: sources.length, stale: 0, unrepairable: 0 },
79
79
  }
@@ -87,7 +87,7 @@ export function createStorageManagerPre(opts = {}) {
87
87
  }
88
88
  const sources = catalog.sources.map((s) => {
89
89
  const reasons = [...new Set(byRef.get(s.sourceRef) || [])]
90
- const repairable = reasons.filter((r) => REPAIRABLE_REASONS_V1.includes(r))
90
+ const repairable = reasons.filter((r) => REPAIRABLE_REASONS_PRE_V1.includes(r))
91
91
  return {
92
92
  sourceRef: s.sourceRef, kind: s.kind, scope: s.scope, file: s.file,
93
93
  status: repairable.length ? 'stale' : (reasons.length ? 'unrepairable' : 'ok'),
@@ -99,7 +99,7 @@ export function createStorageManagerPre(opts = {}) {
99
99
  ok: true,
100
100
  indexEnabled: true,
101
101
  scannedAt: now(),
102
- version: STORAGE_MANAGE_VERSION_V1,
102
+ version: STORAGE_MANAGE_VERSION_PRE_V1,
103
103
  sources,
104
104
  stale,
105
105
  counts: {
@@ -224,7 +224,7 @@ export function createStorageManagerPre(opts = {}) {
224
224
  repair,
225
225
  deleteMemory,
226
226
  auditLog: () => audit.slice(),
227
- version: STORAGE_MANAGE_VERSION_V1,
227
+ version: STORAGE_MANAGE_VERSION_PRE_V1,
228
228
  dispose() { audit.length = 0 },
229
229
  }
230
230
  }