@furongjun1999/dsh-memory 0.4.8 → 0.4.9
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/README.md +54 -26
- package/codebuddy/CODEBUDDY.md +196 -195
- package/codebuddy/README.md +13 -1
- package/codebuddy/mcp.json +9 -0
- 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
- package/docs/README.md +1 -1
- package/docs/discipline/harnesses.yaml +18 -7
- package/docs/discipline/templates/full.md.tmpl +4 -3
- package/docs/experiments/linkref_backfill/candidates_20260917.json +726 -0
- package/docs/experiments/linkref_backfill/candidates_internal_20260917.json +602 -0
- package/docs/experiments/linkref_backfill/candidates_internal_v2.json +603 -0
- package/docs/experiments/linkref_backfill/candidates_secret_20260917.json +884 -0
- package/docs/experiments/linkref_backfill/candidates_secret_v2.json +789 -0
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- package/docs/mdcg/README/350/257/246/347/273/206/347/211/210_v0.4.5.md +631 -625
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- package/dsh/README.md +33 -0
- package/dsh/cordis.yml.example +13 -7
- package/dsh/hive-mcp-probe.mjs +94 -0
- package/dsh/hive-mcp.example.yml +62 -0
- package/dsh/update-lingshu.bat +11 -0
- package/dsh/update-lingshu.ps1 +337 -0
- package/lib/hooks.d.ts +3 -0
- package/lib/hooks.js +17 -23
- package/lib/index.d.ts +4 -2
- package/lib/index.js +29 -6
- package/lib/lib/datapath.d.ts +76 -1
- package/lib/lib/datapath.js +199 -13
- package/lib/lib/mdcg_client.d.ts +6 -3
- package/lib/lib/mdcg_client.js +6 -5
- package/lib/lib/mutual.js +4 -4
- package/lib/lib/token_store.js +4 -5
- package/md_cg/audit.py +17 -2
- package/md_cg/autonomy.py +86 -15
- package/md_cg/backfill.py +36 -1
- package/md_cg/backfill_bigdomain.py +34 -0
- package/md_cg/bench6_arms.py +28 -1
- package/md_cg/bench6_common.py +10 -1
- package/md_cg/bench6_competitors.py +6 -1
- package/md_cg/bench_axis_domain.py +9 -1
- package/md_cg/bench_blind_comp.py +7 -1
- package/md_cg/bench_en_atoms_public.py +9 -0
- package/md_cg/bench_governance.py +348 -0
- package/md_cg/bench_lme_zh.py +16 -1
- package/md_cg/bench_locomo.py +2 -1
- package/md_cg/bench_locomo_zh.py +16 -1
- package/md_cg/bench_locomo_zh_public.py +4 -1
- package/md_cg/bench_longmem.py +2 -1
- package/md_cg/bench_membench.py +27 -1
- package/md_cg/bench_p0.py +4 -1
- package/md_cg/bench_progressive.py +13 -1
- package/md_cg/bench_role_views.py +238 -0
- package/md_cg/bench_task_ab.py +8 -1
- package/md_cg/bench_task_ab_llm.py +13 -1
- package/md_cg/bench_unified_en.py +6 -1
- package/md_cg/bench_zh_mad.py +20 -1
- package/md_cg/blindspot_tickets.py +123 -0
- package/md_cg/branches.py +12 -1
- package/md_cg/build_postings.py +73 -0
- package/md_cg/ccgc.py +67 -2
- package/md_cg/census.py +5 -1
- package/md_cg/chain.py +24 -3
- package/md_cg/codeindex.py +134 -17
- package/md_cg/coldverify.py +265 -0
- package/md_cg/comment_gate.py +338 -0
- package/md_cg/cond_compose.py +190 -0
- package/md_cg/cond_facts.py +155 -0
- package/md_cg/cond_template.json +107 -0
- package/md_cg/condition_anchor.py +143 -0
- package/md_cg/conformance.py +69 -4
- package/md_cg/consistency.py +24 -1
- package/md_cg/consolidate.py +53 -2
- package/md_cg/corpus.py +4 -0
- package/md_cg/crosscheck.py +42 -2
- package/md_cg/crypto.py +35 -1
- package/md_cg/d_meta.py +310 -0
- package/md_cg/datapath.py +201 -26
- package/md_cg/docindex.py +122 -1
- package/md_cg/eval_common.py +29 -1
- package/md_cg/evidence.py +27 -1
- package/md_cg/evolution.py +21 -1
- package/md_cg/export.py +11 -1
- package/md_cg/forgetting.py +23 -1
- package/md_cg/fsutil.py +18 -1
- package/md_cg/hotcache.py +214 -0
- package/md_cg/hyperedge.py +251 -0
- package/md_cg/identity.py +18 -1
- package/md_cg/insight.py +17 -1
- package/md_cg/lexicon/build_cedict_en_zh.py +9 -0
- package/md_cg/lexicon/build_standard_en.py +171 -168
- package/md_cg/lexicon/expand_en_zh.py +6 -0
- package/md_cg/lifecycle.py +12 -1
- package/md_cg/linkref.py +281 -0
- package/md_cg/links.py +29 -1
- package/md_cg/mcp_server.py +362 -43
- package/md_cg/md_whitebox.py +53 -1
- package/md_cg/mdcg.py +1003 -27
- package/md_cg/mdcos.py +558 -36
- package/md_cg/metacognition.py +37 -2
- package/md_cg/migrate.py +4 -0
- package/md_cg/migrate_aeis.py +221 -213
- package/md_cg/migrate_roleplay.py +8 -0
- package/md_cg/migrate_wisdom_graph.py +14 -1
- package/md_cg/mreview/__main__.py +3 -0
- package/md_cg/mreview/bundle.py +8 -0
- package/md_cg/mreview/candidates.py +9 -0
- package/md_cg/mreview/govern.py +21 -1
- package/md_cg/mreview/locate.py +34 -0
- package/md_cg/mreview/pipeline.py +29 -1
- package/md_cg/mreview/ruleset.py +16 -1
- package/md_cg/nodefile.py +233 -3
- package/md_cg/pooling.py +23 -1
- package/md_cg/postings.py +298 -0
- package/md_cg/predict.py +89 -9
- package/md_cg/progressive.py +3 -0
- package/md_cg/protect.py +14 -1
- package/md_cg/protocol.py +372 -0
- package/md_cg/provenance.py +262 -0
- package/md_cg/reach.py +453 -0
- package/md_cg/refindex.py +47 -2
- package/md_cg/refine.py +20 -1
- package/md_cg/roleviews.py +89 -0
- package/md_cg/routing.py +76 -0
- package/md_cg/scrub.py +63 -2
- package/md_cg/security.py +26 -1
- package/md_cg/self_state.py +64 -1
- package/md_cg/selfreport.py +151 -0
- package/md_cg/semantic/canonical.py +5 -0
- package/md_cg/semantic/en_normalizer.py +364 -355
- package/md_cg/semantic/zh_en_atoms.py +139 -136
- package/md_cg/signer.py +41 -1
- package/md_cg/sources.py +583 -547
- package/md_cg/statushdr.py +179 -0
- package/md_cg/stg.py +59 -18
- package/md_cg/subgraph.py +23 -0
- package/md_cg/sustain.py +56 -1
- package/md_cg/tasks.py +26 -2
- package/md_cg/test_autonomy.py +26 -0
- package/md_cg/test_bench_governance.py +102 -0
- package/md_cg/test_blindspot_tickets.py +166 -0
- package/md_cg/test_ccgc.py +10 -0
- package/md_cg/test_codeindex.py +338 -0
- package/md_cg/test_comment_gate.py +187 -0
- package/md_cg/test_cond_compose_anchors.py +76 -0
- package/md_cg/test_condition_anchor.py +82 -0
- package/md_cg/test_d_meta.py +412 -0
- package/md_cg/test_datapath_root.py +188 -0
- package/md_cg/test_gain_gate.py +47 -1
- package/md_cg/test_hot_cold.py +187 -0
- package/md_cg/test_hyperedge.py +245 -0
- package/md_cg/test_linkref.py +306 -0
- package/md_cg/test_md_access_parity.py +15 -3
- package/md_cg/test_mr_m1.py +108 -18
- package/md_cg/test_mr_m3.py +8 -1
- package/md_cg/test_p26_refindex.py +49 -20
- package/md_cg/test_p27_docindex.py +236 -2
- package/md_cg/test_p2_mcp.py +1 -1
- package/md_cg/test_p31_insight.py +24 -0
- package/md_cg/test_p44_md_whitebox.py +14 -1
- package/md_cg/test_protocol.py +243 -0
- package/md_cg/test_reach.py +378 -0
- package/md_cg/test_reach_keys.py +201 -0
- package/md_cg/test_reach_meta_exits.py +145 -0
- package/md_cg/test_read_clip.py +8 -4
- package/md_cg/test_retr_s1.py +340 -0
- package/md_cg/test_retr_s1b.py +209 -0
- package/md_cg/test_retr_s3.py +194 -0
- package/md_cg/test_retr_s4.py +163 -0
- package/md_cg/test_retr_s5.py +200 -0
- package/md_cg/test_retr_s6.py +157 -0
- package/md_cg/test_retr_s7.py +385 -0
- package/md_cg/test_retr_s8_time.py +316 -0
- package/md_cg/test_retr_s9_edges.py +286 -0
- package/md_cg/test_retr_s9_entity_ctx.py +175 -0
- package/md_cg/test_review_conformance.py +59 -2
- package/md_cg/test_role_views.py +354 -0
- package/md_cg/test_trust.py +361 -0
- package/md_cg/test_units_poll.py +71 -0
- package/md_cg/test_v14_fixes.py +397 -0
- package/md_cg/test_validity_filter.py +280 -0
- package/md_cg/test_wisdom_md_store.py +7 -3
- package/md_cg/test_writepipe.py +5 -1
- package/md_cg/theory.py +16 -1
- package/md_cg/tokens.py +40 -8
- package/md_cg/tool_face.py +13 -2
- package/md_cg/trust.py +943 -0
- package/md_cg/twophase.py +12 -1
- package/md_cg/units.py +129 -10
- package/md_cg/vision_evidence.py +24 -1
- package/md_cg/weights.py +24 -1
- package/md_cg/whitebox.py +32 -1
- package/md_cg/whitebox_kb/data/verify_cache.json +21210 -365
- package/md_cg/whitebox_kb/data/verify_savings.jsonl +5078 -0
- package/md_cg/whitebox_kb/wisdom/audit_log/chain_heat.json +10 -10
- package/md_cg/whitebox_kb/wisdom/code_compose.py +113 -6
- package/md_cg/whitebox_kb/wisdom/code_solidified.json +1 -1
- package/md_cg/whitebox_kb/wisdom/verifier.py +340 -55
- package/md_cg/whitebox_kb/wisdom/wisdom-book-cloud.db +0 -0
- package/md_cg/writelimit.py +18 -4
- package/md_cg/writepipe.py +178 -7
- package/package.json +2 -2
- package/skills/skills/designer-perspective/scripts/__pycache__/designer.cpython-310.pyc +0 -0
- package/skills/skills/designer-perspective/scripts/designer.py +17 -1
- package/skills/skills/designer-perspective/tests/selftest.py +3 -1
- package/src/hooks.ts +17 -21
- package/src/index.ts +33 -6
- package/src/lib/datapath.ts +211 -13
- package/src/lib/mdcg_client.ts +12 -8
- package/src/lib/mutual.ts +411 -411
- package/src/lib/token_store.ts +4 -5
- package/zcode/AGENTS.md +196 -195
- package/zcode/README.md +4 -0
- package/md_cg/whitebox_kb/wisdom/wisdom-book-cloud.db-shm +0 -0
- package/md_cg/whitebox_kb/wisdom/wisdom-book-cloud.db-wal +0 -0
|
@@ -24,6 +24,7 @@ from __future__ import annotations
|
|
|
24
24
|
import ast
|
|
25
25
|
import copy
|
|
26
26
|
import hashlib
|
|
27
|
+
import importlib
|
|
27
28
|
import json
|
|
28
29
|
import os
|
|
29
30
|
import subprocess
|
|
@@ -171,7 +172,16 @@ class VerifyResult:
|
|
|
171
172
|
# v6(2026-08-31):cases 形态修复(list→tuple)后指纹归一化同值,旧 False
|
|
172
173
|
# 条目对修复免疫;bump 版本作废全部旧缓存——长驻进程内存快照复活毒条目的
|
|
173
174
|
# 终局解(版本不符整体清空,任何进程加载即重建)。
|
|
174
|
-
VERIFIER_VERSION =
|
|
175
|
+
VERIFIER_VERSION = 10 # v10:结构规范的字面匹配改大小写不敏感——该表是语言无关的
|
|
176
|
+
# 任务语义判据(要求去重结构出现),字面取自 Python 命名习惯
|
|
177
|
+
# (set/seen),JS 标准 API 写作 `new Set(arr)`,敏感匹配把正确
|
|
178
|
+
# 实现判为「未见对应结构」(实测 code-js/去重 假失败)。v9:
|
|
179
|
+
# Python 模板(含 {fn} 占位符)先展开占位符再走 Python
|
|
180
|
+
# 判据——逐字复用 _render_placeholders(模板不再降级为文本判据,
|
|
181
|
+
# R1/R2/R3/R4 全量生效)。v8:非 Python 源码改走文本判据(Rust/JS
|
|
182
|
+
# 不再静默跳过假通过)+ `_audit_all` 审计面纳入语言单元表;
|
|
183
|
+
# v7:R3/R4 由死代码转为软模式恒执行 + 判据校准(入口/窗口/顶层)
|
|
184
|
+
# + evidence 诚实化——判据变更必须 bump,否则旧判定被缓存复用
|
|
175
185
|
_CACHE_VERSION_KEY = "_verifier_version"
|
|
176
186
|
_STATS_KEY = "_stats"
|
|
177
187
|
|
|
@@ -366,6 +376,34 @@ class VerifyCache:
|
|
|
366
376
|
|
|
367
377
|
# ============ 三、校验器 ============
|
|
368
378
|
|
|
379
|
+
#: 唯一真源(codeindex._body_comments)的解析缓存:None=未解析,False=不可用
|
|
380
|
+
_CI_BODY = None
|
|
381
|
+
|
|
382
|
+
|
|
383
|
+
def _ci_body_comments():
|
|
384
|
+
"""解析并缓存 `codeindex._body_comments`(body 窗口的唯一真源)。
|
|
385
|
+
|
|
386
|
+
三种加载形态各试一次;全部失败返回 None(调用方走等价兜底)。
|
|
387
|
+
缓存避免每次校验都付一次 import 代价(暖缓存路径要求零额外开销)。
|
|
388
|
+
"""
|
|
389
|
+
global _CI_BODY
|
|
390
|
+
if _CI_BODY is None:
|
|
391
|
+
fn = False
|
|
392
|
+
cands = ["md_cg.codeindex"]
|
|
393
|
+
pkg = __package__ or ""
|
|
394
|
+
if pkg.count(".") >= 2: # 如 md_cg.whitebox_kb.wisdom
|
|
395
|
+
cands.insert(0, pkg.rsplit(".", 1)[0].rsplit(".", 1)[0] + ".codeindex")
|
|
396
|
+
for mod in cands:
|
|
397
|
+
try:
|
|
398
|
+
fn = getattr(importlib.import_module(mod), "_body_comments", False)
|
|
399
|
+
except Exception: # noqa: BLE001
|
|
400
|
+
continue
|
|
401
|
+
if fn:
|
|
402
|
+
break
|
|
403
|
+
_CI_BODY = fn or False
|
|
404
|
+
return _CI_BODY or None
|
|
405
|
+
|
|
406
|
+
|
|
369
407
|
class Verifier:
|
|
370
408
|
"""本地校验器:六层校验链,零 LLM。"""
|
|
371
409
|
|
|
@@ -643,7 +681,12 @@ class Verifier:
|
|
|
643
681
|
if kw in req.task:
|
|
644
682
|
if kw in ("反转",) and "字典" in req.task:
|
|
645
683
|
continue # 字典反转:值变键语义,无序列反转结构
|
|
646
|
-
|
|
684
|
+
# 大小写不敏感:本表是**语言无关的任务语义判据**(要求「去重结构
|
|
685
|
+
# 出现」这一事实),而字面取自 Python 命名习惯(set/seen)——
|
|
686
|
+
# JS 标准 API 写作 `new Set(arr)`,敏感匹配把正确实现判为
|
|
687
|
+
# 「未见对应结构」(实测 code-js/去重 假失败,v10 修正)。
|
|
688
|
+
# 放宽仅限大小写,令牌本身仍必须出现(不弱化判据强度)。
|
|
689
|
+
hit = any(p.lower() in req.code.lower() for p in patterns)
|
|
647
690
|
if not hit and not req.expected_structure.get("structure_optional"):
|
|
648
691
|
errors.append(f"任务含「{kw}」但代码未见对应结构 {patterns}")
|
|
649
692
|
|
|
@@ -666,12 +709,19 @@ class Verifier:
|
|
|
666
709
|
# 条件论注释规范(WB-SPEC v1.2,用户条件论要求):
|
|
667
710
|
# R1 函数/类等抽象的总体功能必须中文注释(docstring 或紧跟 # 中文注释)
|
|
668
711
|
# R2 注释须为中文语境(英文缩写嵌入中文说明;纯英文注释行违规)
|
|
712
|
+
# R3/R4(六要素契约)恒执行并回报达标率——见 _check_comment_spec 的纪律说明
|
|
713
|
+
self._comment_notes = []
|
|
714
|
+
self._ccg_stat = {}
|
|
669
715
|
errors.extend(self._check_comment_spec(req))
|
|
670
716
|
|
|
671
717
|
if errors:
|
|
672
|
-
return {"level": "规范符合性", "ok": False, "
|
|
673
|
-
|
|
674
|
-
|
|
718
|
+
return {"level": "规范符合性", "ok": False, "ccg": self._ccg_stat,
|
|
719
|
+
"evidence": "; ".join(errors)}
|
|
720
|
+
# evidence 必须**如实**:改造前无论 R3/R4 是否执行都写「条件论注释通过」,
|
|
721
|
+
# 等于把「没检查」冒充「检查通过」(固化记录里留下了这类伪证)。
|
|
722
|
+
detail = ";".join(self._comment_notes) or "R3/R4 无可检符号"
|
|
723
|
+
return {"level": "规范符合性", "ok": True, "ccg": self._ccg_stat,
|
|
724
|
+
"evidence": f"结构规范 + 语义对齐 + 条件论注释 R1/R2 通过({detail})"}
|
|
675
725
|
|
|
676
726
|
def _check_comment_spec(self, req: VerifyRequest) -> list:
|
|
677
727
|
"""条件论注释规范(WB-SPEC v1.2):R1 函数/类中文注释 + R2 注释中文语境。
|
|
@@ -680,6 +730,8 @@ class Verifier:
|
|
|
680
730
|
紧跟的 # 中文注释描述其总体功能。
|
|
681
731
|
R2:注释行必须为中文语境(含中文字符)——英文缩写嵌入中文说明;
|
|
682
732
|
纯英文注释行(无任何中文)违规。
|
|
733
|
+
非 Python 源码分两档(见下方 except 分支):Python 模板(含 `{fn}` 占位符)
|
|
734
|
+
展开后走本函数的**全量**判据;真·非 Python(Rust/JS)走文本判据。
|
|
683
735
|
expected_structure.no_comment_spec=True 可豁免(如注入型组装片段)。
|
|
684
736
|
"""
|
|
685
737
|
if req.expected_structure.get("no_comment_spec"):
|
|
@@ -688,7 +740,26 @@ class Verifier:
|
|
|
688
740
|
try:
|
|
689
741
|
tree = ast.parse(code)
|
|
690
742
|
except SyntaxError:
|
|
691
|
-
|
|
743
|
+
# 分支 A·Python 模板:`*_UNITS` 的 pattern 是**带占位符的模板**
|
|
744
|
+
# (`def {fn}(arr):`),ast.parse 必然 SyntaxError。改造前这类模板
|
|
745
|
+
# 与 Rust/JS 一样落到 `return []` 静默跳过;v8 起落到文本判据——
|
|
746
|
+
# 但那是**降级判据**(只查三要素字面,不查 R1 逐符号中文注释 / R2
|
|
747
|
+
# 纯英文注释行)。故此优先展开占位符再进 Python 判据(v9),
|
|
748
|
+
# 使 Python 模板与六域单元获得同强度判据。
|
|
749
|
+
rendered = self._render_placeholders(code, req)
|
|
750
|
+
if rendered != code:
|
|
751
|
+
try:
|
|
752
|
+
tree = ast.parse(rendered)
|
|
753
|
+
except SyntaxError:
|
|
754
|
+
return self._check_comment_spec_text(req)
|
|
755
|
+
code = rendered
|
|
756
|
+
else:
|
|
757
|
+
# 分支 B·真非 Python 源码(Rust / JavaScript 模板):ast 无法解析。
|
|
758
|
+
# 改造前此处 `return []` = **静默跳过**,等于给多语言单元发
|
|
759
|
+
# 「假通过」——实测 RUST_UNITS/JS_UNITS 各 5 单元的 CCG 契约 0/5
|
|
760
|
+
# 达标,而 verifier 报「R3 缺 0」(`_ccg_stat` 留空、不进分母)
|
|
761
|
+
# → 审计报告完全隐身。
|
|
762
|
+
return self._check_comment_spec_text(req)
|
|
692
763
|
src_lines = code.splitlines()
|
|
693
764
|
_has_cn = lambda s: any('\u4e00' <= c <= '\u9fff' for c in s)
|
|
694
765
|
errs = []
|
|
@@ -719,62 +790,203 @@ class Verifier:
|
|
|
719
790
|
bad_lines.append(f"L{i}")
|
|
720
791
|
if bad_lines:
|
|
721
792
|
errs.append(f"纯英文注释行(条件论 R2,须中文说明): {bad_lines[:5]}")
|
|
722
|
-
# R3
|
|
723
|
-
#
|
|
724
|
-
#
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
#
|
|
730
|
-
#
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
793
|
+
# R3/R4:CCG 六要素注释——**默认软模式恒执行**(2026-09-17 修复)。
|
|
794
|
+
#
|
|
795
|
+
# 改造前的缺陷(「规范没有被执行」的机械根因,用户 2026-09-17 定性):
|
|
796
|
+
# ① R3/R4 被包在 `require_cond_comment` / `require_not_cond` 开关内,
|
|
797
|
+
# 而全仓**无任何调用方**传入这两个键 → 判据为**死代码**,恒不执行;
|
|
798
|
+
# ② 但 evidence 恒写「条件论注释通过」——R1/R2 通过即谎报六要素达标,
|
|
799
|
+
# 于是 code_solidified.json 留下「零标记却通过」的**伪证**;
|
|
800
|
+
# ③ 判据自身只查**首个** def、窗口只覆盖 def 前 2 行——与既有单元库
|
|
801
|
+
# 104+ 处标记的实际形态(`# 生效条件:` 紧跟 def 行之后)物理不相容。
|
|
802
|
+
# 现纪律:软模式**恒执行**并把达标率写入 evidence(诚实报告,不静默),
|
|
803
|
+
# 硬拦截仅在开关被显式传入时(全库升级完成后由调用方开启)。
|
|
804
|
+
r3_missing, total = self._cond_comment_missing(tree, src_lines)
|
|
805
|
+
if total:
|
|
806
|
+
self._comment_notes.append(
|
|
807
|
+
f"R3 三要素 {total - len(r3_missing)}/{total} 达标")
|
|
808
|
+
r4_missing, _ = self._not_cond_missing(tree, src_lines)
|
|
809
|
+
# 结构化留痕:软模式不拦截,若只把结论写进 evidence **字符串**,
|
|
810
|
+
# `--audit` 就无法机械汇总缺失清单——「规范没被执行」正是这样隐身的。
|
|
811
|
+
self._ccg_stat = {"r3_total": total, "r3_missing": r3_missing,
|
|
812
|
+
"r4_missing": r4_missing}
|
|
813
|
+
if r3_missing:
|
|
814
|
+
msg = f"条件论三要素注释缺失(R3): {r3_missing[:5]}"
|
|
815
|
+
if req.expected_structure.get("require_cond_comment"):
|
|
816
|
+
errs.append(msg)
|
|
817
|
+
else:
|
|
818
|
+
self._comment_notes.append(msg + "(软模式,不拦截)")
|
|
819
|
+
if r4_missing:
|
|
820
|
+
msg = f"不适用条件缺失(R4·代码语义条件协议): {r4_missing[:5]}"
|
|
821
|
+
if req.expected_structure.get("require_not_cond"):
|
|
822
|
+
errs.append(msg)
|
|
823
|
+
else:
|
|
824
|
+
self._comment_notes.append(msg + "(软模式,不拦截)")
|
|
735
825
|
return errs
|
|
736
826
|
|
|
737
|
-
def
|
|
738
|
-
"""
|
|
827
|
+
def _render_placeholders(self, code: str, req: VerifyRequest) -> str:
|
|
828
|
+
"""展开单元模板占位符(`{fn}` → 具体标识符),使模板可被 ast 解析。
|
|
739
829
|
|
|
740
|
-
|
|
741
|
-
|
|
830
|
+
为什么需要:`*_UNITS` 的 pattern 是模板形态(`def {fn}(arr):`),
|
|
831
|
+
未经 `code_compose.compose_code` 替换前**不是合法 Python**——直接
|
|
832
|
+
ast.parse 必 SyntaxError,判据就会降级(v7 静默跳过 / v8 文本判据)。
|
|
833
|
+
展开后 template 与「运行时真实生成的代码」同形,R1/R2/R3/R4 全量判据
|
|
834
|
+
才真正作用于模板本体(v9)。
|
|
835
|
+
|
|
836
|
+
占位符名真源:`expected_structure["params"]`(调用方传单元声明的参数名),
|
|
837
|
+
缺省回退约定名 `("fn",)`(`code_compose` 第 17 行声明的模板约定)。
|
|
838
|
+
只替换**声明过的**占位符——不做正则通配,避免误伤 f-string / dict 字面量
|
|
839
|
+
(`{x}`、`{1: 1}` 形态)。
|
|
840
|
+
返回值与输入相同 = 无占位符可展开(调用方据此判定「真·非 Python」)。
|
|
742
841
|
"""
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
窗口 [lineno-2 : lineno+6]:三要素注释为 def 后 3-4 行。
|
|
842
|
+
names = req.expected_structure.get("params") or ("fn",)
|
|
843
|
+
out = code
|
|
844
|
+
for p in names:
|
|
845
|
+
out = out.replace("{" + str(p) + "}", "solve")
|
|
846
|
+
return out
|
|
847
|
+
|
|
848
|
+
def _check_comment_spec_text(self, req: VerifyRequest) -> list:
|
|
849
|
+
"""非 Python 源码的 CCG 契约判据(Rust / JavaScript,`//` 或 `#` 注释)。
|
|
850
|
+
|
|
851
|
+
存在理由:`ast.parse` 对非 Python 源码抛 `SyntaxError`。改造前该分支直接
|
|
852
|
+
`return []` = **静默跳过**——多语言单元在校验器眼里永远「合规」,
|
|
853
|
+
`_ccg_stat` 留空不进分母,缺口在审计报告里完全隐身
|
|
854
|
+
(实测:verifier 报 R3 缺 0,文本实缺 5/5)。
|
|
855
|
+
|
|
856
|
+
判据与 Python 分支**同口径**(分层不重复):
|
|
857
|
+
· 入口符号 = 首个非空非注释行(Rust `fn {fn}(...)`、JS `function {fn}(...)`);
|
|
858
|
+
· 注释块 = 入口行之后的连续 `//`/`#` 行;
|
|
859
|
+
· R3 三要素(生效条件/子功能/执行)+ R4 不适用条件——软模式,恒执行;
|
|
860
|
+
· R1/R2 精神:注释块须含中文(缺失则硬拦截,与 Python 分支一致)。
|
|
763
861
|
"""
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
if
|
|
862
|
+
lines = req.code.splitlines()
|
|
863
|
+
entry = next((i for i, ln in enumerate(lines)
|
|
864
|
+
if ln.strip() and not ln.strip().startswith(("//", "#"))),
|
|
865
|
+
None)
|
|
866
|
+
if entry is None:
|
|
769
867
|
return []
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
868
|
+
block = []
|
|
869
|
+
for ln in lines[entry + 1:]:
|
|
870
|
+
s = ln.strip()
|
|
871
|
+
if s.startswith(("//", "#")):
|
|
872
|
+
block.append(s.lstrip("/#").strip())
|
|
873
|
+
elif not s:
|
|
874
|
+
continue
|
|
875
|
+
else:
|
|
876
|
+
break
|
|
877
|
+
joined = " ".join(block)
|
|
878
|
+
_has_cn = lambda s: any('\u4e00' <= c <= '\u9fff' for c in s)
|
|
879
|
+
errs = []
|
|
880
|
+
if not block or not _has_cn(joined):
|
|
881
|
+
errs.append(f"入口符号缺中文注释(条件论 R1/R2): L{entry + 1}")
|
|
776
882
|
need = ("生效条件", "子功能", "执行")
|
|
777
|
-
|
|
883
|
+
r3_missing = [k for k in need if k not in joined]
|
|
884
|
+
r4_missing = [] if "不适用条件" in joined else [f"L{entry + 1}"]
|
|
885
|
+
# 与 Python 分支同款结构化留痕(分母记 1 = 入口符号,不是「整个文件」)
|
|
886
|
+
self._ccg_stat = {"r3_total": 1, "r3_missing": r3_missing,
|
|
887
|
+
"r4_missing": r4_missing, "mode": "text"}
|
|
888
|
+
if r3_missing:
|
|
889
|
+
msg = f"条件论三要素注释缺失(R3·文本判据): {r3_missing}"
|
|
890
|
+
if req.expected_structure.get("require_cond_comment"):
|
|
891
|
+
errs.append(msg)
|
|
892
|
+
else:
|
|
893
|
+
self._comment_notes.append(msg + "(软模式,不拦截)")
|
|
894
|
+
if r4_missing:
|
|
895
|
+
msg = "不适用条件缺失(R4·文本判据)"
|
|
896
|
+
if req.expected_structure.get("require_not_cond"):
|
|
897
|
+
errs.append(msg)
|
|
898
|
+
else:
|
|
899
|
+
self._comment_notes.append(msg + "(软模式,不拦截)")
|
|
900
|
+
return errs
|
|
901
|
+
|
|
902
|
+
def _ccg_scope(self, tree) -> list:
|
|
903
|
+
"""R3/R4 共同判定面:CCG 注释的**检查对象** = 单元入口(唯一口径)。
|
|
904
|
+
|
|
905
|
+
三处判据校准(2026-09-17,依据 `--audit` 实测暴露的噪声):
|
|
906
|
+
① **只用 `tree.body` 顶层**——改造前用 `ast.walk`,会把**类内方法**
|
|
907
|
+
也展开计入(实测 `Cat` 与 `Cat.speak` 同现、`speak` 重复两次);
|
|
908
|
+
② **排除 `*_test` / `test_*` / `_` 前缀**——测试样例代码与私有辅助
|
|
909
|
+
不是契约对象(实测 `closure_test`/`gen_test`/`with_test` 等被计入分母);
|
|
910
|
+
③ **取首个合格符号 = 单元对外入口**——`*_units.py` 的既有约定:
|
|
911
|
+
pattern 首符号承载「何时可调 / 做什么 / 怎么用」,内部辅助
|
|
912
|
+
(dfs/walk/find/h1/up/down)无外部调用方,由 R1 中文注释覆盖。
|
|
913
|
+
与改造前注释声明的设计意图一致(「检查首个 def/class;内部辅助函数由
|
|
914
|
+
R1 覆盖」),修的是**窗口错位与谎报**,不是检查面。
|
|
915
|
+
"""
|
|
916
|
+
for n in getattr(tree, "body", []):
|
|
917
|
+
if isinstance(n, (ast.FunctionDef, ast.AsyncFunctionDef, ast.ClassDef)):
|
|
918
|
+
nm = n.name
|
|
919
|
+
if (nm.startswith("_") or nm.endswith("_test")
|
|
920
|
+
or nm.startswith("test_")):
|
|
921
|
+
continue
|
|
922
|
+
return [n]
|
|
923
|
+
return []
|
|
924
|
+
|
|
925
|
+
def _ccg_block(self, f, src_lines) -> tuple:
|
|
926
|
+
"""读取单个符号的 CCG 注释块:返回 (注释行文本列表, docstring)。
|
|
927
|
+
|
|
928
|
+
窗口 = def/class 签名行之后、**函数体首个语句之前**的 `#` 注释
|
|
929
|
+
(多行签名的续行非 `#` 起始,自然跳过)+ docstring。
|
|
930
|
+
该窗口即既有单元库 104+ 处标记的实际物理位置(`# 生效条件:` 紧贴
|
|
931
|
+
def 行下一行);`f.body[0].lineno` 保证多行签名下窗口仍正确。
|
|
932
|
+
|
|
933
|
+
**唯一真源**(2026-09-17 裁决 A):窗口计算委托
|
|
934
|
+
`codeindex._body_comments`。历史问题:本函数曾自述「与
|
|
935
|
+
`codeindex._body_comments` 同款语义」,而该名当时**并不存在**——
|
|
936
|
+
悬空引用即第二份真相,已由本次收敛坐实。
|
|
937
|
+
|
|
938
|
+
兜底(第 7 条):本模块有三种加载形态——`-m md_cg.whitebox_kb.wisdom.verifier`、
|
|
939
|
+
顶层 `wisdom.verifier`(wheel 发布态,见 code_compose.py)、直跑
|
|
940
|
+
`python verifier.py`;后两者 import 不到 `md_cg.codeindex`,故保留
|
|
941
|
+
**逐字等价的兜底**,等价性由 `md_cg/test_codeindex.py` 机械守卫。
|
|
942
|
+
"""
|
|
943
|
+
body = getattr(f, "body", None)
|
|
944
|
+
body_lineno = body[0].lineno if body else f.lineno
|
|
945
|
+
raw = None
|
|
946
|
+
fn = _ci_body_comments()
|
|
947
|
+
if fn is not None:
|
|
948
|
+
raw = fn(src_lines, f.lineno, body_lineno)
|
|
949
|
+
if raw is None:
|
|
950
|
+
# 兜底(第 7 条):与唯一真源**逐字等价**,等价性由
|
|
951
|
+
# md_cg/test_codeindex.py 的委托等价断言机械守卫。
|
|
952
|
+
raw = [ln.strip()
|
|
953
|
+
for ln in src_lines[f.lineno:max(f.lineno, body_lineno - 1)]
|
|
954
|
+
if ln.strip().startswith("#")]
|
|
955
|
+
out = [s.lstrip("#").strip() for s in raw]
|
|
956
|
+
doc = (ast.get_docstring(f) or "").strip()
|
|
957
|
+
return out, doc
|
|
958
|
+
|
|
959
|
+
def _cond_comment_missing(self, tree, src_lines) -> tuple:
|
|
960
|
+
"""R3:缺「生效条件/子功能/执行」三要素的符号清单;返回 (missing, total)。
|
|
961
|
+
|
|
962
|
+
要素名真源 = `nodefile.CCG_CONTRACT_ROLES`(六要素接口契约);
|
|
963
|
+
R3 只抽其中三要素——功能名由 R1 覆盖、验证方式与不适用条件由
|
|
964
|
+
`codeindex.render` 落槽(分层不重复,改造前此三要素从未被检查过)。
|
|
965
|
+
"""
|
|
966
|
+
funcs = self._ccg_scope(tree)
|
|
967
|
+
need = ("生效条件", "子功能", "执行")
|
|
968
|
+
missing = []
|
|
969
|
+
for f in funcs:
|
|
970
|
+
block, doc = self._ccg_block(f, src_lines)
|
|
971
|
+
joined = " ".join(block) + " " + doc
|
|
972
|
+
if not all(k in joined for k in need):
|
|
973
|
+
missing.append(f.name)
|
|
974
|
+
return missing, len(funcs)
|
|
975
|
+
|
|
976
|
+
def _not_cond_missing(self, tree, src_lines) -> tuple:
|
|
977
|
+
"""R4:缺「不适用条件」的符号清单;返回 (missing, total)。
|
|
978
|
+
|
|
979
|
+
不适用条件 = 拒绝域 rejection_domain(何时**不能**调用)——
|
|
980
|
+
知道边界才敢调用,与「何时能调用」同等重要。
|
|
981
|
+
"""
|
|
982
|
+
funcs = self._ccg_scope(tree)
|
|
983
|
+
missing = []
|
|
984
|
+
for f in funcs:
|
|
985
|
+
block, doc = self._ccg_block(f, src_lines)
|
|
986
|
+
joined = " ".join(block) + " " + doc
|
|
987
|
+
if "不适用条件" not in joined:
|
|
988
|
+
missing.append(f.name)
|
|
989
|
+
return missing, len(funcs)
|
|
778
990
|
|
|
779
991
|
# ---------- ⑥ 集成测试 ----------
|
|
780
992
|
def _check_integration(self, req: VerifyRequest) -> Dict[str, Any]:
|
|
@@ -894,11 +1106,19 @@ def _audit_all() -> None:
|
|
|
894
1106
|
domains = [('compiler', COMPILER_UNITS), ('pylang', PYTHON_UNITS),
|
|
895
1107
|
('graph', GRAPH_UNITS), ('os', OS_UNITS),
|
|
896
1108
|
('browser', BROWSER_UNITS), ('net', NET_UNITS)]
|
|
1109
|
+
# 语言单元表(白箱自举主干):改造前**不在审计面**——`_audit_all` 只遍历六域
|
|
1110
|
+
# `*_units.py`,而模板生产的 CODE_UNITS/RUST_UNITS/JS_UNITS 从不进报告。
|
|
1111
|
+
# 「规范没被执行」正是这样隐身的:生成端零契约 + 审计端不看它。
|
|
1112
|
+
from code_compose import CODE_UNITS, RUST_UNITS, JS_UNITS
|
|
1113
|
+
lang_domains = [('code-py', CODE_UNITS), ('code-rust', RUST_UNITS),
|
|
1114
|
+
('code-js', JS_UNITS)]
|
|
897
1115
|
t0 = time.time()
|
|
898
1116
|
v = Verifier()
|
|
899
1117
|
total = ok = 0
|
|
900
1118
|
fails = []
|
|
901
1119
|
by_domain = {}
|
|
1120
|
+
ccg_total = ccg_missing = 0
|
|
1121
|
+
ccg_gaps = [] # [(uid, r3_missing, r4_missing)] —— 软模式缺失清单
|
|
902
1122
|
for dname, units in domains:
|
|
903
1123
|
d_ok = d_fail = 0
|
|
904
1124
|
for uid, u in units.items():
|
|
@@ -908,6 +1128,18 @@ def _audit_all() -> None:
|
|
|
908
1128
|
cases=list(u.get('cases', [])),
|
|
909
1129
|
expected_structure={'inject': True} if u.get('needs_inject') else {})
|
|
910
1130
|
r = v.verify(req)
|
|
1131
|
+
# CCG 六要素(软模式)汇总:R3/R4 缺失**不拦截**,故不能只看 r.ok——
|
|
1132
|
+
# 必须单独取「规范符合性」check 里的结构化 ccg 块;否则「规范未执行」
|
|
1133
|
+
# 会再次隐身(改造前 evidence 恒写「条件论注释通过」,无从发现)。
|
|
1134
|
+
spec = next((c for c in r.checks if isinstance(c, dict)
|
|
1135
|
+
and c.get("level") == "规范符合性"), None)
|
|
1136
|
+
st = (spec or {}).get("ccg") or {}
|
|
1137
|
+
if st.get("r3_total"):
|
|
1138
|
+
ccg_total += st["r3_total"]
|
|
1139
|
+
ccg_missing += len(st.get("r3_missing") or [])
|
|
1140
|
+
if st.get("r3_missing") or st.get("r4_missing"):
|
|
1141
|
+
ccg_gaps.append((uid, st.get("r3_missing") or [],
|
|
1142
|
+
st.get("r4_missing") or []))
|
|
911
1143
|
if r.ok:
|
|
912
1144
|
ok += 1; d_ok += 1
|
|
913
1145
|
else:
|
|
@@ -917,6 +1149,59 @@ def _audit_all() -> None:
|
|
|
917
1149
|
print(f"=== 全量审计(verifier v{VERIFIER_VERSION}): {ok}/{total} 通过 "
|
|
918
1150
|
f"({100.0 * ok / total:.1f}%) 耗时 {time.time() - t0:.1f}s ===")
|
|
919
1151
|
print("各域:", by_domain)
|
|
1152
|
+
# CCG 六要素报告必须**显式打印**:单元"校验通过"只代表 L1-L3 + R1/R2 过了,
|
|
1153
|
+
# 六要素接口契约是否达成是另一件事——两者混为一谈正是问题根源。
|
|
1154
|
+
rate = (100.0 * (ccg_total - ccg_missing) / ccg_total) if ccg_total else 0.0
|
|
1155
|
+
print(f"CCG 三要素(R3 软模式): {ccg_total - ccg_missing}/{ccg_total} 符号达标"
|
|
1156
|
+
f"({rate:.1f}%) | 有缺失的单元 {len(ccg_gaps)}/{total} 个")
|
|
1157
|
+
if ccg_gaps:
|
|
1158
|
+
print("--- CCG 注释缺失清单(软模式,不拦截)---")
|
|
1159
|
+
for uid, m3, m4 in ccg_gaps:
|
|
1160
|
+
print(f"[{uid}] R3缺={m3[:6]} R4缺={m4[:4]}")
|
|
1161
|
+
# --- 语言单元表(自举主干)CCG 契约审计 ---
|
|
1162
|
+
# 与六域**分开统计**,原因有二:
|
|
1163
|
+
# ① RUST/JS 模板非 Python 语法,L1 语法必失败属正常(校验器与目标语言
|
|
1164
|
+
# 不匹配),此处只问「契约是否写进模板」——走文本判据
|
|
1165
|
+
# (`_check_comment_spec_text`,mode="text");
|
|
1166
|
+
# ② CODE_UNITS 是 Python 模板但含 `{fn}` 占位符(未替换前非法 Python),
|
|
1167
|
+
# v9 起由 `_render_placeholders` 展开后走**全量 Python 判据**
|
|
1168
|
+
# (mode="python":R1 逐符号中文注释 + R2 注释语境 + R3/R4),
|
|
1169
|
+
# 故本节 `params` 必须透传单元声明的占位符名。
|
|
1170
|
+
# 两档判据强度不同但**达标口径一致**(三要素 + 不适用条件齐备)。
|
|
1171
|
+
lang_total = lang_ok = 0
|
|
1172
|
+
lang_gaps = []
|
|
1173
|
+
lang_modes = {} # 判据档位计数(python=全量判据 / text=文本判据)
|
|
1174
|
+
for dname, units in lang_domains:
|
|
1175
|
+
for uid, u in units.items():
|
|
1176
|
+
lang_total += 1
|
|
1177
|
+
r = v.verify(VerifyRequest(task=u.get('task', ''), code=u['pattern'],
|
|
1178
|
+
unit_id=uid, cases=[],
|
|
1179
|
+
expected_structure={
|
|
1180
|
+
'params': u.get('params') or ['fn']}))
|
|
1181
|
+
spec = next((c for c in r.checks if isinstance(c, dict)
|
|
1182
|
+
and c.get("level") == "规范符合性"), None) or {}
|
|
1183
|
+
st = spec.get("ccg") or {}
|
|
1184
|
+
r3m = st.get("r3_missing") or []
|
|
1185
|
+
r4m = st.get("r4_missing") or []
|
|
1186
|
+
md = st.get("mode", "python")
|
|
1187
|
+
lang_modes[md] = lang_modes.get(md, 0) + 1
|
|
1188
|
+
# 「达标」= 判据产出 + 无硬失败 + 契约齐备,三者缺一不可——
|
|
1189
|
+
# 只统计 ccg 缺失会漏报 R1/R2 硬失败(spec.ok=False 但 ccg 齐备
|
|
1190
|
+
# 仍会被算作达标)= 新的谎报面(同 v7「没检查冒充检查通过」同构)。
|
|
1191
|
+
if not st.get("r3_total"):
|
|
1192
|
+
lang_gaps.append((dname, uid, md, "判据未产出(ccg 块为空)"))
|
|
1193
|
+
elif not spec.get("ok"):
|
|
1194
|
+
lang_gaps.append((dname, uid, md,
|
|
1195
|
+
f"硬失败: {str(spec.get('evidence'))[:90]}"))
|
|
1196
|
+
elif r3m or r4m:
|
|
1197
|
+
lang_gaps.append((dname, uid, md, f"R3缺={r3m} R4缺={r4m}"))
|
|
1198
|
+
else:
|
|
1199
|
+
lang_ok += 1
|
|
1200
|
+
lrate = (100.0 * lang_ok / lang_total) if lang_total else 0.0
|
|
1201
|
+
print(f"=== 语言单元表 CCG 契约: {lang_ok}/{lang_total} 达标({lrate:.1f}%)"
|
|
1202
|
+
f" | 判据档位 {lang_modes}(python=展开占位符后全量判据 / text=文本判据)===")
|
|
1203
|
+
for dname, uid, md, desc in lang_gaps:
|
|
1204
|
+
print(f" [{dname}/{uid}] mode={md} {desc}")
|
|
920
1205
|
print(f"缓存: 共 {s['total']} 条(通过 {s['passed']} / 失败 {s['failed']})"
|
|
921
1206
|
f" | 本进程命中 {s['hits']} / 未命中 {s['misses']}(命中率 {s['hit_rate']}%)")
|
|
922
1207
|
if fails:
|
|
Binary file
|
package/md_cg/writelimit.py
CHANGED
|
@@ -51,12 +51,14 @@ _RE_WS = re.compile(r"\s+")
|
|
|
51
51
|
_RE_TITLE = re.compile(r"^\s*#\s*功能名[::]\s*(.+?)\s*$", re.M)
|
|
52
52
|
|
|
53
53
|
|
|
54
|
+
# 生效条件:读环境变量 MDCG_WRITELIMIT(缺省 "1"),strip 并 lower 后不在 ("0", "false", "no") 中即返回 True——默认开启,仅显式关闭值才停用;
|
|
54
55
|
def enabled() -> bool:
|
|
55
56
|
"""总开关:env MDCG_WRITELIMIT=0/false/no 时关闭所有限流。"""
|
|
56
57
|
return os.environ.get("MDCG_WRITELIMIT", "1").strip().lower() \
|
|
57
58
|
not in ("0", "false", "no")
|
|
58
59
|
|
|
59
60
|
|
|
61
|
+
# 生效条件:形参 content 为假值返回 None;否则 strip 后若 _RE_TITLE 命中且标题骨架 s 非空且 len(s)>=MIN_SKELETON 返回 "t:"+s,否则返回 None;无标题且 len(text)>=MAX_CONTENT 返回 None;无标题且 len(text)<MAX_CONTENT 时全文骨架 s 非空且 len(s)>=MIN_SKELETON 返回 s,否则返回 None;
|
|
60
62
|
def template_signature(content: str):
|
|
61
63
|
"""同模板流水签名:标题模板优先,其次全文骨架。
|
|
62
64
|
|
|
@@ -80,6 +82,7 @@ def template_signature(content: str):
|
|
|
80
82
|
return s if s and len(s) >= MIN_SKELETON else None
|
|
81
83
|
|
|
82
84
|
|
|
85
|
+
# 生效条件:形参 text 为任意字符串时,依次清除 URL、长十六进制、长数字、数字与空白后返回 s(空串返回空串);
|
|
83
86
|
def _skeleton(text: str) -> str:
|
|
84
87
|
s = _RE_URL.sub(" ", text)
|
|
85
88
|
s = _RE_HEX.sub(" ", s)
|
|
@@ -91,10 +94,12 @@ def _skeleton(text: str) -> str:
|
|
|
91
94
|
|
|
92
95
|
# ---------- 状态持久化 ----------
|
|
93
96
|
|
|
97
|
+
# 生效条件:形参 cg 提供 root 时返回 os.path.join(cg.root, STATE_FILE);
|
|
94
98
|
def _state_path(cg) -> str:
|
|
95
99
|
return os.path.join(cg.root, STATE_FILE)
|
|
96
100
|
|
|
97
101
|
|
|
102
|
+
# 生效条件:形参 cg 使 _state_path(cg) 可读取且 json.load 得到 dict 且 st.get("sigs")/st.get("rate") 均为 dict 时返回 st;否则若读取或解析抛 OSError/ValueError 或该 dict 中 sigs/rate 任一非 dict 则返回 {"sigs": {}, "rate": {}}(顶层非 dict 会在 st.get 处抛 AttributeError);
|
|
98
103
|
def _load(cg) -> dict:
|
|
99
104
|
try:
|
|
100
105
|
with open(_state_path(cg), encoding="utf-8") as f:
|
|
@@ -106,12 +111,14 @@ def _load(cg) -> dict:
|
|
|
106
111
|
return {"sigs": {}, "rate": {}}
|
|
107
112
|
|
|
108
113
|
|
|
114
|
+
# 生效条件:调用方传入 cg 与 st,无任何早退守卫,一经调用即以 json.dumps(st, ensure_ascii=False, sort_keys=True) 在 _state_path(cg) 的 FileLock 内原子写入。
|
|
109
115
|
def _save(cg, st: dict) -> None:
|
|
110
116
|
p = _state_path(cg)
|
|
111
117
|
with FileLock(p):
|
|
112
118
|
atomic_write(p, json.dumps(st, ensure_ascii=False, sort_keys=True))
|
|
113
119
|
|
|
114
120
|
|
|
121
|
+
# 生效条件:调用方传入 st、key、now,st 须含 "rate" 键(st["rate"].get(key, []) 缺该 key 时按空列表处理),按 now - t < RATE_WINDOW 过滤后追加 now,并只回写末尾 RATE_MAX*4 项。
|
|
115
122
|
def _push_rate(st: dict, key: str, now: float) -> None:
|
|
116
123
|
win = [t for t in st["rate"].get(key, []) if now - t < RATE_WINDOW]
|
|
117
124
|
win.append(now)
|
|
@@ -120,6 +127,7 @@ def _push_rate(st: dict, key: str, now: float) -> None:
|
|
|
120
127
|
|
|
121
128
|
# ---------- 写入侧:前置限流 + 同构聚合 ----------
|
|
122
129
|
|
|
130
|
+
# 生效条件:仅当 layer=="contextual" 且 enabled() 为真、且 importance_hint 为 None 或 float(importance_hint)<0.7(转换抛 TypeError/ValueError 时同样继续)才进入限流,此时 now 为 None 取 time.time(),key 取 f"{layer}:{role or 'user'}"(role 为 None/空串等假值回落 'user'):同 key 窗口内条数 ≥ RATE_MAX 返回 DEFER;否则 signature 非空且 st["sigs"] 中该签名记录在 CONVERGE_WINDOW 内、nid 有效且 cg.get(rec["nid"]) 存在时,正文 strip 后完全一致返回 DROP、不一致返回 CONVERGE;其余情形返回 None 放行。
|
|
123
131
|
def check(cg, content, layer="contextual", role=None, node_id=None,
|
|
124
132
|
importance_hint=None, now=None):
|
|
125
133
|
"""remember_gated 前置限流。None=放行;否则 {"verdict": DEFER|CONVERGE}。
|
|
@@ -184,6 +192,7 @@ def check(cg, content, layer="contextual", role=None, node_id=None,
|
|
|
184
192
|
return None
|
|
185
193
|
|
|
186
194
|
|
|
195
|
+
# 生效条件:形参 content 的 template_signature 非空,且 st["sigs"].get(sig) 为 None 或该记录 nid 为假值时,写入 {"nid": node_id, ...} 并保存;否则不写并返回 None;
|
|
187
196
|
def record_accepted(cg, node_id, content, now=None) -> None:
|
|
188
197
|
"""ACCEPT 落盘后确保签名→节点映射存在(check 已预占位,此处兜底)。"""
|
|
189
198
|
sig = template_signature(content)
|
|
@@ -198,6 +207,7 @@ def record_accepted(cg, node_id, content, now=None) -> None:
|
|
|
198
207
|
_save(cg, st)
|
|
199
208
|
|
|
200
209
|
|
|
210
|
+
# 生效条件:cg.get(target) 为假值返回 {"ok": False, "error": "target_missing"};否则以 content 空白归一后截 80 字追加 "【聚合 stamp】" 行、fm["merge_count"]=int(fm.get("merge_count") or 0)+1 后 _write_node 落盘(重要性不变),并返回 {"ok": True, "merge_count": 新值}。
|
|
201
211
|
def converge_into(cg, target: str, content: str) -> dict:
|
|
202
212
|
"""同构聚合落库:正文追加一行【聚合】摘要,merge_count+1。
|
|
203
213
|
|
|
@@ -225,6 +235,7 @@ def converge_into(cg, target: str, content: str) -> dict:
|
|
|
225
235
|
|
|
226
236
|
# ---------- 读侧:已落盘同构组的周期整理(sustain 巡检入口)----------
|
|
227
237
|
|
|
238
|
+
# 生效条件:形参 cg 的 index["nodes"] 中 layer=="contextual" 且 cg.get 可取的节点按 template_signature 非空分组,扫描到 scanned>=limit(limit=0 立即 break)为止,成员数≥min_group 的组按成员数降序进入 planned;apply 为真且 planned 非空时,逐组循环且累计 n_ap>=MAX_APPLY 后不再开新组(组内成员仍可继续 n_ap 累加超过),跳过 protected/immutable 成员,保留每组最早节点并追加整理聚合、成员降权,输出 out(out["planned"] 仅取 planned[:20];apply 假或 planned 空直接返回 out);
|
|
228
239
|
def tidy_contextual(cg, apply=False, min_group=3, actor="sustain_tidy",
|
|
229
240
|
limit=2000) -> dict:
|
|
230
241
|
"""扫描 contextual 层已落盘节点,按模板签名分组并整理。
|
|
@@ -254,11 +265,12 @@ def tidy_contextual(cg, apply=False, min_group=3, actor="sustain_tidy",
|
|
|
254
265
|
for sig, members in groups.items():
|
|
255
266
|
if len(members) < min_group:
|
|
256
267
|
continue
|
|
257
|
-
members.sort(key=lambda m: float((m.get("frontmatter") or {})
|
|
258
|
-
|
|
268
|
+
members.sort(key=lambda m: (float((m.get("frontmatter") or {})
|
|
269
|
+
.get("created_at", 0) or 0),
|
|
270
|
+
str(m.get("id") or "")))
|
|
259
271
|
planned.append({"sig": sig[:40], "keep": members[0]["id"],
|
|
260
272
|
"members": [m["id"] for m in members[1:]]})
|
|
261
|
-
planned.sort(key=lambda p: -len(p["members"]))
|
|
273
|
+
planned.sort(key=lambda p: (-len(p["members"]), str(p.get("sig") or "")))
|
|
262
274
|
out = {"t": time.time(), "actor": actor, "scanned": scanned,
|
|
263
275
|
"groups": len(planned),
|
|
264
276
|
"members": sum(len(p["members"]) for p in planned),
|
|
@@ -301,6 +313,7 @@ def tidy_contextual(cg, apply=False, min_group=3, actor="sustain_tidy",
|
|
|
301
313
|
return out
|
|
302
314
|
|
|
303
315
|
|
|
316
|
+
# 生效条件:e 提供 "id"/"path",对其 frontmatter 副本在 tags 不含 "tidy:converged" 时追加该 tag、importance 置 round(max(0.1, float(fm.get("importance",0.5) or 0)*0.5), 3),经 lifecycle.stamp(fm,"converged",reason=...,actor=actor) 后 _ok 为真时 setdefault(lifecycle.STATE_FIELD,"converged"),随后 cg._write_node 落盘,且 cg.index["nodes"].get(e["id"]) 非 None 时同步 tags/importance/状态字段到索引。
|
|
304
317
|
def _demote(cg, e: dict, actor: str = "sustain_tidy") -> None:
|
|
305
318
|
"""成员降权:tags += tidy:converged,importance×0.5(下限 0.1)。
|
|
306
319
|
|
|
@@ -333,6 +346,7 @@ def _demote(cg, e: dict, actor: str = "sustain_tidy") -> None:
|
|
|
333
346
|
cg._dirty[e["id"]] = entry
|
|
334
347
|
|
|
335
348
|
|
|
349
|
+
# 生效条件:形参 cg 使 _load(cg) 返回 dict 时,返回 enabled()、st["sigs"] 计数、nid 为真的 live_sigs、st["rate"] 键数与 STATE_FILE 组成的摘要;
|
|
336
350
|
def stats(cg) -> dict:
|
|
337
351
|
"""限流状态摘要(诊断面)。"""
|
|
338
352
|
st = _load(cg)
|
|
@@ -340,4 +354,4 @@ def stats(cg) -> dict:
|
|
|
340
354
|
return {"enabled": enabled(), "sigs_tracked": len(st["sigs"]),
|
|
341
355
|
"sigs_live": live_sigs,
|
|
342
356
|
"rate_keys": len(st["rate"]),
|
|
343
|
-
"state_file": STATE_FILE}
|
|
357
|
+
"state_file": STATE_FILE}
|