@furongjun1999/dsh-memory 0.4.7 → 0.4.8
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 +92 -37
- package/codebuddy/CODEBUDDY.md +195 -185
- package/docs/Pi/345/217/257/345/255/246/344/271/240/344/274/230/347/202/271_/347/201/265/346/236/242/345/244/247/350/204/221/346/224/271/350/277/233/344/272/244/346/216/245_20260915.md +144 -0
- package/docs/README.md +111 -0
- package/docs/discipline/harnesses.yaml +215 -152
- package/docs/discipline/templates/full.md.tmpl +60 -58
- package/docs/eval//347/254/254/344/270/211/346/226/271/351/252/214/350/257/201/346/212/245/345/221/212_LoCoMo_/347/201/265/346/236/242_.md +127 -0
- package/docs/eval//347/254/254/344/270/211/346/226/271/351/252/214/350/257/201/346/212/245/345/221/212_LoCoMo_/347/201/265/346/236/242_.png +0 -0
- package/docs/eval//347/254/254/344/270/211/346/226/271/351/252/214/350/257/201/346/212/245/345/221/212_/347/201/265/346/236/242_vs_dejavu_/347/273/237/344/270/200/350/257/204/345/210/206_v7.md +202 -0
- package/docs/experiments/m4-role-probe/census.py +86 -0
- package/docs/experiments/m4-role-probe/probe.py +89 -0
- package/docs/experiments/mapped_confidence/bench_mapped_conf.py +216 -0
- package/docs/experiments/mapped_confidence/post_check.py +75 -0
- package/docs/experiments/mapped_confidence/repro_thirdparty_tol.py +83 -0
- package/docs/experiments/mapped_confidence/result_mapped_conf.json +215 -0
- package/docs/{ → hive/}/350/234/202/345/267/242/345/267/245/344/275/234/350/256/260/345/277/206_/351/241/271/347/233/256/350/256/241/345/210/222.md +14 -2
- package/docs/{README → mdcg/README}/350/257/246/347/273/206/347/211/210_v0.4.5.md +9 -9
- package/docs/{ → mdcg/}/344/270/273/344/273/243/347/220/206/345/255/220/344/273/243/347/220/206/350/256/260/345/277/206/346/236/266/346/236/204/350/256/276/350/256/241.md +1 -1
- 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 +261 -0
- package/docs/{ → mdcg/}/345/215/225/345/205/203/350/207/252/346/210/221/351/224/232/347/202/271_/347/263/273/347/273/237/346/217/220/347/244/272/350/257/215/346/240/207/345/207/206_v0.3.md +1 -1
- 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 +148 -0
- package/docs/{ → mdcg/}/346/272/220/347/240/201/347/272/247/346/236/266/346/236/204/345/256/241/350/256/241_GPT/346/211/271/350/257/204/345/257/271/347/205/247_v1.0.md +2 -2
- package/docs/{ → mdcg/}/347/201/265/346/236/242/344/270/211/345/261/202/346/213/206/345/210/206/350/247/204/345/210/222_v0.1.md +181 -181
- package/docs/{ → mdcg/}/347/274/272/345/217/243/345/215/225_P0/346/224/266/345/217/243_v0.1.md +2 -2
- package/docs/{ → mdcg/}/350/256/244/347/237/245/345/233/276_G4-G8/347/274/272/345/217/243/350/243/201/345/256/232/345/215/225_v0.1.md +1 -1
- package/docs/{ → mdcg/}/350/256/244/347/237/245/345/233/276_/347/264/242/345/274/225/344/270/216/345/267/245/347/250/213/350/247/204/350/214/203/345/214/226_/350/256/241/345/210/222_v0.1.md +4 -4
- package/docs/{ → mdcg/}/350/256/260/345/277/206/346/223/215/344/275/234/347/263/273/347/273/237_MdCGOS/344/270/216MCP/346/216/245/345/205/245_v0.1.md +2 -2
- package/docs/plans/GridWorld/346/234/200/345/260/217/351/227/255/347/216/257/350/247/204/346/240/274_v0.1.md +176 -0
- package/docs/{ → swarm/}/350/234/202/347/276/244/344/272/222/350/201/224_v0.1.md +3 -3
- package/docs/swarm//350/234/202/347/276/244/345/220/214/351/224/231/346/243/200/346/265/213/345/256/236/351/252/214/345/215/217/350/256/256_v0.1.md +242 -0
- package/docs/theory//345/215/225/347/272/277/347/250/213/344/270/216/346/263/250/346/204/217/345/212/233/351/233/206/344/270/255_/346/227/240/344/272/211/350/256/256/347/220/206/350/256/272/346/226/207/346/241/243_v1.0.md +129 -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 +134 -0
- package/docs/theory//346/246/202/345/277/265/345/210/206/345/261/202/345/257/271/351/275/220/350/241/250_v0.1.md +145 -0
- 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/}/347/220/206/350/256/272/344/273/223/346/213/206/345/210/206/344/270/216/347/231/275/347/256/261/347/237/245/350/257/206/345/272/223/345/206/205/350/277/201_v0.1.md +1 -1
- 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 +221 -0
- package/docs/world_model//344/270/226/347/225/214/346/250/241/345/236/213_/347/245/236/347/273/217/347/275/221/347/273/234/345/272/225/345/261/202/346/236/266/346/236/204_v1.0.md +237 -0
- package/docs//345/255/230/347/256/227/344/270/200/344/275/223/346/236/266/346/236/204/345/255/246/344/271/240_/345/244/226/351/203/250/347/220/206/350/256/272/345/257/271/347/205/247/344/270/216/350/267/257/347/272/277/344/272/244/346/216/245_20260916.md +98 -0
- package/docs//345/255/230/347/256/227/344/270/200/344/275/223/347/245/236/347/273/217/347/275/221/347/273/234/346/236/266/346/236/204_/346/200/273/347/272/262/344/270/216/344/272/244/346/216/245_20260916.md +109 -0
- 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 +34 -6
- package/docs//347/231/275/347/256/261/345/214/226/347/272/262/351/242/206_/344/270/211/351/241/271/347/233/256/345/255/246/344/271/240/346/200/273/346/236/266/346/236/204/344/270/216Ghidra/344/272/244/346/216/245_20260916.md +104 -0
- package/docs//350/256/260/345/277/206/350/257/204/345/256/241/347/263/273/347/273/237_/347/253/213/351/241/271/350/256/276/350/256/241/344/270/216/346/226/275/345/267/245/344/272/244/346/216/245_20260915.md +183 -0
- package/dsh/README.md +1 -1
- package/lib/hooks.d.ts +5 -0
- package/lib/hooks.js +10 -1
- package/lib/lib/prompt_safety.d.ts +48 -0
- package/lib/lib/prompt_safety.js +62 -0
- package/md_cg/audit.py +76 -2
- package/md_cg/bench_membench.py +10 -5
- package/md_cg/bench_progressive.py +48 -9
- package/md_cg/branches.py +275 -0
- package/md_cg/ccgc.py +884 -0
- package/md_cg/conformance.py +662 -0
- package/md_cg/consistency.py +1 -1
- package/md_cg/corpus.py +1 -1
- package/md_cg/eval_common.py +6 -5
- package/md_cg/forgetting.py +11 -3
- package/md_cg/fsutil.py +63 -1
- package/md_cg/identity.py +2 -2
- package/md_cg/insight.py +2 -2
- package/md_cg/lifecycle.py +262 -0
- package/md_cg/mcp_server.py +547 -140
- package/md_cg/mdcg.py +206 -18
- package/md_cg/mdcos.py +512 -50
- package/md_cg/metacognition.py +2 -2
- package/md_cg/mreview/__init__.py +25 -0
- package/md_cg/mreview/__main__.py +107 -0
- package/md_cg/mreview/bundle.py +170 -0
- package/md_cg/mreview/candidates.py +253 -0
- package/md_cg/mreview/govern.py +674 -0
- package/md_cg/mreview/locate.py +905 -0
- package/md_cg/mreview/pipeline.py +701 -0
- package/md_cg/mreview/rules/duplication.json +21 -0
- package/md_cg/mreview/rules/field_coverage.json +54 -0
- package/md_cg/mreview/rules/source_license.json +21 -0
- package/md_cg/mreview/rules/template_flow.json +21 -0
- package/md_cg/mreview/ruleset.py +238 -0
- package/md_cg/nodefile.py +38 -0
- package/md_cg/predict.py +24 -6
- package/md_cg/protect.py +4 -4
- package/md_cg/refindex.py +41 -2
- package/md_cg/scrub.py +5 -1
- package/md_cg/self_state.py +1 -1
- package/md_cg/semantic/en_normalizer.py +85 -25
- package/md_cg/sustain.py +18 -0
- package/md_cg/tasks.py +447 -0
- package/md_cg/test_action_derive.py +203 -0
- package/md_cg/test_audit_rotate.py +270 -0
- package/md_cg/test_branches.py +249 -0
- package/md_cg/test_ccg_perturb.py +1 -1
- package/md_cg/test_ccgc.py +423 -0
- package/md_cg/test_conformance.py +343 -0
- package/md_cg/test_en_pipeline.py +24 -0
- package/md_cg/test_health_scale.py +173 -0
- package/md_cg/test_identity_attribution.py +6 -0
- package/md_cg/test_index_durability.py +224 -0
- package/md_cg/test_lifecycle.py +309 -0
- package/md_cg/test_mr_m1.py +679 -0
- package/md_cg/test_mr_m2.py +587 -0
- package/md_cg/test_mr_m3.py +703 -0
- package/md_cg/test_mr_m4.py +485 -0
- package/md_cg/test_p1.py +1 -1
- package/md_cg/test_p11_consistency.py +1 -1
- package/md_cg/test_p12_metacognition.py +2 -2
- package/md_cg/test_p16_self_state.py +1 -1
- package/md_cg/test_p26_refindex.py +1 -1
- package/md_cg/test_p27_docindex.py +22 -2
- package/md_cg/test_p28_refcheck.py +1 -1
- package/md_cg/test_p29_session_ingest_export.py +1 -1
- package/md_cg/test_p2_mcp.py +7 -1
- package/md_cg/test_p38_contextualize.py +1 -1
- package/md_cg/test_p39_vision_evidence.py +1 -1
- package/md_cg/test_p40_refine_worklist.py +1 -1
- package/md_cg/test_p41_evolve_patrol.py +1 -1
- package/md_cg/test_p42_provenance.py +1 -1
- package/md_cg/test_p43_pooling.py +41 -13
- package/md_cg/test_read_clip.py +137 -0
- package/md_cg/test_review_conformance.py +310 -0
- package/md_cg/test_tasks.py +409 -0
- package/md_cg/test_tool_face.py +189 -0
- package/md_cg/test_twophase.py +286 -0
- package/md_cg/test_writepipe.py +210 -0
- package/md_cg/theory.py +1 -1
- package/md_cg/tokens.py +95 -5
- package/md_cg/tool_face.py +250 -0
- package/md_cg/twophase.py +221 -0
- package/md_cg/units.py +546 -0
- package/md_cg/vision_evidence.py +1 -1
- package/md_cg/weights.py +1 -1
- package/md_cg/whitebox.py +1 -1
- package/md_cg/whitebox_kb/data/verify_cache.json +28 -0
- package/md_cg/whitebox_kb/data/verify_savings.jsonl +46 -0
- package/md_cg/whitebox_kb/wisdom/audit_log/chain_heat.json +10 -10
- 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
- package/md_cg/writelimit.py +19 -3
- package/md_cg/writepipe.py +372 -0
- package/package.json +1 -1
- package/src/hooks.ts +10 -1
- package/src/lib/prompt_safety.ts +62 -0
- package/zcode/AGENTS.md +195 -185
- package/docs//345/212/237/350/203/275/350/260/203/347/224/250/346/230/240/345/260/204/350/241/250_v0.1.md +0 -221
- /package/docs/{AGI → eval/AGI}/344/270/203/347/273/264/350/257/204/345/210/206/346/212/245/345/221/212_md_cg_v1.0.md" +0 -0
- /package/docs/{AGI → eval/AGI}/344/270/203/347/273/264/350/257/204/345/210/206/346/212/245/345/221/212_md_cg_v2.0.md" +0 -0
- /package/docs/{ → eval/}/345/256/236/351/252/214/346/226/271/346/241/210_/345/255/246/344/271/240/351/227/255/347/216/257AB/344/270/216/346/250/252/350/257/204_v1.0.md" +0 -0
- /package/docs/{ → eval/}/346/250/252/350/257/204_/345/205/255/345/256/266100/351/242/230/344/270/255/350/213/261/345/217/214/346/237/245_v1.0.md" +0 -0
- /package/docs/{guardrail-charter.md → mdcg/guardrail-charter.md} +0 -0
- /package/docs/{lingshu_tutorial.html → mdcg/lingshu_tutorial.html} +0 -0
- /package/docs/{memory-assessment.html → mdcg/memory-assessment.html} +0 -0
- /package/docs/{memory_score.html → mdcg/memory_score.html} +0 -0
- /package/docs/{memory_score.png → mdcg/memory_score.png} +0 -0
- /package/docs/{release_v0.3.0.md → mdcg/release_v0.3.0.md} +0 -0
- /package/docs/{release_v0.4.5.md → mdcg/release_v0.4.5.md} +0 -0
- /package/docs/{tool_table_v0.3.0.md → mdcg/tool_table_v0.3.0.md} +0 -0
- /package/docs/{ → mdcg/}/344/273/244/347/211/214/344/270/216/350/247/222/350/211/262/346/235/203/350/201/214/345/210/206/347/246/273_v0.1.md" +0 -0
- /package/docs/{ → mdcg/}/347/201/265/346/236/24282/345/267/245/345/205/267_/345/212/237/350/203/275/346/225/264/347/220/206/344/270/216/350/277/201/347/247/273/346/230/240/345/260/204_v0.1.md" +0 -0
- /package/docs/{ → mdcg/}/347/201/265/346/236/242MCP/345/267/245/345/205/267/346/200/273/350/241/250_v3.4.md" +0 -0
- /package/docs/{ → mdcg/}/347/201/265/346/236/242_/350/207/252/346/210/221/345/261/202/345/256/232/344/271/211.md" +0 -0
- /package/docs/{ → mdcg/}/350/256/244/347/237/245/345/233/276_MD/347/233/256/345/275/225/346/226/271/346/241/210_v0.1.md" +0 -0
- /package/docs/{ → mdcg/}/350/256/244/347/237/245/345/233/276_/346/235/241/344/273/266/347/251/272/351/227/264/345/220/210/346/210/220/344/270/216/347/224/237/346/225/210/346/235/241/344/273/266/345/217/243/345/276/204_v0.1.md" +0 -0
- /package/docs/{ → mdcg/}/350/256/244/347/237/245/345/233/276/344/275/234/344/270/272/350/256/260/345/277/206/346/223/215/344/275/234/347/263/273/347/273/237_/350/257/204/344/274/260/344/270/216/350/267/257/347/272/277_v0.1.md" +0 -0
- /package/docs/{ → plans/}/345/221/275/345/220/215/346/262/273/347/220/206_/351/241/271/347/233/256/350/256/241/345/210/222.md" +0 -0
- /package/docs/{ → plans/}/347/237/245/350/257/206/345/233/276/350/260/261/351/241/271/347/233/256_/344/272/244/346/216/245/346/226/207/346/241/243_20260914.md" +0 -0
- /package/docs/{ → swarm/}/350/234/202/347/276/244/345/244/232/346/231/272/350/203/275/344/275/223_/344/275/277/347/224/250/346/212/245/345/221/212_20260913.md" +0 -0
- /package/docs/{ → swarm/}/350/234/202/347/276/244/345/244/232/346/231/272/350/203/275/344/275/223_/345/212/237/350/203/275/350/257/264/346/230/216_v0.6.md" +0 -0
- /package/docs/{ → swarm/}/350/234/202/347/276/244/347/233/262/345/214/272/346/240/207/350/256/260_v1.0.md" +0 -0
- /package/docs/{ → theory/}/344/277/241/346/201/257/345/267/256/344/270/272/344/273/200/344/271/210/345/277/205/347/204/266/345/255/230/345/234/250/344/270/224/350/207/252/347/204/266/346/211/251/345/244/247.md" +0 -0
- /package/docs/{ → theory/}/346/231/272/350/203/275/347/232/204/345/205/254/347/220/206/345/214/226/345/237/272/347/237/263.md" +0 -0
- /package/docs/{ → theory/}/346/231/272/350/203/275/347/232/204/350/256/244/347/237/245/350/277/207/347/250/213.md" +0 -0
- /package/docs/{ → theory/}/346/231/272/350/203/275/350/256/2723.4.md" +0 -0
- /package/docs/{ → theory/}/347/231/275/347/256/261/346/231/272/350/203/275/346/230/257/344/273/200/344/271/210/357/274/237.md" +0 -0
- /package/docs/{ → theory/}/347/231/275/347/256/261/346/231/272/350/203/275/347/263/273/345/210/227/302/267/347/254/254/344/272/224/347/257/207/357/274/232/350/256/251AI/347/234/237/346/255/243/350/243/205/344/270/212/350/256/260/345/277/206.md" +0 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"spec_version": 1,
|
|
3
|
+
"domain": "duplication",
|
|
4
|
+
"rules": [
|
|
5
|
+
{
|
|
6
|
+
"id": "R-DUP-HASH-GROUP",
|
|
7
|
+
"title": "包内内容指纹重复",
|
|
8
|
+
"severity": "HIGH",
|
|
9
|
+
"matcher": {},
|
|
10
|
+
"mechanical": [{"check": "dup_hash_group", "issue_kind": "dup_content"}],
|
|
11
|
+
"llm": [
|
|
12
|
+
{
|
|
13
|
+
"check": "merge_or_keep",
|
|
14
|
+
"question": "同指纹成员是否应合并为一个节点(保留最新、其余合并计数)?给出可合并子集与理由;若属刻意的多副本(不同 session/分支)则说明保留理由。",
|
|
15
|
+
"expect": "可合并子集 或 保留理由"
|
|
16
|
+
}
|
|
17
|
+
],
|
|
18
|
+
"remedy": "合并(merge;受保护节点须剔除)"
|
|
19
|
+
}
|
|
20
|
+
]
|
|
21
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"spec_version": 1,
|
|
3
|
+
"domain": "field_coverage",
|
|
4
|
+
"rules": [
|
|
5
|
+
{
|
|
6
|
+
"id": "R-ROLE-MISSING",
|
|
7
|
+
"title": "knowledge 节点缺 role 归属",
|
|
8
|
+
"severity": "WARN",
|
|
9
|
+
"matcher": {"layer": ["knowledge"]},
|
|
10
|
+
"mechanical": [{"check": "field_absent", "field": "role", "issue_kind": "missing_field"}],
|
|
11
|
+
"llm": [
|
|
12
|
+
{
|
|
13
|
+
"check": "role_inferable",
|
|
14
|
+
"question": "该节点能否从 writer/tags/正文确定 role(user/agent/tool/system/command 等)?能则给出 role 值,不能则 UNKNOWN。只输出 role 判定,不改写正文。",
|
|
15
|
+
"expect": "role 取值 或 UNKNOWN"
|
|
16
|
+
}
|
|
17
|
+
],
|
|
18
|
+
"remedy": "按 writer/tags 回填 role(backfill,M4 由 designer 终裁)"
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"id": "R-EVIDENCE-ZERO",
|
|
22
|
+
"title": "knowledge 节点无验证证据计数",
|
|
23
|
+
"severity": "WARN",
|
|
24
|
+
"matcher": {"layer": ["knowledge"]},
|
|
25
|
+
"mechanical": [{"check": "evidence_zero", "issue_kind": "missing_field"}],
|
|
26
|
+
"llm": [
|
|
27
|
+
{
|
|
28
|
+
"check": "evidence_sufficient",
|
|
29
|
+
"question": "verification_basis 与正文四要素是否已自足(不依赖 evidence_count 也能复核)?给出 SUFFICIENT 或 NEEDS_EVIDENCE 与理由。",
|
|
30
|
+
"expect": "SUFFICIENT 或 NEEDS_EVIDENCE"
|
|
31
|
+
}
|
|
32
|
+
],
|
|
33
|
+
"remedy": "回填 evidence_count 或降权(backfill / demote)"
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
"id": "R-BASIS-MISSING",
|
|
37
|
+
"title": "节点缺验证基底(无法判定验证档)",
|
|
38
|
+
"severity": "WARN",
|
|
39
|
+
"matcher": {},
|
|
40
|
+
"mechanical": [{"check": "field_absent", "field": "verification_basis", "issue_kind": "missing_field"}],
|
|
41
|
+
"llm": [],
|
|
42
|
+
"remedy": "补 verification_basis(backfill;逃逸口 other 供『分析过但无法归类』)"
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
"id": "R-BASIS-RATIO-PACKAGE",
|
|
46
|
+
"title": "包内基底覆盖率过低(包级对照片段)",
|
|
47
|
+
"severity": "INFO",
|
|
48
|
+
"matcher": {},
|
|
49
|
+
"mechanical": [{"check": "field_ratio_below", "field": "verification_basis", "ratio": 0.9}],
|
|
50
|
+
"llm": [],
|
|
51
|
+
"remedy": "整包回填(backfill,需人工裁决)"
|
|
52
|
+
}
|
|
53
|
+
]
|
|
54
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"spec_version": 1,
|
|
3
|
+
"domain": "source_license",
|
|
4
|
+
"rules": [
|
|
5
|
+
{
|
|
6
|
+
"id": "R-BASIS-LICENSE",
|
|
7
|
+
"title": "赛道与验证基底不相容(来源执照)",
|
|
8
|
+
"severity": "HIGH",
|
|
9
|
+
"matcher": {},
|
|
10
|
+
"mechanical": [{"check": "basis_licensed", "issue_kind": "weak_source"}],
|
|
11
|
+
"llm": [
|
|
12
|
+
{
|
|
13
|
+
"check": "basis_replace",
|
|
14
|
+
"question": "该节点基底与赛道不相容:应替换为相容基底、降级还是移层?给出裁决与理由(允许基底见 evidence 字段)。",
|
|
15
|
+
"expect": "替换基底 或 降权 或 移层"
|
|
16
|
+
}
|
|
17
|
+
],
|
|
18
|
+
"remedy": "替换基底 / 降权 / 移层(backfill / demote / move_layer)"
|
|
19
|
+
}
|
|
20
|
+
]
|
|
21
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"spec_version": 1,
|
|
3
|
+
"domain": "template_flow",
|
|
4
|
+
"rules": [
|
|
5
|
+
{
|
|
6
|
+
"id": "R-TEMPLATE-FLOW",
|
|
7
|
+
"title": "同模板流水(去数字骨架相同)",
|
|
8
|
+
"severity": "INFO",
|
|
9
|
+
"matcher": {},
|
|
10
|
+
"mechanical": [{"check": "template_flow_digits_only", "issue_kind": "template_flow"}],
|
|
11
|
+
"llm": [
|
|
12
|
+
{
|
|
13
|
+
"check": "batch_summary_candidate",
|
|
14
|
+
"question": "这些同模板条目是否应合并为一个『批次汇总』节点(保留可追溯信息与关键差异)?给出结论与理由。",
|
|
15
|
+
"expect": "MERGE_TO_SUMMARY 或 KEEP_AS_IS"
|
|
16
|
+
}
|
|
17
|
+
],
|
|
18
|
+
"remedy": "合并为批次汇总节点(merge,样本一次裁决整组)"
|
|
19
|
+
}
|
|
20
|
+
]
|
|
21
|
+
}
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""记忆评审流水线 · 级 3:D3 规则装配(确定性,零写入)。
|
|
3
|
+
|
|
4
|
+
真源:docs/记忆评审系统_立项设计与施工交接_20260915.md §3 第 3 级 + §5 D3。
|
|
5
|
+
|
|
6
|
+
**规则全落数据文件**(`rules/*.json`)——新增/调整规则**零代码改动**(Ghidra G2 吸收件:
|
|
7
|
+
把「规则」与「引擎」解耦,规则库可增删而引擎冻结)。引擎只提供**机械检查器注册表**:
|
|
8
|
+
|
|
9
|
+
==================== ==========================================================
|
|
10
|
+
检查器 语义(确定性、纯输入、可复算)
|
|
11
|
+
==================== ==========================================================
|
|
12
|
+
field_absent 条目字段为空 → 逐条 issue
|
|
13
|
+
evidence_zero evidence_count==0 → 逐条 issue
|
|
14
|
+
field_ratio_below 包级字段覆盖率低于阈值 → 一条包级 issue
|
|
15
|
+
dup_hash_group 包内同 content_hash → 除首条外逐条 issue
|
|
16
|
+
template_flow_digits_only 包内正文骨架相同(只换数字)→ 除首条外逐条 issue
|
|
17
|
+
basis_licensed 来源执照——赛道与验证基底不相容(复用 crosscheck 判据)
|
|
18
|
+
==================== ==========================================================
|
|
19
|
+
|
|
20
|
+
机械检查在蜂巢 job **之前**本地跑完:LLM 只看机械无法裁决的语义问题(token 经济 + 可复算)。
|
|
21
|
+
规则 matcher 语义:`layer` / `tags_any` 表示「包内存在该类条目」→ 规则入包,
|
|
22
|
+
检查执行时按同一条件**过滤到该类条目**(绝不跨层套用)。
|
|
23
|
+
"""
|
|
24
|
+
from __future__ import annotations
|
|
25
|
+
|
|
26
|
+
import collections
|
|
27
|
+
import json
|
|
28
|
+
import os
|
|
29
|
+
|
|
30
|
+
from .. import crosscheck as CC
|
|
31
|
+
from .. import writelimit as WL
|
|
32
|
+
|
|
33
|
+
RULES_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "rules")
|
|
34
|
+
SEVERITIES = ("INFO", "WARN", "HIGH")
|
|
35
|
+
_BLANK = (None, "", "None", "[]", "{}")
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _blank(v) -> bool:
|
|
39
|
+
if isinstance(v, (list, tuple, dict)):
|
|
40
|
+
return not v
|
|
41
|
+
return v is None or str(v).strip() in ("", "None")
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _as_int(v) -> int:
|
|
45
|
+
try:
|
|
46
|
+
return int(v)
|
|
47
|
+
except (TypeError, ValueError):
|
|
48
|
+
return 0
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _issue(entry, rule, kind, field, evidence, severity=None) -> dict:
|
|
52
|
+
return {"ref": entry.get("ref"), "node_id": entry.get("node_id"),
|
|
53
|
+
"field": field, "issue_kind": kind, "evidence": evidence,
|
|
54
|
+
"rule_id": rule["id"], "severity": severity or rule.get("severity") or "WARN"}
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
# ---------------------------- 机械检查器注册表 ----------------------------
|
|
58
|
+
|
|
59
|
+
def _chk_field_absent(entries, spec, rule):
|
|
60
|
+
field = spec.get("field")
|
|
61
|
+
return [_issue(e, rule, spec.get("issue_kind") or "missing_field", field,
|
|
62
|
+
"字段 %s 为空" % field) for e in entries if _blank(e.get(field))]
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def _chk_evidence_zero(entries, spec, rule):
|
|
66
|
+
return [_issue(e, rule, spec.get("issue_kind") or "missing_field", "evidence_count",
|
|
67
|
+
"evidence_count=0(无验证证据计数)")
|
|
68
|
+
for e in entries if _as_int(e.get("evidence_count")) == 0]
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def _chk_field_ratio_below(entries, spec, rule):
|
|
72
|
+
field, thr = spec.get("field"), float(spec.get("ratio", 1.0))
|
|
73
|
+
ratio = sum(1 for e in entries if not _blank(e.get(field))) / max(len(entries), 1)
|
|
74
|
+
if ratio >= thr:
|
|
75
|
+
return []
|
|
76
|
+
return [{"ref": None, "node_id": None, "field": field, "issue_kind": "package_ratio",
|
|
77
|
+
"evidence": "包内 %s 覆盖率 %.3f < %.3f(%d 条)" % (field, ratio, thr, len(entries)),
|
|
78
|
+
"rule_id": rule["id"], "severity": rule.get("severity") or "INFO"}]
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def _chk_dup_hash_group(entries, spec, rule):
|
|
82
|
+
seen, out = {}, []
|
|
83
|
+
for e in entries:
|
|
84
|
+
h = str(e.get("content_hash") or "")
|
|
85
|
+
if _blank(h) or h == "None":
|
|
86
|
+
continue
|
|
87
|
+
if h in seen:
|
|
88
|
+
out.append(_issue(e, rule, "dup_content", "content_hash",
|
|
89
|
+
"同包内与 %s 同指纹 %s" % (seen[h], h)))
|
|
90
|
+
else:
|
|
91
|
+
seen[h] = e.get("ref")
|
|
92
|
+
return out
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def _chk_template_flow(entries, spec, rule):
|
|
96
|
+
seen, out = {}, []
|
|
97
|
+
for e in entries:
|
|
98
|
+
sk = WL._skeleton(e.get("excerpt") or "")
|
|
99
|
+
if not sk or len(sk) < WL.MIN_SKELETON:
|
|
100
|
+
continue
|
|
101
|
+
if sk in seen:
|
|
102
|
+
out.append(_issue(e, rule, "template_flow", "content",
|
|
103
|
+
"同模板流水(去数字骨架与 %s 相同):%s" % (seen[sk], sk[:40])))
|
|
104
|
+
else:
|
|
105
|
+
seen[sk] = e.get("ref")
|
|
106
|
+
return out
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def _chk_basis_licensed(entries, spec, rule):
|
|
110
|
+
out = []
|
|
111
|
+
for e in entries:
|
|
112
|
+
basis = e.get("verification_basis")
|
|
113
|
+
if _blank(basis):
|
|
114
|
+
continue
|
|
115
|
+
fm = {"layer": e.get("layer"), "tags": e.get("tags") or []}
|
|
116
|
+
track = CC.classify_track(fm, e.get("excerpt") or "")
|
|
117
|
+
if not CC.basis_licensed(track, basis):
|
|
118
|
+
out.append(_issue(e, rule, "weak_source", "verification_basis",
|
|
119
|
+
"赛道 %s(政策 %s)不允许基底 %s;允许 %s"
|
|
120
|
+
% (track, CC.source_policy(track), basis,
|
|
121
|
+
"/".join(CC.allowed_basis(track)))))
|
|
122
|
+
return out
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
MECH_CHECKS = {
|
|
126
|
+
"field_absent": _chk_field_absent,
|
|
127
|
+
"evidence_zero": _chk_evidence_zero,
|
|
128
|
+
"field_ratio_below": _chk_field_ratio_below,
|
|
129
|
+
"dup_hash_group": _chk_dup_hash_group,
|
|
130
|
+
"template_flow_digits_only": _chk_template_flow,
|
|
131
|
+
"basis_licensed": _chk_basis_licensed,
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
# ---------------------------- 规则库加载 ----------------------------
|
|
136
|
+
|
|
137
|
+
def load_rules(rules_dir=None) -> dict:
|
|
138
|
+
"""读 `rules/*.json`。格式非法/字段缺失/id 重复 → 抛 ValueError(不静默降级)。"""
|
|
139
|
+
d = rules_dir or RULES_DIR
|
|
140
|
+
files = sorted(f for f in os.listdir(d) if f.endswith(".json")) if os.path.isdir(d) else []
|
|
141
|
+
rules, ids = [], {}
|
|
142
|
+
for fn in files:
|
|
143
|
+
with open(os.path.join(d, fn), encoding="utf-8") as f:
|
|
144
|
+
doc = json.load(f)
|
|
145
|
+
for r in doc.get("rules") or []:
|
|
146
|
+
miss = [k for k in ("id", "matcher", "severity") if k not in r]
|
|
147
|
+
if miss:
|
|
148
|
+
raise ValueError("规则库 %s:规则缺字段 %s(%s)" % (fn, miss, r.get("id")))
|
|
149
|
+
if r["severity"] not in SEVERITIES:
|
|
150
|
+
raise ValueError("规则库 %s:severity=%s 非法(%s)" % (fn, r["severity"], r["id"]))
|
|
151
|
+
if r["id"] in ids:
|
|
152
|
+
raise ValueError("规则 id 重复:%s(%s / %s)" % (r["id"], ids[r["id"]], fn))
|
|
153
|
+
ids[r["id"]] = fn
|
|
154
|
+
for spec in list(r.get("mechanical") or []):
|
|
155
|
+
if spec.get("check") not in MECH_CHECKS:
|
|
156
|
+
raise ValueError("规则库 %s:未知机械检查 %s(%s)"
|
|
157
|
+
% (fn, spec.get("check"), r["id"]))
|
|
158
|
+
rules.append(r)
|
|
159
|
+
return {"rules_dir": d, "files": files, "rules": rules, "count": len(rules)}
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def _match(rule, ctx) -> tuple:
|
|
163
|
+
"""matcher 语义:包内**存在**该类条目即入包(执行时按同条件过滤)。"""
|
|
164
|
+
m = rule.get("matcher") or {}
|
|
165
|
+
if m.get("layer") and not (ctx["layers"] & set(m["layer"])):
|
|
166
|
+
return False, "包内无 %s 层条目" % "/".join(m["layer"])
|
|
167
|
+
if m.get("tags_any") and not (ctx["tag_prefixes"] & set(m["tags_any"])):
|
|
168
|
+
return False, "包内无 %s 标签" % "/".join(m["tags_any"])
|
|
169
|
+
return True, "matcher 命中"
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def _scope(entries, rule) -> list:
|
|
173
|
+
m = rule.get("matcher") or {}
|
|
174
|
+
if m.get("layer"):
|
|
175
|
+
want = set(m["layer"])
|
|
176
|
+
return [e for e in entries if str(e.get("layer")) in want]
|
|
177
|
+
return list(entries)
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def _as_rules(rules, rules_dir=None) -> list:
|
|
181
|
+
"""规则入参防呆:None→读规则库;{"rules":[...]}→取内层;列表→原样。
|
|
182
|
+
|
|
183
|
+
仿 cg/stg「op 必填」防呆:传错形态时给出明确入口,而非静默 AttributeError。
|
|
184
|
+
"""
|
|
185
|
+
if rules is None:
|
|
186
|
+
return load_rules(rules_dir)["rules"]
|
|
187
|
+
if isinstance(rules, dict):
|
|
188
|
+
if "rules" not in rules:
|
|
189
|
+
raise ValueError("规则入参 dict 缺少 rules 键(应为规则列表或 load_rules 结果)")
|
|
190
|
+
return list(rules.get("rules") or [])
|
|
191
|
+
return list(rules)
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def assemble(pkg: dict, *, rules=None, rules_dir=None) -> dict:
|
|
195
|
+
"""单个待评包 → 规则集 + 机械检查结果 + 待 LLM 检查清单。"""
|
|
196
|
+
rl = _as_rules(rules, rules_dir)
|
|
197
|
+
entries = pkg.get("entries") or []
|
|
198
|
+
ctx = {"layers": {str(e.get("layer") or "") for e in entries},
|
|
199
|
+
"tag_prefixes": {str(t).split(":", 1)[0] for e in entries
|
|
200
|
+
for t in (e.get("tags") or [])}}
|
|
201
|
+
matched, mech, llm = [], [], []
|
|
202
|
+
for rule in rl:
|
|
203
|
+
ok, why = _match(rule, ctx)
|
|
204
|
+
if not ok:
|
|
205
|
+
continue
|
|
206
|
+
scoped = _scope(entries, rule)
|
|
207
|
+
matched.append({"id": rule["id"], "title": rule.get("title"),
|
|
208
|
+
"severity": rule.get("severity"), "why": why,
|
|
209
|
+
"scope": len(scoped), "remedy": rule.get("remedy")})
|
|
210
|
+
for spec in rule.get("mechanical") or []:
|
|
211
|
+
mech += MECH_CHECKS[spec["check"]](scoped, spec, rule)
|
|
212
|
+
for spec in rule.get("llm") or []:
|
|
213
|
+
llm.append({"rule_id": rule["id"], "check": spec.get("check"),
|
|
214
|
+
"question": spec.get("question"),
|
|
215
|
+
"expect": spec.get("expect"),
|
|
216
|
+
"scope_refs": [e.get("ref") for e in scoped][:200]})
|
|
217
|
+
cnt = collections.Counter(i["issue_kind"] for i in mech)
|
|
218
|
+
return {"bundle_id": pkg.get("bundle_id"), "group_kind": pkg.get("group_kind"),
|
|
219
|
+
"group_key": pkg.get("group_key"), "size": len(entries),
|
|
220
|
+
"matched": matched, "mechanical": mech, "llm": llm,
|
|
221
|
+
"mechanical_by_kind": dict(cnt),
|
|
222
|
+
"mechanical_flagged": len({i.get("ref") for i in mech if i.get("ref")})}
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
def assemble_all(result: dict, *, rules=None, rules_dir=None) -> dict:
|
|
226
|
+
"""全部包装配 + 汇总(包级统计供 M2 spec 与 M4 量化复跑)。"""
|
|
227
|
+
rl = _as_rules(rules, rules_dir)
|
|
228
|
+
out = [assemble(p, rules=rl) for p in result.get("bundles") or []]
|
|
229
|
+
kinds = collections.Counter()
|
|
230
|
+
for a in out:
|
|
231
|
+
kinds.update(a["mechanical_by_kind"])
|
|
232
|
+
return {"packages": out, "stats": {
|
|
233
|
+
"bundles": len(out),
|
|
234
|
+
"mechanical_total": sum(len(a["mechanical"]) for a in out),
|
|
235
|
+
"mechanical_by_kind": dict(kinds),
|
|
236
|
+
"llm_checks": sum(len(a["llm"]) for a in out),
|
|
237
|
+
"rules": [r["id"] for r in rl]},
|
|
238
|
+
"bundles_input": result.get("stats")}
|
package/md_cg/nodefile.py
CHANGED
|
@@ -37,6 +37,7 @@ CCG 要素的语义对应(白箱第 1 篇第 17 章):
|
|
|
37
37
|
拿 `observation_position` 单槽加前缀冒充,等于把坐标的一维当成整条生效条件。
|
|
38
38
|
故 CCG_REQUIRED == CCG_MARKS:6 行缺一不可,缺则补写、或按 q-3 判 BLINDSPOT。
|
|
39
39
|
"""
|
|
40
|
+
import hashlib
|
|
40
41
|
import json
|
|
41
42
|
import time
|
|
42
43
|
|
|
@@ -48,6 +49,27 @@ CCG_MARKS = ("功能名", "生效条件", "子功能", "执行", "验证方式",
|
|
|
48
49
|
# 见 `condition_space_text`)——生效条件必须由 condition_space 四槽合成显式声明,
|
|
49
50
|
# 不存在「常用条件默认省略」的合法情形。缺它即缺证据:补写,或判 BLINDSPOT。
|
|
50
51
|
CCG_REQUIRED = CCG_MARKS
|
|
52
|
+
|
|
53
|
+
# ---- 裁定 B(使用者 2026-09-16):六要素的契约角色(术语真源) -----------------
|
|
54
|
+
# 六要素不是注释/描述,而是**接口契约**——每行在契约里有一个确定角色:
|
|
55
|
+
# 功能名 = 签名 signature(可执行入口符号)
|
|
56
|
+
# 生效条件 = 前置条件 precondition(由 condition_space 四槽合成,唯一入口)
|
|
57
|
+
# 子功能 = 依赖 dependency
|
|
58
|
+
# 执行 = 调用 invocation
|
|
59
|
+
# 验证方式 = 后置条件 postcondition + test
|
|
60
|
+
# 不适用条件 = 拒绝域 rejection_domain
|
|
61
|
+
# 「生效条件」的契约角色正是 **precondition**——这与其「不可隐含、必须四槽合成」
|
|
62
|
+
# 的既有硬约束同源:缺前置条件的接口无法判定可否调用,故缺参即编译错误。
|
|
63
|
+
# 本常量是术语的**唯一真源**;其余模块(如 ccgc.CONTRACT_ROLES)一律引用本处,
|
|
64
|
+
# 禁止各自再定义一份,防「术语双写法」漂移。
|
|
65
|
+
CCG_CONTRACT_ROLES = {
|
|
66
|
+
"功能名": "签名 signature(可执行入口符号)",
|
|
67
|
+
"生效条件": "前置条件 precondition",
|
|
68
|
+
"子功能": "依赖 dependency",
|
|
69
|
+
"执行": "调用 invocation",
|
|
70
|
+
"验证方式": "后置条件 postcondition + test",
|
|
71
|
+
"不适用条件": "拒绝域 rejection_domain",
|
|
72
|
+
}
|
|
51
73
|
# 外部验证基底的可取值(frontmatter.verification_basis)
|
|
52
74
|
#
|
|
53
75
|
# 分两档(口径:文科宽松、理科严格):
|
|
@@ -64,6 +86,22 @@ REPRODUCIBLE_BASIS = ("compiler", "test", "measurement", "formal_proof", "data")
|
|
|
64
86
|
CONSISTENCY_BASIS = ("textbook", "public_kb")
|
|
65
87
|
|
|
66
88
|
|
|
89
|
+
def content_hash(content: str) -> str:
|
|
90
|
+
"""节点正文的**内容指纹**(sha256 前 12 位)——全仓唯一实现。
|
|
91
|
+
|
|
92
|
+
两处共用且必须逐位一致,否则对账/变更探测会永远"匹配成功"而失去意义
|
|
93
|
+
(与 `codeindex.region_hash` 同一纪律:两侧各写一份哈希算法,漂移就悄悄失效):
|
|
94
|
+
① 索引快照 `_index.json` 的 `content_hash`(变更探测);
|
|
95
|
+
② 写入两段式(`twophase`)的 intent 载荷指纹——崩溃后靠它回答
|
|
96
|
+
「那笔写入到底落盘了没有」。
|
|
97
|
+
|
|
98
|
+
口径:只哈希**正文**,不含 frontmatter——frontmatter 会被写路径正常改写
|
|
99
|
+
(importance / protected / 生命周期状态 / 时间戳),把它算进去则「内容没变、
|
|
100
|
+
元数据变了」也会判成不匹配,对账会大面积误报 interrupted。
|
|
101
|
+
"""
|
|
102
|
+
return hashlib.sha256((content or "").encode("utf-8")).hexdigest()[:12]
|
|
103
|
+
|
|
104
|
+
|
|
67
105
|
def dumps(frontmatter: dict, content: str) -> str:
|
|
68
106
|
lines = [_DELIM]
|
|
69
107
|
for k in sorted(frontmatter):
|
package/md_cg/predict.py
CHANGED
|
@@ -428,10 +428,18 @@ def predictability(blindspot):
|
|
|
428
428
|
def anchor_from_description(cg, description):
|
|
429
429
|
"""盲区描述 → 锚点节点(检索器打分,与 AEIS 的 LIKE→坐标回退同构)。
|
|
430
430
|
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
431
|
+
择锚规则:候选池内**可起推者优先**——出边非空是路线生成的必要条件,无出边的
|
|
432
|
+
节点只能靠语义回退硬凑路线,起点语义即失真。**全部不可起推时才降级**为首个合格
|
|
433
|
+
候选(诚实降级:宁可标注锚点弱,不假装有锚)。
|
|
434
|
+
动机(实测):同分并列时检索序不可依赖——「正题节点」与其「同名+近义描述」变体
|
|
435
|
+
词法分可完全相同(各自都为 1.0),纯按检索名次取首位会漂到无出边的近义变体上。
|
|
436
|
+
|
|
437
|
+
过滤与择锚分两关:先按标签/层过滤掉推断脚手架(`gap_hint`/`scene`)与负记忆
|
|
438
|
+
(`unresolved`/`rejected`),再在剩余候选里按**可起推资格**择优——出边非空者
|
|
439
|
+
立即采纳,无出边者记为降级候选继续后看,全池无可起推者才降级取首个合格候选。
|
|
440
|
+
候选全被过滤时返回 None(等价「无锚点」),由调用方按 no_anchor 处理。
|
|
441
|
+
邻接表不可用(chain 取表异常)时退回「取首个合格候选」——辅助判据故障不应
|
|
442
|
+
让整条预测链失效(兜底优先于择优)。
|
|
435
443
|
"""
|
|
436
444
|
q = str(description or "").strip()
|
|
437
445
|
if not q:
|
|
@@ -440,6 +448,12 @@ def anchor_from_description(cg, description):
|
|
|
440
448
|
results, _meta = cg.search(q, k=ANCHOR_FETCH_K, record=False, judge=False)
|
|
441
449
|
except Exception:
|
|
442
450
|
return None
|
|
451
|
+
try:
|
|
452
|
+
from . import chain
|
|
453
|
+
out_edges = chain.adjacency(cg)
|
|
454
|
+
except Exception:
|
|
455
|
+
out_edges = None
|
|
456
|
+
fallback = None
|
|
443
457
|
for item in results:
|
|
444
458
|
nd = item[0] if isinstance(item, (tuple, list)) else item
|
|
445
459
|
if not isinstance(nd, dict):
|
|
@@ -459,8 +473,12 @@ def anchor_from_description(cg, description):
|
|
|
459
473
|
# 兜底:fs 派生的 id 可能是相对路径(如 unresolved/bs_x.md),归一为裸节点名
|
|
460
474
|
if "/" in str(nid) and str(nid).endswith(".md"):
|
|
461
475
|
nid = os.path.basename(str(nid))[:-3]
|
|
462
|
-
|
|
463
|
-
|
|
476
|
+
# 可起推资格:出边非空是路线生成的必要条件;不可起推者只作降级候选。
|
|
477
|
+
if out_edges is None or out_edges.get(nid):
|
|
478
|
+
return nid
|
|
479
|
+
if fallback is None:
|
|
480
|
+
fallback = nid
|
|
481
|
+
return fallback
|
|
464
482
|
|
|
465
483
|
|
|
466
484
|
# ---------------------------------------------------------------- 路线生成
|
package/md_cg/protect.py
CHANGED
|
@@ -3,14 +3,14 @@
|
|
|
3
3
|
|
|
4
4
|
理论出处(全部来自本仓已有文档,非外部发明):
|
|
5
5
|
|
|
6
|
-
· `docs/灵枢_自我层定义.md:6`
|
|
6
|
+
· `docs/mdcg/灵枢_自我层定义.md:6`
|
|
7
7
|
SELF 层 = 身份 + 价值观 + 图接口,**不可遗忘**、跨会话自动加载。
|
|
8
8
|
任何实例都能改写自我层 = 自我认知可被任意覆写,与「自我」定义冲突。
|
|
9
|
-
· `docs/认知图_MD目录方案_v0.1.md:53`
|
|
9
|
+
· `docs/mdcg/认知图_MD目录方案_v0.1.md:53`
|
|
10
10
|
`anchor/ # 锚点层(不可遗忘,保护)`
|
|
11
|
-
· `docs/认知图_MD目录方案_v0.1.md:412-416`
|
|
11
|
+
· `docs/mdcg/认知图_MD目录方案_v0.1.md:412-416`
|
|
12
12
|
「importance 提升(保护:不可遗忘记入 anchor/ 且**受保护标记**)」
|
|
13
|
-
· `docs/tool_table_v0.3.0.md:15`
|
|
13
|
+
· `docs/mdcg/tool_table_v0.3.0.md:15`
|
|
14
14
|
「importance_hint 可显式提示重要性(**≥0.7 触发不可遗忘保护**)」
|
|
15
15
|
|
|
16
16
|
三层保护(任一命中即受保护):
|
package/md_cg/refindex.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
2
|
"""md_cg · 统一 ref 协议 + 索引水位(增量)+ 漂移/悬空巡检
|
|
3
3
|
|
|
4
|
-
对照 `docs/认知图_索引与工程规范化_计划_v0.1.md` 的 R3(修 D + 修 F):
|
|
4
|
+
对照 `docs/mdcg/认知图_索引与工程规范化_计划_v0.1.md` 的 R3(修 D + 修 F):
|
|
5
5
|
|
|
6
6
|
**D 漂移 / 悬空检测(本模块 `check_refs`)**
|
|
7
7
|
扫描带 `code_ref` / `doc_ref` 的节点,回两类问题:
|
|
@@ -598,6 +598,25 @@ def _forget_many(cg, plan, why: str) -> tuple:
|
|
|
598
598
|
return done, blocked
|
|
599
599
|
|
|
600
600
|
|
|
601
|
+
def _drop_ghosts(cg, ghosts) -> list:
|
|
602
|
+
"""摘除幽灵条目的索引记录(节点文件已不存在,没有可软删的实体)。
|
|
603
|
+
|
|
604
|
+
走 `MdCG._unstage`:它顺带落删除记录,保证幽灵不会再次从分片日志里复活。
|
|
605
|
+
基础层没有该能力时如实返回空列表(不假装成功)。
|
|
606
|
+
"""
|
|
607
|
+
drop = getattr(cg, "_unstage", None)
|
|
608
|
+
if not callable(drop):
|
|
609
|
+
return []
|
|
610
|
+
out = []
|
|
611
|
+
for nid in ghosts:
|
|
612
|
+
try:
|
|
613
|
+
drop(nid)
|
|
614
|
+
out.append(nid)
|
|
615
|
+
except Exception: # 单条失败不拖垮整批
|
|
616
|
+
continue
|
|
617
|
+
return out
|
|
618
|
+
|
|
619
|
+
|
|
601
620
|
def prune_orphans(cg, *, kind: str, root: str, items, dry_run: bool = False,
|
|
602
621
|
reason: str = "") -> dict:
|
|
603
622
|
"""清退「同 root + 同 path,但已不在本次产出里」的**过期代**节点。
|
|
@@ -663,8 +682,23 @@ def prune_dangling(cg, *, only_roots=None, dry_run: bool = False,
|
|
|
663
682
|
`check_refs` 只报告不处置(其原话是「悬空需人工处置」),本函数就是那个
|
|
664
683
|
出口——已删脚本、被搬走的文档留下的残留节点一次清掉,而不是逐条手工
|
|
665
684
|
`forget`。判定与巡检共用 `probe_ref` 的唯一实现,口径不会打架。
|
|
685
|
+
|
|
686
|
+
另清**幽灵条目**(ghosts):索引有条目、节点文件却不存在。它们是历史
|
|
687
|
+
「删除只摘内存索引、不落盘」的遗留——`cg.get` 取不回 → 悬空清退够不着它,
|
|
688
|
+
而 `check_refs` 走 ledger 会一直报 → dangling 永不归零。判据只用唯一真源
|
|
689
|
+
(节点 .md 不存在即脏索引),与「索引是派生物」的宣言一致;`only_roots`
|
|
690
|
+
非空时跳过(幽灵条目无 ref,无法归因到某个源大域)。
|
|
666
691
|
"""
|
|
667
692
|
nodes = (getattr(cg, "index", {}) or {}).get("nodes") or {}
|
|
693
|
+
ghosts = []
|
|
694
|
+
if not only_roots:
|
|
695
|
+
for nid, e in list(nodes.items()):
|
|
696
|
+
tags = (e or {}).get("tags") or []
|
|
697
|
+
if not any(t in ("code", "doc") for t in tags):
|
|
698
|
+
continue
|
|
699
|
+
path = (e or {}).get("path") or ""
|
|
700
|
+
if path and not os.path.exists(os.path.join(cg.root, path)):
|
|
701
|
+
ghosts.append(nid)
|
|
668
702
|
# 同 prune_orphans:ref 只在节点 frontmatter 里,索引条目里没有,
|
|
669
703
|
# 必须 cg.get 取回节点再 ref_of(否则恒空、静默不删)。
|
|
670
704
|
todo = []
|
|
@@ -684,7 +718,7 @@ def prune_dangling(cg, *, only_roots=None, dry_run: bool = False,
|
|
|
684
718
|
if only_roots and not any(_same_root(ref.get("root"), r) for r in only_roots):
|
|
685
719
|
continue
|
|
686
720
|
todo.append((nid, ref))
|
|
687
|
-
truncated = len(todo) > max_nodes
|
|
721
|
+
truncated = len(todo) > max_nodes or len(ghosts) > max_nodes
|
|
688
722
|
plan = []
|
|
689
723
|
for nid, ref in todo[:max_nodes]:
|
|
690
724
|
try:
|
|
@@ -695,14 +729,19 @@ def prune_dangling(cg, *, only_roots=None, dry_run: bool = False,
|
|
|
695
729
|
|
|
696
730
|
why = reason or ("源文件已删除,索引节点悬空(回读必然失败),"
|
|
697
731
|
"清退以消除永不消失的 dangling")
|
|
732
|
+
ghost_plan = sorted(ghosts)[:max_nodes]
|
|
698
733
|
base = {"scanned": len(nodes), "candidates": len(plan),
|
|
734
|
+
"ghosts": len(ghost_plan),
|
|
699
735
|
"dry_run": bool(dry_run), "truncated": truncated,
|
|
700
736
|
"max_nodes": max_nodes}
|
|
701
737
|
if dry_run:
|
|
702
738
|
return {**base, "ok": True, "count": len(plan), "pruned": sorted(plan)[:50],
|
|
739
|
+
"ghost_pruned": ghost_plan[:50],
|
|
703
740
|
"skipped_protected": [], "reason": why}
|
|
704
741
|
done, blocked = _forget_many(cg, plan, why)
|
|
742
|
+
dropped = _drop_ghosts(cg, ghost_plan)
|
|
705
743
|
return {**base, "ok": True, "count": len(done), "pruned": done[:50],
|
|
744
|
+
"ghost_pruned": dropped[:50],
|
|
706
745
|
"skipped_protected": blocked[:20], "reason": why}
|
|
707
746
|
|
|
708
747
|
|
package/md_cg/scrub.py
CHANGED
|
@@ -622,7 +622,11 @@ def decontaminate(cg, node_ids=None, *, kinds=None, dry_run: bool = True,
|
|
|
622
622
|
fm = _fm_of(cg, nid, None)
|
|
623
623
|
if str(fm.get("layer") or "") != "contextual":
|
|
624
624
|
cg._move_layer(nid, "contextual", reason=f"scrub:{kind}")
|
|
625
|
-
|
|
625
|
+
# 层降级 = 生命周期降级(②):状态同批推进(负路由,失败不抛)
|
|
626
|
+
_st = cg.set_state(nid, "demoted", reason=f"scrub:{kind}",
|
|
627
|
+
actor=actor or "scrub")
|
|
628
|
+
done.append("demote" if _st.get("ok") else
|
|
629
|
+
f"demote_state:{_st.get('error')}")
|
|
626
630
|
except Exception as exc: # noqa: BLE001
|
|
627
631
|
done.append(f"demote_failed:{type(exc).__name__}")
|
|
628
632
|
n_applied += 1
|
package/md_cg/self_state.py
CHANGED