@furongjun1999/dsh-memory 0.4.8 → 0.4.10

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 (232) hide show
  1. package/README.md +54 -26
  2. package/codebuddy/CODEBUDDY.md +196 -195
  3. package/codebuddy/README.md +13 -1
  4. package/codebuddy/mcp.json +9 -0
  5. package/docs/GBrain/345/217/257/345/200/237/351/211/264/347/202/271_/347/201/265/346/236/242/350/220/275/347/202/271/344/272/244/346/216/245_20260919.md +169 -0
  6. package/docs/README.md +1 -1
  7. package/docs/discipline/harnesses.yaml +18 -7
  8. package/docs/discipline/templates/full.md.tmpl +4 -3
  9. package/docs/experiments/linkref_backfill/candidates_20260917.json +726 -0
  10. package/docs/experiments/linkref_backfill/candidates_internal_20260917.json +602 -0
  11. package/docs/experiments/linkref_backfill/candidates_internal_v2.json +603 -0
  12. package/docs/experiments/linkref_backfill/candidates_secret_20260917.json +884 -0
  13. package/docs/experiments/linkref_backfill/candidates_secret_v2.json +789 -0
  14. package/docs/hive//345/244/232/347/253/257harness/351/200/232/344/277/241/345/245/221/347/272/246_v0.1.md +42 -0
  15. package/docs/hive//346/243/200/347/264/242/346/224/266/346/225/233/345/256/236/346/265/213/344/270/216S1b/350/256/276/350/256/241_v0.1.md +43 -0
  16. package/docs/hive//346/243/200/347/264/242/350/267/257/345/276/204/344/270/216/350/256/244/347/237/245/347/273/223/346/236/204/345/245/221/347/272/246_v0.1.md +102 -0
  17. package/docs/hive//347/234/237/345/256/236/345/272/223/347/253/257/345/210/260/347/253/257/345/256/236/346/265/213_S1b/344/270/216/345/217/254/345/233/236/346/235/203/350/241/241_v0.1.md +57 -0
  18. package/docs/hive//347/234/237/345/256/236/345/272/223/347/253/257/345/210/260/347/253/257/345/256/236/346/265/213_S7/345/200/222/346/216/222/345/200/231/351/200/211/345/261/202_v0.1.md +126 -0
  19. package/docs/hive//350/234/202/345/267/242/345/217/214/345/256/236/344/276/213/344/272/222/351/252/214_/350/256/276/350/256/241/345/256/232/347/250/277.md +503 -0
  20. package/docs/mdcg/D_meta_/345/267/245/347/250/213/345/214/226/346/226/271/346/241/210_v0.2.md +216 -0
  21. package/docs/mdcg/README/350/257/246/347/273/206/347/211/210_v0.4.5.md +631 -625
  22. package/docs/mdcg//344/273/243/347/240/201/350/257/204/345/256/241/344/270/216/346/235/241/344/273/266/345/214/226/346/263/250/351/207/212_/345/245/221/347/272/246_v0.1.md +82 -0
  23. package/docs/mdcg//345/205/250/345/272/223/344/273/243/347/240/201/350/257/204/345/256/241/344/270/216/346/235/241/344/273/266/345/214/226/346/263/250/351/207/212_/350/256/241/345/210/222_v0.1.md +600 -0
  24. package/docs/mdcg//345/212/237/350/203/275/350/260/203/347/224/250/346/230/240/345/260/204/350/241/250_v0.1.md +47 -45
  25. package/docs/mdcg//345/255/220/344/273/243/347/220/206/351/205/215/347/275/256/346/240/207/345/207/{206_v0.4.md → 206_v0.5.md} +92 -4
  26. package/docs/mdcg//347/201/265/346/236/242/350/256/260/345/277/206/345/212/250/350/257/215/345/215/217/350/256/256_v1.0-draft.md +172 -0
  27. package/docs/mdcg//347/216/257/344/272/214_/347/231/275/347/256/261/345/241/253/345/205/205/346/265/201/346/260/264/347/272/277_v0.1.md +31 -0
  28. package/docs/mdcg//350/267/250/347/253/257/351/252/214/350/257/201/344/270/216/345/220/214/346/255/245/345/215/217/350/256/256_v0.1.md +93 -0
  29. package/docs/theory//345/271/266/345/217/221/345/277/205/347/204/266/346/200/247/347/220/206/350/256/272_v0.2.md +2 -2
  30. package/docs/theory//347/220/206/350/256/272_/346/234/272/345/210/266_/344/273/243/347/240/201_/345/256/236/351/252/214_/347/274/272/345/217/243/347/237/251/351/230/265_v0.1.md +3 -3
  31. package/docs/theory//350/256/244/347/237/245/344/273/243/347/220/206/344/270/216/346/224/266/346/225/233/347/273/223/346/236/204_/346/235/241/344/273/266/350/256/272/351/207/215/346/236/204_v0.1.md +2 -2
  32. package/docs//345/267/245/344/275/234/347/272/252/345/276/213_/350/256/244/347/237/245/345/233/276/346/235/241/347/233/256_v1.1.json +434 -433
  33. package/docs//347/201/265/346/236/242/350/207/252/346/210/221/346/224/271/350/277/233/345/267/245/344/275/234/350/256/241/345/210/222_/345/244/226/351/203/250/347/240/224/347/251/266/347/263/273/345/210/227/345/220/270/346/224/266_v1_20260919.md +286 -0
  34. package/dsh/README.md +33 -0
  35. package/dsh/cordis.yml.example +13 -7
  36. package/dsh/hive-mcp-probe.mjs +94 -0
  37. package/dsh/hive-mcp.example.yml +62 -0
  38. package/dsh/update-lingshu.bat +11 -0
  39. package/dsh/update-lingshu.ps1 +337 -0
  40. package/lib/hooks.d.ts +3 -0
  41. package/lib/hooks.js +17 -23
  42. package/lib/index.d.ts +4 -2
  43. package/lib/index.js +29 -6
  44. package/lib/lib/datapath.d.ts +76 -1
  45. package/lib/lib/datapath.js +199 -13
  46. package/lib/lib/mdcg_client.d.ts +40 -3
  47. package/lib/lib/mdcg_client.js +46 -22
  48. package/lib/lib/mutual.js +4 -4
  49. package/lib/lib/token_store.js +4 -5
  50. package/md_cg/audit.py +17 -2
  51. package/md_cg/autonomy.py +86 -15
  52. package/md_cg/backfill.py +36 -1
  53. package/md_cg/backfill_bigdomain.py +34 -0
  54. package/md_cg/bench6_arms.py +28 -1
  55. package/md_cg/bench6_common.py +10 -1
  56. package/md_cg/bench6_competitors.py +6 -1
  57. package/md_cg/bench_axis_domain.py +9 -1
  58. package/md_cg/bench_blind_comp.py +7 -1
  59. package/md_cg/bench_en_atoms_public.py +9 -0
  60. package/md_cg/bench_governance.py +348 -0
  61. package/md_cg/bench_lme_zh.py +16 -1
  62. package/md_cg/bench_locomo.py +2 -1
  63. package/md_cg/bench_locomo_zh.py +16 -1
  64. package/md_cg/bench_locomo_zh_public.py +4 -1
  65. package/md_cg/bench_longmem.py +2 -1
  66. package/md_cg/bench_membench.py +27 -1
  67. package/md_cg/bench_p0.py +4 -1
  68. package/md_cg/bench_progressive.py +13 -1
  69. package/md_cg/bench_role_views.py +238 -0
  70. package/md_cg/bench_task_ab.py +8 -1
  71. package/md_cg/bench_task_ab_llm.py +13 -1
  72. package/md_cg/bench_unified_en.py +6 -1
  73. package/md_cg/bench_zh_mad.py +20 -1
  74. package/md_cg/blindspot_tickets.py +123 -0
  75. package/md_cg/branches.py +12 -1
  76. package/md_cg/build_postings.py +73 -0
  77. package/md_cg/ccgc.py +67 -2
  78. package/md_cg/census.py +5 -1
  79. package/md_cg/chain.py +24 -3
  80. package/md_cg/codeindex.py +134 -17
  81. package/md_cg/coldverify.py +265 -0
  82. package/md_cg/comment_gate.py +338 -0
  83. package/md_cg/cond_compose.py +190 -0
  84. package/md_cg/cond_facts.py +155 -0
  85. package/md_cg/cond_template.json +107 -0
  86. package/md_cg/condition_anchor.py +143 -0
  87. package/md_cg/conformance.py +69 -4
  88. package/md_cg/consistency.py +24 -1
  89. package/md_cg/consolidate.py +53 -2
  90. package/md_cg/corpus.py +4 -0
  91. package/md_cg/crosscheck.py +42 -2
  92. package/md_cg/crypto.py +35 -1
  93. package/md_cg/d_meta.py +310 -0
  94. package/md_cg/datapath.py +201 -26
  95. package/md_cg/docindex.py +122 -1
  96. package/md_cg/eval_common.py +29 -1
  97. package/md_cg/evidence.py +27 -1
  98. package/md_cg/evolution.py +21 -1
  99. package/md_cg/export.py +11 -1
  100. package/md_cg/forgetting.py +23 -1
  101. package/md_cg/fsutil.py +18 -1
  102. package/md_cg/hotcache.py +214 -0
  103. package/md_cg/hyperedge.py +251 -0
  104. package/md_cg/identity.py +18 -1
  105. package/md_cg/insight.py +17 -1
  106. package/md_cg/lexicon/build_cedict_en_zh.py +9 -0
  107. package/md_cg/lexicon/build_standard_en.py +171 -168
  108. package/md_cg/lexicon/expand_en_zh.py +6 -0
  109. package/md_cg/lifecycle.py +12 -1
  110. package/md_cg/linkref.py +281 -0
  111. package/md_cg/links.py +29 -1
  112. package/md_cg/mcp_server.py +362 -43
  113. package/md_cg/md_whitebox.py +53 -1
  114. package/md_cg/mdcg.py +1003 -27
  115. package/md_cg/mdcos.py +558 -36
  116. package/md_cg/metacognition.py +37 -2
  117. package/md_cg/migrate.py +4 -0
  118. package/md_cg/migrate_aeis.py +221 -213
  119. package/md_cg/migrate_roleplay.py +8 -0
  120. package/md_cg/migrate_wisdom_graph.py +14 -1
  121. package/md_cg/mreview/__main__.py +3 -0
  122. package/md_cg/mreview/bundle.py +8 -0
  123. package/md_cg/mreview/candidates.py +9 -0
  124. package/md_cg/mreview/govern.py +21 -1
  125. package/md_cg/mreview/locate.py +34 -0
  126. package/md_cg/mreview/pipeline.py +29 -1
  127. package/md_cg/mreview/ruleset.py +16 -1
  128. package/md_cg/nodefile.py +233 -3
  129. package/md_cg/pooling.py +23 -1
  130. package/md_cg/postings.py +298 -0
  131. package/md_cg/predict.py +89 -9
  132. package/md_cg/progressive.py +3 -0
  133. package/md_cg/protect.py +14 -1
  134. package/md_cg/protocol.py +372 -0
  135. package/md_cg/provenance.py +262 -0
  136. package/md_cg/reach.py +453 -0
  137. package/md_cg/refindex.py +47 -2
  138. package/md_cg/refine.py +20 -1
  139. package/md_cg/roleviews.py +89 -0
  140. package/md_cg/routing.py +76 -0
  141. package/md_cg/scrub.py +63 -2
  142. package/md_cg/security.py +26 -1
  143. package/md_cg/self_state.py +64 -1
  144. package/md_cg/selfreport.py +151 -0
  145. package/md_cg/semantic/canonical.py +5 -0
  146. package/md_cg/semantic/en_normalizer.py +364 -355
  147. package/md_cg/semantic/zh_en_atoms.py +139 -136
  148. package/md_cg/signer.py +41 -1
  149. package/md_cg/sources.py +583 -547
  150. package/md_cg/statushdr.py +179 -0
  151. package/md_cg/stg.py +59 -18
  152. package/md_cg/subgraph.py +23 -0
  153. package/md_cg/sustain.py +56 -1
  154. package/md_cg/tasks.py +26 -2
  155. package/md_cg/test_autonomy.py +26 -0
  156. package/md_cg/test_bench_governance.py +102 -0
  157. package/md_cg/test_blindspot_tickets.py +166 -0
  158. package/md_cg/test_ccgc.py +10 -0
  159. package/md_cg/test_codeindex.py +338 -0
  160. package/md_cg/test_comment_gate.py +187 -0
  161. package/md_cg/test_cond_compose_anchors.py +76 -0
  162. package/md_cg/test_condition_anchor.py +82 -0
  163. package/md_cg/test_d_meta.py +412 -0
  164. package/md_cg/test_datapath_root.py +188 -0
  165. package/md_cg/test_gain_gate.py +47 -1
  166. package/md_cg/test_hot_cold.py +187 -0
  167. package/md_cg/test_hyperedge.py +245 -0
  168. package/md_cg/test_linkref.py +306 -0
  169. package/md_cg/test_md_access_parity.py +15 -3
  170. package/md_cg/test_mr_m1.py +108 -18
  171. package/md_cg/test_mr_m3.py +8 -1
  172. package/md_cg/test_p26_refindex.py +49 -20
  173. package/md_cg/test_p27_docindex.py +236 -2
  174. package/md_cg/test_p2_mcp.py +1 -1
  175. package/md_cg/test_p31_insight.py +24 -0
  176. package/md_cg/test_p44_md_whitebox.py +14 -1
  177. package/md_cg/test_protocol.py +243 -0
  178. package/md_cg/test_reach.py +378 -0
  179. package/md_cg/test_reach_keys.py +201 -0
  180. package/md_cg/test_reach_meta_exits.py +145 -0
  181. package/md_cg/test_read_clip.py +8 -4
  182. package/md_cg/test_retr_s1.py +340 -0
  183. package/md_cg/test_retr_s1b.py +209 -0
  184. package/md_cg/test_retr_s3.py +194 -0
  185. package/md_cg/test_retr_s4.py +163 -0
  186. package/md_cg/test_retr_s5.py +200 -0
  187. package/md_cg/test_retr_s6.py +157 -0
  188. package/md_cg/test_retr_s7.py +385 -0
  189. package/md_cg/test_retr_s8_time.py +316 -0
  190. package/md_cg/test_retr_s9_edges.py +286 -0
  191. package/md_cg/test_retr_s9_entity_ctx.py +175 -0
  192. package/md_cg/test_review_conformance.py +59 -2
  193. package/md_cg/test_role_views.py +354 -0
  194. package/md_cg/test_subproc_encoding.py +188 -0
  195. package/md_cg/test_trust.py +361 -0
  196. package/md_cg/test_units_poll.py +71 -0
  197. package/md_cg/test_v14_fixes.py +397 -0
  198. package/md_cg/test_validity_filter.py +280 -0
  199. package/md_cg/test_wisdom_md_store.py +7 -3
  200. package/md_cg/test_writepipe.py +5 -1
  201. package/md_cg/theory.py +16 -1
  202. package/md_cg/tokens.py +40 -8
  203. package/md_cg/tool_face.py +13 -2
  204. package/md_cg/trust.py +943 -0
  205. package/md_cg/twophase.py +12 -1
  206. package/md_cg/units.py +132 -10
  207. package/md_cg/vision_evidence.py +24 -1
  208. package/md_cg/weights.py +24 -1
  209. package/md_cg/whitebox.py +32 -1
  210. package/md_cg/whitebox_kb/data/verify_cache.json +21210 -365
  211. package/md_cg/whitebox_kb/data/verify_savings.jsonl +5078 -0
  212. package/md_cg/whitebox_kb/wisdom/audit_log/chain_heat.json +10 -10
  213. package/md_cg/whitebox_kb/wisdom/code_compose.py +113 -6
  214. package/md_cg/whitebox_kb/wisdom/code_solidified.json +1 -1
  215. package/md_cg/whitebox_kb/wisdom/verifier.py +340 -55
  216. package/md_cg/whitebox_kb/wisdom/wisdom-book-cloud.db +0 -0
  217. package/md_cg/writelimit.py +18 -4
  218. package/md_cg/writepipe.py +178 -7
  219. package/package.json +2 -2
  220. package/skills/skills/designer-perspective/scripts/__pycache__/designer.cpython-310.pyc +0 -0
  221. package/skills/skills/designer-perspective/scripts/designer.py +17 -1
  222. package/skills/skills/designer-perspective/tests/selftest.py +3 -1
  223. package/src/hooks.ts +17 -21
  224. package/src/index.ts +33 -6
  225. package/src/lib/datapath.ts +211 -13
  226. package/src/lib/mdcg_client.ts +64 -25
  227. package/src/lib/mutual.ts +411 -411
  228. package/src/lib/token_store.ts +4 -5
  229. package/zcode/AGENTS.md +196 -195
  230. package/zcode/README.md +4 -0
  231. package/md_cg/whitebox_kb/wisdom/wisdom-book-cloud.db-shm +0 -0
  232. package/md_cg/whitebox_kb/wisdom/wisdom-book-cloud.db-wal +0 -0
package/md_cg/autonomy.py CHANGED
@@ -26,13 +26,24 @@ import json
26
26
  import os
27
27
  import time
28
28
 
29
- from . import metacognition, predict
29
+ from . import d_meta, metacognition, predict
30
30
  from .fsutil import append_jsonl
31
31
 
32
32
  #: 信息差评分权重:不确定度二阶差分 |d2|(加速度)×1,
33
33
  #: BLINDSPOT ×2(资格失败最重)、DEFER ×1(降级但可确认)。
34
34
  W_D2, W_BLINDSPOT, W_DEFER = 1.0, 2.0, 1.0
35
35
 
36
+ #: D_meta 排序加分(方案 §2.4,**默认生效**):D_meta 只进**排序键**,
37
+ #: 不改 `score`(ΔD 定价器口径不变)、不改资格判据(score>0 才是资格)、
38
+ #: 也不改 σ(Gain) 筛选(#37 价值链)。加分取**单一指定代理**(DMETA_PROXY),
39
+ #: 禁三代理加权合成(智能论3.4 DEV-002a)。`MDCG_D_META=0` → compute()
40
+ #: 返回 enabled=False → 加分退化 0(回到旧排序)。
41
+ #: 口径诚实标注:本轮取库侧 query 无关代理 → 同轮全部提案加分同值 → **当前
42
+ #: 口径下排序不变**;区分性待 query-relative 口径(`_neg_coverage`)在
43
+ #: reflect 侧落盘后自然生效(不提前编造区分性)。
44
+ W_DMETA = 0.1
45
+ DMETA_PROXY = "unmodeled_growth"
46
+
36
47
  EXPLORE_LOG = "_explore.jsonl"
37
48
 
38
49
  # P-T-40 信息增益门槛(#6/#37 投影):Value = ΔD·σ(Gain)——
@@ -44,10 +55,12 @@ GAIN_COOLDOWN = 3600.0 # 冷却秒数:过期后 σ 回 1.0(给探索机会
44
55
  _GAIN_STUCK = ("carried", "unresolved", "unknowable", "no_anchor")
45
56
 
46
57
 
58
+ # 生效条件:cg 无 root 属性时以 '.' 作为 os.path.join 的第一参数,有 root 属性时始终使用 cg.root 的值(即使为 None,也会传入 os.path.join 导致报错),再与模块常量 EXPLORE_LOG 拼接。
47
59
  def _explore_log_path(cg):
48
60
  return os.path.join(getattr(cg, "root", "."), EXPLORE_LOG)
49
61
 
50
62
 
63
+ # 生效条件:给定 cg 与 bid 读取探索留痕,无该 bid 的 outcomes 时返回 gain=None、sigma=1.0,最近 window(默认 GAIN_WINDOW)次终态全属 _GAIN_STUCK 且 now-last_t 小于 cooldown(默认 GAIN_COOLDOWN)时返回 sigma=0.0、gain=0,终态全属 _GAIN_STUCK 但冷却已过、且与该 bid 相关轮次的 meta_outcomes.delta[DMETA_PROXY] 均值大于 0 时同样返回 sigma=0.0、gain=0(压力仍在扩大则不放行),其余返回 sigma=1.0、gain=1;meta_outcomes 键缺失、delta 非字典或其值为非数值的轮次不计入压力均值(缺键即不触发新分支,既有 ①-④ 行为逐字不变)。
51
64
  def gain_gate(cg, bid: str, now: float | None = None,
52
65
  window: int = GAIN_WINDOW, cooldown: float = GAIN_COOLDOWN) -> dict:
53
66
  """信息增益门槛(P-T-40 第四保护投影,纯读无副作用)。
@@ -61,7 +74,7 @@ def gain_gate(cg, bid: str, now: float | None = None,
61
74
  `now` 可注入以便测试冷却过期。
62
75
  """
63
76
  now = time.time() if now is None else float(now)
64
- terminals, last_t = [], 0.0
77
+ terminals, last_t, meta_deltas = [], 0.0, []
65
78
  try:
66
79
  with open(_explore_log_path(cg), encoding="utf-8") as f:
67
80
  for line in f:
@@ -75,30 +88,58 @@ def gain_gate(cg, bid: str, now: float | None = None,
75
88
  if bid in oc:
76
89
  terminals.append(str(oc[bid]))
77
90
  last_t = max(last_t, float(r.get("t") or 0.0))
91
+ # D_meta 跨轮压力增量(缺键/关闭 → 不计入 → 不触发新分支)
92
+ mo = r.get("meta_outcomes")
93
+ dlt = mo.get("delta") if isinstance(mo, dict) else None
94
+ v = dlt.get(DMETA_PROXY) if isinstance(dlt, dict) else None
95
+ if isinstance(v, (int, float)) and not isinstance(v, bool):
96
+ meta_deltas.append(float(v))
78
97
  except OSError:
79
98
  pass
80
99
  if not terminals:
81
100
  return {"sigma": 1.0, "gain": None, "last_t": 0.0,
82
101
  "reason": "无探索历史(首探放行:无证据不否决)"}
83
102
  recent = terminals[-int(window):]
84
- if (len(recent) >= int(window)
85
- and all(t in _GAIN_STUCK for t in recent)
86
- and (now - last_t) < float(cooldown)):
103
+ stuck = (len(recent) >= int(window)
104
+ and all(t in _GAIN_STUCK for t in recent))
105
+ rising = bool(meta_deltas) and (sum(meta_deltas) / len(meta_deltas)) > 0
106
+ meta_txt = ""
107
+ if rising:
108
+ meta_txt = (";D_meta 压力均值 %+.4f(%s,近 %d 轮留痕)"
109
+ % (sum(meta_deltas) / len(meta_deltas), DMETA_PROXY,
110
+ len(meta_deltas)))
111
+ if stuck and (now - last_t) < float(cooldown):
87
112
  return {"sigma": 0.0, "gain": 0, "last_t": round(last_t, 3),
88
113
  "reason": ("增益门槛(P-T-40):连续 %d 次终态无变化 %s → "
89
- "DEFER_EXHAUSTED(冷却 %ds 内不再重复探索)"
90
- % (len(recent), list(recent), int(cooldown)))}
114
+ "DEFER_EXHAUSTED(冷却 %ds 内不再重复探索)%s"
115
+ % (len(recent), list(recent), int(cooldown),
116
+ meta_txt))}
117
+ if stuck and rising:
118
+ return {"sigma": 0.0, "gain": 0, "last_t": round(last_t, 3),
119
+ "meta_rising": True,
120
+ "reason": ("增益门槛(P-T-40):连续 %d 次终态无变化 %s 且 "
121
+ "D_meta 压力仍在扩大%s → "
122
+ "DEFER_EXHAUSTED_UNDER_PRESSURE(冷却已过但不放行:"
123
+ "压力扩大而终态停滞 = 重复探索不再产生增益)"
124
+ % (len(recent), list(recent), meta_txt))}
91
125
  return {"sigma": 1.0, "gain": 1, "last_t": round(last_t, 3),
92
126
  "reason": "有增益证据或冷却已过"}
93
127
 
94
128
 
129
+ # 生效条件:cg 的反思记录经 metacognition._reflections 取末 int(window) 条切片(window=0 时切片即全量),按非空 query 聚合后仅 score=W_D2*d2_abs+W_BLINDSPOT*blindspot+W_DEFER*defer>0 的项进入 out(**资格判据先于加分**:score<=0 即 continue,D_meta 不构成资格),其中 enforce_gain 为真值时还须 gain_gate(cg, …, now)["sigma"]>0 否则该项标 deferred_exhausted 转入 deferred,enforce_gain 为假值时不做增益筛选全数进入 out;D_meta 加分 W_DMETA*d_meta.pressure(d_meta.compute(cg, window), DMETA_PROXY) 每轮只算一次并在循环内复用,只写入 sort_score 与 reason 证据(不改 score、不改资格、不改 σ),MDCG_D_META 关闭或未启用时该加分为 0.0;最终 out 按 (-sort_score, -last_t) 排序后截取 max(1, int(limit)) 条(limit=0 或负数也返回 1 条)并随 n_signals=len(agg) 返回;
95
130
  def proposals(cg, window: int = 200, limit: int = 3,
96
131
  enforce_gain: bool = True) -> dict:
97
132
  """从反思日志聚合信息差信号,产出排序后的探索提案。
98
133
 
99
134
  每条提案携带证据明细(d2 之和/绝对值和、BLINDSPOT/DEFER 计数、样本数),
100
- 排序键 = W_D2*Σ|d2| + W_BLINDSPOT*BLINDSPOT + W_DEFER*DEFER。
101
- 无信号的查询不产生提案(不编造探索价值)。
135
+ `score` = W_D2*Σ|d2| + W_BLINDSPOT*BLINDSPOT + W_DEFER*DEFER(**ΔD 定价器,
136
+ 口径不变**);`sort_score` = score + W_DMETA×D_meta 边界压力(排序键)。
137
+ 无信号的查询不产生提案(不编造探索价值)——**D_meta 加分不构成资格**:
138
+ `score<=0` 的项在加分之前就被 continue 掉。
139
+
140
+ D_meta 只影响**排序**、不改 σ(Gain) 筛选;本轮取库侧 query 无关的单一
141
+ 代理(DMETA_PROXY),同轮各提案加分同值,故当前口径下排序不变——
142
+ 区分性待 query-relative 口径落盘后自然生效(不提前编造)。
102
143
 
103
144
  `enforce_gain`(默认 True):σ(Gain) 资格筛选(#37 价值链——score 是
104
145
  定价器,Gain 是筛选器)。被门槛拦下的提案不静默丢弃,落入 `deferred`
@@ -129,17 +170,24 @@ def proposals(cg, window: int = 200, limit: int = 3,
129
170
 
130
171
  out, deferred = [], []
131
172
  now = time.time()
173
+ mv = d_meta.compute(cg, window=window) # 每轮一次、循环内复用(禁 N× 扫描)
174
+ bonus = (round(W_DMETA * d_meta.pressure(mv, DMETA_PROXY), 4)
175
+ if mv.get("enabled") else 0.0)
132
176
  for a in agg.values():
133
177
  score = (W_D2 * a["d2_abs"] + W_BLINDSPOT * a["blindspot"]
134
178
  + W_DEFER * a["defer"])
135
179
  if score <= 0:
136
180
  continue # 无信息差信号 → 不提案(不编造)
137
181
  a = dict(a)
138
- a["score"] = round(score, 4)
182
+ a["score"] = round(score, 4) # ΔD 定价器:口径不变
183
+ a["d_meta_bonus"] = bonus # D_meta 排序加分(只影响排序)
184
+ a["sort_score"] = round(score + bonus, 4)
139
185
  a["reason"] = ("信息差信号:Σ|d2|=%s(Σd2=%s)+ BLINDSPOT×%d + DEFER×%d"
140
- "(近 %d 次反思)"
186
+ "(近 %d 次反思);D_meta 排序加分 +%s(%s=%s,三代理"
187
+ "不合成;不改资格/σ(Gain))"
141
188
  % (a["d2_abs"], a["d2_sum"], a["blindspot"],
142
- a["defer"], a["samples"]))
189
+ a["defer"], a["samples"], bonus, DMETA_PROXY,
190
+ d_meta.pressure(mv, DMETA_PROXY)))
143
191
  if enforce_gain: # P-T-40 增益门槛:资格由实现值裁决
144
192
  gate = gain_gate(cg, metacognition._key(a["query"]), now=now)
145
193
  a["gain_gate"] = gate
@@ -148,13 +196,17 @@ def proposals(cg, window: int = 200, limit: int = 3,
148
196
  deferred.append(a)
149
197
  continue
150
198
  out.append(a)
151
- out.sort(key=lambda x: (-x["score"], -x["last_t"]))
199
+ out.sort(key=lambda x: (-x["sort_score"], -x["last_t"]))
152
200
  return {"ok": True, "proposals": out[:max(1, int(limit))],
153
201
  "n_signals": len(agg), "deferred": deferred,
202
+ "d_meta": {"enabled": bool(mv.get("enabled")), "bonus": bonus,
203
+ "proxy": DMETA_PROXY},
154
204
  "value_chain": ("Value=ΔD·σ(Gain):score 定价排序(ΔD 代理),"
155
- "σ(Gain) 资格筛选(实现值=outcomes 留痕)")}
205
+ "σ(Gain) 资格筛选(实现值=outcomes 留痕);"
206
+ "D_meta 只加排序分、不改资格(DEV-002a)")}
156
207
 
157
208
 
209
+ # 生效条件:cg 必需,该符号始终返回 ok=True、action="explore";其中 bypass_gain 为真值时以 enforce_gain=False 调 proposals、为假值时以 True 调(window/limit 原样透传),逐条提案用 apply/actor 调 predict.learn_blindspots 并把其首个 step 的 terminal 记入 outcomes[bid],同一 blindspot_id 本轮已见则记为 skipped,调用抛异常则记为 error("%s: %s" % (type(exc).__name__, exc)),并额外取本轮 d_meta.compute(append 前状态)与上一条 explore 留痕的 meta_outcomes.before,并列写入 meta_outcomes={before, prev_before, delta, proxy, enabled}(**outcomes 值类型不变**),最后 append_jsonl 写 _explore_log_path(cg) 时仅 OSError 被忽略;
158
210
  def explore(cg, apply: bool = False, limit: int = 3, window: int = 200,
159
211
  actor: str = "autonomy", bypass_gain: bool = False) -> dict:
160
212
  """最小探索闭环:提案 → 逐盲区五态验证 →(apply)回写待补线索。
@@ -173,6 +225,11 @@ def explore(cg, apply: bool = False, limit: int = 3, window: int = 200,
173
225
  (绕过增益筛选照常探索,留痕 bypass_gain 字段可审计)。
174
226
  """
175
227
  pr = proposals(cg, window=window, limit=limit, enforce_gain=not bypass_gain)
228
+ mv = d_meta.compute(cg, window=window) # 本轮 D_meta(append 前状态)
229
+ prev = None
230
+ for r in d_meta.tail_jsonl(_explore_log_path(cg), 1):
231
+ mo = r.get("meta_outcomes") if isinstance(r, dict) else None
232
+ prev = mo.get("before") if isinstance(mo, dict) else None
176
233
  steps, outcomes, seen = [], {}, set()
177
234
  for p in pr["proposals"]:
178
235
  bid = metacognition._key(p["query"])
@@ -201,6 +258,20 @@ def explore(cg, apply: bool = False, limit: int = 3, window: int = 200,
201
258
  "bids": [s["blindspot_id"] for s in steps],
202
259
  "outcomes": outcomes,
203
260
  "bypass_gain": bool(bypass_gain),
261
+ # D_meta 并列留痕(**outcomes 值类型不变**:终态字符串契约是
262
+ # gain_gate 停滞集比对的前提,改成 dict 会让 σ 静默恒为 1.0):
263
+ # before = 本轮开始时的三代理(含上轮 apply 的效果)
264
+ # prev_before = 上一条 explore 留痕的 before(无则 None)
265
+ # delta = before − prev_before(跨轮压力增量 → gain_gate 输入)
266
+ "meta_outcomes": {
267
+ "before": {k: float(mv.get(k) or 0.0)
268
+ for k in d_meta.PROXY_KEYS},
269
+ "prev_before": ({k: float(prev.get(k) or 0.0)
270
+ for k in d_meta.PROXY_KEYS}
271
+ if isinstance(prev, dict) else None),
272
+ "delta": d_meta.diff(prev, mv),
273
+ "proxy": DMETA_PROXY,
274
+ "enabled": bool(mv.get("enabled"))},
204
275
  "gain_deferred": [(d.get("query"), d.get("gain_gate", {}).get("reason"))
205
276
  for d in (pr.get("deferred") or [])]}
206
277
  try:
@@ -214,4 +285,4 @@ def explore(cg, apply: bool = False, limit: int = 3, window: int = 200,
214
285
  "裁决资格);终态五态由 learn_blindspots 诚实判定;"
215
286
  "apply=True 仅落地 carried/unresolved 的 gap_hint 待补"
216
287
  "线索(幂等);P-T-40 四保护=单轮深度/本轮循环去重/"
217
- "增益冷却/预算 limit(bypass_gain 显式豁免可审计)")}
288
+ "增益冷却/预算 limit(bypass_gain 显式豁免可审计)")}
package/md_cg/backfill.py CHANGED
@@ -127,11 +127,13 @@ CAP_RULES = OrderedDict(
127
127
 
128
128
  # ---- 通用工具 ------------------------------------------------------------
129
129
 
130
+ # 生效条件:x 为 str 时返回 MdCGOS(x),否则原样返回 x。
130
131
  def _as_cg(x):
131
132
  """接受 root 路径或已构造的 cg 实例——保持密级隔离与密钥上下文。"""
132
133
  return MdCGOS(x) if isinstance(x, str) else x
133
134
 
134
135
 
136
+ # 生效条件:v 为 list/tuple 时返回分号连接的非空元素文本;v 为 None 或空串时返回 "";否则返回 str(v).strip()(v 为 0 或 False 走此支返回 "0"/"False")。
135
137
  def _as_text(v) -> str:
136
138
  if isinstance(v, (list, tuple)):
137
139
  return ";".join(str(x).strip() for x in v if str(x).strip())
@@ -140,12 +142,14 @@ def _as_text(v) -> str:
140
142
  return str(v).strip()
141
143
 
142
144
 
145
+ # 生效条件:fm 为假值时按 {} 处理,state_attributes.comment 为 dict 时返回该 dict,否则返回 {}。
143
146
  def _comment(fm: dict) -> dict:
144
147
  st = (fm or {}).get("state_attributes")
145
148
  c = st.get("comment") if isinstance(st, dict) else None
146
149
  return c if isinstance(c, dict) else {}
147
150
 
148
151
 
152
+ # 生效条件:fm 的 state_attributes 为 dict 且其下 comment 为 dict 时原样返回该 comment;否则创建并返回空 comment dict(state_attributes 非 dict 时置 fm["state_attributes"]={},comment 非 dict 时置 st["comment"]={})。
149
153
  def _ensure_comment(fm: dict) -> dict:
150
154
  st = fm.get("state_attributes")
151
155
  if not isinstance(st, dict):
@@ -158,10 +162,12 @@ def _ensure_comment(fm: dict) -> dict:
158
162
  return c
159
163
 
160
164
 
165
+ # 生效条件:e 的 id 为真值时返回 id,否则返回 e 的 path 基名去掉最后 3 个字符(path 为假值时基名为空,结果空串)。
161
166
  def _node_id(e: dict) -> str:
162
167
  return e.get("id") or os.path.basename(e.get("path") or "")[:-3]
163
168
 
164
169
 
170
+ # 生效条件:fm 的 condition_space 四槽齐全时返回 nodefile.condition_space_text 合成文本,否则返回空串。
165
171
  def _condition_text(fm: dict) -> str:
166
172
  """→ 条件空间四槽合成的生效条件声明;四槽不齐 → ""(不冒充)。
167
173
 
@@ -172,14 +178,17 @@ def _condition_text(fm: dict) -> str:
172
178
  return nodefile.condition_space_text((fm or {}).get("condition_space"))
173
179
 
174
180
 
181
+ # 生效条件:s 为真值时返回其 sha1 前 12 位,s 为假值(None/空串等)时对空串取 sha1 前 12 位。
175
182
  def _sha(s: str) -> str:
176
183
  return hashlib.sha1((s or "").encode("utf-8")).hexdigest()[:12]
177
184
 
178
185
 
186
+ # 生效条件:batch 与 nid 经 f-string 拼接后取 sha1 前 12 位;两者为 None 会字符串化为 "None"。
179
187
  def _entry_id(batch: str, nid: str) -> str:
180
188
  return hashlib.sha1(f"{batch}|{nid}".encode("utf-8")).hexdigest()[:12]
181
189
 
182
190
 
191
+ # 生效条件:content 中 strip 后以 # 开头且去掉 # 与空白后、全角或半角冒号前首段等于 field 的整行被删除,其余行保留并 join。
183
192
  def _remove_ccg_line(content: str, field: str) -> str:
184
193
  """删掉 `# <字段>:…` 整行(回滚用)。"""
185
194
  keep = []
@@ -192,12 +201,14 @@ def _remove_ccg_line(content: str, field: str) -> str:
192
201
  return "\n".join(keep)
193
202
 
194
203
 
204
+ # 生效条件:cg.root 与模块级常量 BACKFILL_LOG 拼接为返回路径。
195
205
  def _log_path(cg) -> str:
196
206
  return os.path.join(cg.root, BACKFILL_LOG)
197
207
 
198
208
 
199
209
  # ---- 字段推导(唯一入口:只搬运已声明的证据) -----------------------------
200
210
 
211
+ # 生效条件:fm 与 content 给定时,仅对 content 中尚无对应 CCG 行的字段(功能名/生效条件/子功能/执行/验证方式/不适用条件)从 fm 的已有声明(state_attributes.comment、frontmatter、verification_basis、或形参 basis_text)取值,值非空且非占位文本才写入 out,假值不写、占位文本只把字段名追加进 placeholder_out(未传该形参时用临时列表)。
201
212
  def derive_fields(fm: dict, content: str, basis_text: str = None,
202
213
  placeholder_out: list = None) -> dict:
203
214
  """按**已有声明**推导可回填字段 → `{field: (value, basis)}`。
@@ -213,6 +224,7 @@ def derive_fields(fm: dict, content: str, basis_text: str = None,
213
224
  ph = placeholder_out if placeholder_out is not None else []
214
225
  out = {}
215
226
 
227
+ # 生效条件:field 与 basis 在 value 为真值且 nodefile.is_placeholder_text(value) 为假时写入 out;value 为假值不写入;value 为占位文本时仅把 field 记入 ph。
216
228
  def _put(field, value, basis):
217
229
  """有值且非占位标记才写出;占位值只记名,绝不渲染成事实。"""
218
230
  if not value:
@@ -267,6 +279,7 @@ def derive_fields(fm: dict, content: str, basis_text: str = None,
267
279
 
268
280
  # ---- 节点筛选 ------------------------------------------------------------
269
281
 
282
+ # 生效条件:cg 无 _readable 可调用时返回 True;有可调用时返回 bool(fn(e)),fn(e) 抛异常时返回 False。
270
283
  def _readable_guard(cg, e) -> bool:
271
284
  fn = getattr(cg, "_readable", None)
272
285
  if not callable(fn):
@@ -277,6 +290,7 @@ def _readable_guard(cg, e) -> bool:
277
290
  return False
278
291
 
279
292
 
293
+ # 生效条件:仅当 cg 对 e 读出的 fm 非 None、content 未被 crypto.is_encrypted、e['layer'] 不在 SKIP_LAYERS 且 fm.get('tags') 无命中 SKIP_TAGS、ccg_completeness(content)['complete'] 为假时才继续——derive_fields(受 basis_text 影响)过滤掉 content 已有 CCG 行的可写字段为空时按 placeholder_out 是否非空返回 ('placeholder'/'unfillable', None),非空时返回 ('', item)(item 的 id 取 nid、class 依 undeducible 是否为空取 'backfillable' 或 'partial');上述四个前置不满足时依次返回 ('unreadable'/'locked'/'derived'/'present', None)。
280
294
  def _classify(cg, e, nid, basis_text=None):
281
295
  """→ (skip_reason, item);skip_reason 非空表示不参与回填。"""
282
296
  fm, content = cg._read(e)
@@ -317,6 +331,7 @@ def _classify(cg, e, nid, basis_text=None):
317
331
  }
318
332
 
319
333
 
334
+ # 生效条件:x 经 _as_cg 后遍历 cg.index.nodes,按 ids/layer/prefix 过滤,内部层/不可读/locked/derived/present/unfillable/placeholder/partial 且 include_partial 为 False 分别计数跳过,其余标记 entry_id 并计入 targeted,items 受 limit 限制(limit 为 None 或 len(items) < limit 时追加),返回 dry_run rep。
320
335
  def plan(x, layer=None, limit=None, ids=None, include_partial=False,
321
336
  basis_text=None, prefix=None) -> dict:
322
337
  """预演:产出可回填清单,不写盘。
@@ -381,6 +396,7 @@ def plan(x, layer=None, limit=None, ids=None, include_partial=False,
381
396
 
382
397
  # ---- 回填写入 / 回滚 / 留痕 ----------------------------------------------
383
398
 
399
+ # 生效条件:x 可被 _as_cg 解释为 CG 句柄时,按 layer/ids/prefix/entry_ids 选定条目做回填并返回含 written 等计数的 rep,其中 limit 非 None 时把写入数截断到该值。
384
400
  def apply(x, ids=None, entry_ids=None, layer=None, limit=None,
385
401
  batch=BATCH_DEFAULT, include_partial=False, basis_text=None,
386
402
  actor=None, prefix=None) -> dict:
@@ -458,6 +474,7 @@ def apply(x, ids=None, entry_ids=None, layer=None, limit=None,
458
474
  return rep
459
475
 
460
476
 
477
+ # 生效条件:x 经 _as_cg 定位后读留痕日志,仅对 action=='backfill' 且(batch 为假值则不过滤 batch,否则 rec['batch']==batch)、(entry_ids 为假值则不过滤,否则 rec['entry_id'] 属于该集合)、write_id 未出现在已完成 rollback 集合中、节点命中 cg.index['nodes'] 且 cg._read(e) 的 fm 可读、字段当前 _ccg_field(content, f) 等于留痕 after 的记录执行撤销写回(before 为 None 则删该 comment 键,否则还原原值),无字段可撤销只计 conflict 不写盘,reverted 非空时 rebuild_index,结果汇总进返回的 rep。
461
478
  def rollback(x, batch=None, entry_ids=None, actor=None) -> dict:
462
479
  """按留痕反向应用:撤销本批次回填(当前值 ≠ 写入值时跳过,防覆盖)。"""
463
480
  cg = _as_cg(x)
@@ -528,6 +545,7 @@ def rollback(x, batch=None, entry_ids=None, actor=None) -> dict:
528
545
  return rep
529
546
 
530
547
 
548
+ # 生效条件:x 经 _as_cg,action/batch 为真值时过滤对应日志记录;limit 不为 None 且 >=0 时按 recs[-limit:] 截断(limit=0 时切片为全部记录),limit 为 None 或负数时不截断;返回 total/returned/records。
531
549
  def history(x, limit=100, action=None, batch=None) -> dict:
532
550
  cg = _as_cg(x)
533
551
  recs = []
@@ -555,6 +573,7 @@ def history(x, limit=100, action=None, batch=None) -> dict:
555
573
  _CAP_TEMPLATE_LINES = ("验证方式",)
556
574
 
557
575
 
576
+ # 生效条件:content 中去除 # 与空白后、全角或半角冒号前首段命中 _CAP_TEMPLATE_LINES 的行被剔除,其余行保留并 join。
558
577
  def _cap_body(content: str) -> str:
559
578
  """正文(供关键词匹配)——剔除 CCG 模板行,防模板词污染候选。"""
560
579
  keep = []
@@ -567,6 +586,7 @@ def _cap_body(content: str) -> str:
567
586
  return "\n".join(keep)
568
587
 
569
588
 
589
+ # 生效条件:当 fm 为可 get 的 frontmatter、e 为带 id 的节点条目、content 为正文文本时,返回 title+e.id+功能名/生效条件/子功能字段+正文前 300 字合并后的小写串(不含 fm.tags)。
570
590
  def _cap_text(e, fm, content) -> str:
571
591
  """候选匹配文本:节点标识 + CCG 字段 + 正文(**不含 `fm.tags`**)。"""
572
592
  parts = [_as_text(fm.get("title")), e.get("id") or ""]
@@ -578,6 +598,7 @@ def _cap_text(e, fm, content) -> str:
578
598
  return " ".join(parts).lower()
579
599
 
580
600
 
601
+ # 生效条件:text 包含 CAP_RULES 中某 cap 的至少一个关键词时,该 cap 以匹配关键词与置信度加入返回,按置信度降序、cap 升序排序;无匹配返回空 hits。
581
602
  def cap_matches(text: str) -> list:
582
603
  hits = []
583
604
  for cap, kws in CAP_RULES.items():
@@ -589,6 +610,7 @@ def cap_matches(text: str) -> list:
589
610
  return hits
590
611
 
591
612
 
613
+ # 生效条件:x 经 _as_cg,遍历 nodes 按 ids/layer 过滤,不可读计 denied,读取失败或加密计 locked,扫描后以 _cap_text 匹配并过滤 confidence >= min_conf 且标签未含 cap:,无命中计 present,有命中则 targeted 并受 limit 限制加入 items,返回 dry_run rep。
592
614
  def cap_plan(x, layer=None, limit=None, ids=None, min_conf=0.5) -> dict:
593
615
  """预演:按关键词启发式给出 `cap:<op>` 标签建议(含依据与置信度),不写盘。"""
594
616
  cg = _as_cg(x)
@@ -626,6 +648,7 @@ def cap_plan(x, layer=None, limit=None, ids=None, min_conf=0.5) -> dict:
626
648
  return rep
627
649
 
628
650
 
651
+ # 生效条件:x 经 _as_cg,batch 假值回落 BATCH_DEFAULT;先 cap_plan 得 items,按 entry_ids 过滤;逐个检查节点存在、读取可读、无新增标签分别计 drift/locked/drift,成功写 tags 并记日志,limit 非 None 且 written >= limit 时 break;返回 rep。
629
652
  def cap_apply(x, ids=None, entry_ids=None, layer=None, limit=None,
630
653
  batch=BATCH_DEFAULT, min_conf=0.5, actor=None) -> dict:
631
654
  """注入 `cap:<op>` 标签:只改 frontmatter.tags,不动正文。"""
@@ -676,6 +699,7 @@ def cap_apply(x, ids=None, entry_ids=None, layer=None, limit=None,
676
699
  return rep
677
700
 
678
701
 
702
+ # 生效条件:x 经 _as_cg,读取日志中 action=cap 且未回滚的记录,按 batch/entry_ids 过滤,节点存在且原加入标签仍在 tags 中时移除,否则计 conflict/missing,返回 rep。
679
703
  def cap_rollback(x, batch=None, entry_ids=None, actor=None) -> dict:
680
704
  """撤销 cap 注入:仅移除**仍在 tags 里**的 `cap:` 标签(防覆盖后续修改)。"""
681
705
  cg = _as_cg(x)
@@ -730,6 +754,7 @@ def cap_rollback(x, batch=None, entry_ids=None, actor=None) -> dict:
730
754
  EXEMPT_FLAG = "ccg_exempt"
731
755
 
732
756
 
757
+ # 生效条件:fm 的 verification_basis 合法且 content 的 ccg_completeness 标记 complete 时返回 (True, []),否则把缺失项放入 miss 返回 ready=False。
733
758
  def _exempt_ready(fm: dict, content: str):
734
759
  """摘豁免前置条件 → `(ready, missing)`。
735
760
 
@@ -745,6 +770,7 @@ def _exempt_ready(fm: dict, content: str):
745
770
  return (not miss), miss
746
771
 
747
772
 
773
+ # 生效条件:x 经 _as_cg,遍历 nodes 按 ids/prefix/layer 过滤,内部层/不可读/读取失败或加密分别计数跳过,fm 无 EXEMPT_FLAG 计 not_exempt,require_ready 为真且不 ready 时计 unready 并在 want 为 None 时跳过,否则生成 item 受 limit 限制;sample 为真值时按 id 序等距抽取 sample 个。
748
774
  def exempt_plan(x, layer=None, limit=None, ids=None, require_ready=True,
749
775
  sample=0, prefix=None) -> dict:
750
776
  """预演:列出可摘 `ccg_exempt` 的节点(默认要求证据就绪),不写盘。
@@ -804,6 +830,7 @@ def exempt_plan(x, layer=None, limit=None, ids=None, require_ready=True,
804
830
  return rep
805
831
 
806
832
 
833
+ # 生效条件:x 经 _as_cg,batch 假值回落 BATCH_DEFAULT;先 exempt_plan 得 items,按 entry_ids 过滤;逐个检查节点存在、可读、EXEMPT_FLAG 仍真、require_ready 为真时 _exempt_ready 再次通过;不通过计 skipped_unready 并记 exempt_skip;通过则置 EXEMPT_FLAG=False 写盘记日志;limit 限制 written;返回 rep。
807
834
  def exempt_apply(x, ids=None, entry_ids=None, layer=None, limit=None,
808
835
  batch=BATCH_DEFAULT, require_ready=True, actor=None,
809
836
  prefix=None) -> dict:
@@ -864,6 +891,7 @@ def exempt_apply(x, ids=None, entry_ids=None, layer=None, limit=None,
864
891
  return rep
865
892
 
866
893
 
894
+ # 生效条件:当 x 可解析为 cg 时,日志中 action 为 exempt 的记录若其非空 write_id 已存在于既有 action 为 exempt_rollback 的记录 write_id 集合中,则跳过并计入 skipped_done,否则在通过 batch 与 entry_ids 过滤后,节点存在且可读、EXEMPT_FLAG 当前为假时,该记录才被还原并计入 reverted;。
867
895
  def exempt_rollback(x, batch=None, entry_ids=None, actor=None) -> dict:
868
896
  """反向还原 `ccg_exempt`:仅当当前仍为「已摘」状态时还原,否则计 conflict。"""
869
897
  cg = _as_cg(x)
@@ -920,6 +948,7 @@ def exempt_rollback(x, batch=None, entry_ids=None, actor=None) -> dict:
920
948
  # ③ verify_conditions 只读复算:验收指标 legacy_position == 0
921
949
 
922
950
 
951
+ # 生效条件:x 经 _as_cg,遍历 nodes 按 prefix/layer 过滤,内部层/不可读/读取失败或加密/derived 层或 SKIP_TAGS 分别计数跳过;扫描后,正文已有生效条件计 declared,derive_fields 可推出计 derivable,否则按 condition_space 缺失槽计 pending 并受 limit 限制收集 items;返回 rep。
923
952
  def conditions_pending(x, prefix=None, layer=None, limit=None) -> dict:
924
953
  """只读台账:列出「未声明生效条件、且四槽推不出」的节点及其缺失槽。
925
954
 
@@ -978,6 +1007,7 @@ def conditions_pending(x, prefix=None, layer=None, limit=None) -> dict:
978
1007
  return rep
979
1008
 
980
1009
 
1010
+ # 生效条件:cg 的留痕日志中存在 basis 为 LEGACY_CONDITION_BASIS 且未被回滚的 backfill 生效条件写入时,按 node 记录最后一次的 after/original 返回;无匹配返回空。
981
1011
  def _legacy_condition_records(cg) -> dict:
982
1012
  """→ {node: {after, original, …}}:我们**自己写下的**单槽冒充行(真源 = 留痕)。
983
1013
 
@@ -1005,6 +1035,7 @@ def _legacy_condition_records(cg) -> dict:
1005
1035
  return out
1006
1036
 
1007
1037
 
1038
+ # 生效条件:x 经 _as_cg,从 _legacy_condition_records 取候选,按 prefix 过滤,节点不存在/不可读/读取失败或加密分别计 missing/denied/locked;当前生效条件等于留痕 after 时,四槽合成非空则 rewrite 否则 drop,不等则 conflict;items 受 limit 限制;返回 dry_run rep。
1008
1039
  def fix_conditions_plan(x, prefix=None, limit=None) -> dict:
1009
1040
  """预演:清洗存量单槽冒充行。不写盘。
1010
1041
 
@@ -1054,6 +1085,7 @@ def fix_conditions_plan(x, prefix=None, limit=None) -> dict:
1054
1085
  return rep
1055
1086
 
1056
1087
 
1088
+ # 生效条件:x 经 _as_cg,batch 假值回落 FIX_BATCH_DEFAULT;先 fix_conditions_plan 得 items,按 ids/entry_ids 过滤;逐个再验当前值仍等于 before 且 comment_before 一致,否则 skipped_drift;rewrite 时 upsert 生效条件行并写 comment,drop 时删行并还原 comment;写盘记 condition_fix,drop 另记 condition_pending;limit 限制 rewritten+dropped;返回 rep。
1057
1089
  def fix_conditions_apply(x, ids=None, entry_ids=None, prefix=None, limit=None,
1058
1090
  batch=FIX_BATCH_DEFAULT, actor=None) -> dict:
1059
1091
  """执行清洗:`rewrite` 改写为四槽合成声明;`drop` 删行并登记待补。
@@ -1144,6 +1176,7 @@ def fix_conditions_apply(x, ids=None, entry_ids=None, prefix=None, limit=None,
1144
1176
  return rep
1145
1177
 
1146
1178
 
1179
+ # 生效条件:x 经 _as_cg,读取日志中 action=condition_fix 且未回滚的记录,按 batch/entry_ids 过滤,节点存在且当前生效条件等于 after 时,还原 before(空则删行)并还原 comment_before,否则计 conflict/missing;返回 rep。
1147
1180
  def fix_conditions_rollback(x, batch=None, entry_ids=None, actor=None) -> dict:
1148
1181
  """按留痕反向应用:恢复被改写的旧值 / 重建被删除的冒充行。
1149
1182
 
@@ -1210,6 +1243,7 @@ def fix_conditions_rollback(x, batch=None, entry_ids=None, actor=None) -> dict:
1210
1243
  return rep
1211
1244
 
1212
1245
 
1246
+ # 生效条件:x 经 _as_cg,遍历 nodes 按 prefix 过滤、内部层跳过,读取失败或加密跳过;对每个节点当前生效条件行,缺失计 absent,否则按 is_legacy_position_condition、等于 _condition_text 合成、等于 comment、其他分别计数,legacy_ids 受 limit 限制;返回 rep。
1213
1247
  def verify_conditions(x, prefix=None, limit=None) -> dict:
1214
1248
  """复算核对(只读):生效条件行的来源分布 + 弱等价残留计数。
1215
1249
 
@@ -1264,6 +1298,7 @@ ACTIONS = ("backfill", "backfill_rollback", "backfill_history",
1264
1298
  "exempt", "exempt_rollback", "exempt_history")
1265
1299
 
1266
1300
 
1301
+ # 生效条件:按其 action 分派——action=='backfill' 时 kw['apply'] 为真调 apply(x, 去掉 apply 的 kw)、否则调 plan 同参;action=='cap'/'exempt' 同理在 kw['apply'] 为真时调 cap_apply/exempt_apply、否则调 cap_plan/exempt_plan;action=='backfill_rollback'/'cap_rollback'/'exempt_rollback' 分别调 rollback/cap_rollback/exempt_rollback(x, **kw);action=='backfill_history' 调 history(x, **kw),'cap_history'/'exempt_history' 调 history(x, action='cap'/'exempt', **kw);其余 action 值抛 ValueError。
1267
1302
  def run(x, action, **kw) -> dict:
1268
1303
  """`maintain` op 的分派入口:action ∈ ACTIONS。"""
1269
1304
  if action == "backfill":
@@ -1290,4 +1325,4 @@ def run(x, action, **kw) -> dict:
1290
1325
  return exempt_rollback(x, **kw)
1291
1326
  if action == "exempt_history":
1292
1327
  return history(x, action="exempt", **kw)
1293
- raise ValueError(f"未知 backfill action:{action}(允许:{ACTIONS})")
1328
+ raise ValueError(f"未知 backfill action:{action}(允许:{ACTIONS})")
@@ -0,0 +1,34 @@
1
+ # -*- coding: utf-8 -*-
2
+ """big_domain 域标签回填(S1 大域收敛的前置元数据;幂等、支持 --dry-run)。
3
+
4
+ 用法:
5
+ python -X utf8 -m md_cg.backfill_bigdomain --root <库根> [--dry-run] [--limit N]
6
+
7
+ 为何需要单独的回填入口:域标签是**写入侧**新增的元数据,存量节点没有;
8
+ S1 要按域收敛,就必须先把存量补齐(契约 §8.1)。
9
+ """
10
+ import argparse
11
+ import json
12
+ import sys
13
+
14
+ from .mdcg import MdCG
15
+
16
+
17
+ # 生效条件:无条件解析参数(root 必填,dry_run/limit 可选)、以 MdCG(root) 打开库、调用 backfill_big_domain 并把统计 dict 以 JSON 打印,返回 0。
18
+ def main(argv=None):
19
+ ap = argparse.ArgumentParser()
20
+ ap.add_argument("--root", required=True, help="库根目录")
21
+ ap.add_argument("--dry-run", action="store_true", dest="dry_run",
22
+ help="只统计不改盘")
23
+ ap.add_argument("--limit", type=int, default=None, help="最多补写多少个节点")
24
+ a = ap.parse_args(argv)
25
+ cg = MdCG(a.root)
26
+ st = cg.backfill_big_domain(dry_run=a.dry_run, limit=a.limit)
27
+ print(json.dumps(st, ensure_ascii=False))
28
+ return 0
29
+
30
+
31
+ if __name__ == "__main__":
32
+ if hasattr(sys.stdout, "reconfigure"):
33
+ sys.stdout.reconfigure(encoding="utf-8")
34
+ sys.exit(main())