@furongjun1999/dsh-memory 0.4.11 → 0.5.1
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 +552 -465
- package/codebuddy/CODEBUDDY.md +11 -3
- package/codebuddy/README.md +92 -90
- package/codebuddy/mcp.json +27 -27
- 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 -169
- 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 -144
- package/docs/README.md +143 -111
- package/docs/discipline/harnesses.yaml +244 -226
- package/docs/discipline/templates/full.md.tmpl +61 -61
- package/docs/discipline/templates/rules.mdc.tmpl +68 -0
- package/docs/discipline/templates/skill.md.tmpl +23 -23
- package/docs/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 +299 -299
- package/docs/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 +239 -239
- package/docs/eval/bench_lingshu_self/bench_self.py +140 -0
- package/docs/eval/bench_lingshu_self/self_bench_result.json +404 -0
- package/docs/eval/bench_lingshu_self//347/201/265/346/236/242/350/207/252/345/272/223/347/253/257/345/210/260/347/253/257/346/243/200/347/264/242/345/256/236/346/265/213_v1.0.md +38 -0
- package/docs/eval//344/270/215/345/217/257/351/235/240/346/200/247/350/220/275/345/234/260_P0_v1.0.md +185 -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 +174 -174
- package/docs/eval//346/225/205/351/232/234/346/263/250/345/205/245/345/256/236/346/265/213_v1.0.md +422 -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 +223 -223
- package/docs/eval//347/253/257/345/210/260/347/253/257LoCoMoQA/345/220/214/345/217/243/345/276/204/345/257/271/347/205/247_v1.0.md +100 -0
- package/docs/eval//347/253/257/345/210/260/347/253/257/345/271/262/346/211/260/346/261/240/350/257/204/346/265/213_/347/241/256/345/256/232/346/200/247/350/243/201/345/206/263vsLLM_judge_v1.1.md +197 -0
- package/docs/eval//347/274/272/351/231/267/346/214/226/346/216/230_/350/207/252/344/270/273/350/277/255/344/273/243_v1.md +156 -0
- package/docs/eval//347/274/272/351/231/267/346/214/226/346/216/230_/350/207/252/344/270/273/350/277/255/344/273/243_v10.md +210 -0
- package/docs/eval//347/274/272/351/231/267/346/214/226/346/216/230_/350/207/252/344/270/273/350/277/255/344/273/243_v11.md +227 -0
- package/docs/eval//347/274/272/351/231/267/346/214/226/346/216/230_/350/207/252/344/270/273/350/277/255/344/273/243_v12.md +203 -0
- package/docs/eval//347/274/272/351/231/267/346/214/226/346/216/230_/350/207/252/344/270/273/350/277/255/344/273/243_v13.md +233 -0
- package/docs/eval//347/274/272/351/231/267/346/214/226/346/216/230_/350/207/252/344/270/273/350/277/255/344/273/243_v14.md +191 -0
- package/docs/eval//347/274/272/351/231/267/346/214/226/346/216/230_/350/207/252/344/270/273/350/277/255/344/273/243_v15.md +213 -0
- package/docs/eval//347/274/272/351/231/267/346/214/226/346/216/230_/350/207/252/344/270/273/350/277/255/344/273/243_v16.md +214 -0
- package/docs/eval//347/274/272/351/231/267/346/214/226/346/216/230_/350/207/252/344/270/273/350/277/255/344/273/243_v17.md +199 -0
- package/docs/eval//347/274/272/351/231/267/346/214/226/346/216/230_/350/207/252/344/270/273/350/277/255/344/273/243_v2.md +156 -0
- package/docs/eval//347/274/272/351/231/267/346/214/226/346/216/230_/350/207/252/344/270/273/350/277/255/344/273/243_v3.md +152 -0
- package/docs/eval//347/274/272/351/231/267/346/214/226/346/216/230_/350/207/252/344/270/273/350/277/255/344/273/243_v4.md +128 -0
- package/docs/eval//347/274/272/351/231/267/346/214/226/346/216/230_/350/207/252/344/270/273/350/277/255/344/273/243_v5.md +114 -0
- package/docs/eval//347/274/272/351/231/267/346/214/226/346/216/230_/350/207/252/344/270/273/350/277/255/344/273/243_v6.md +192 -0
- package/docs/eval//347/274/272/351/231/267/346/214/226/346/216/230_/350/207/252/344/270/273/350/277/255/344/273/243_v7.md +187 -0
- package/docs/eval//347/274/272/351/231/267/346/214/226/346/216/230_/350/207/252/344/270/273/350/277/255/344/273/243_v8.md +207 -0
- package/docs/eval//347/274/272/351/231/267/346/214/226/346/216/230_/350/207/252/344/270/273/350/277/255/344/273/243_v9.md +203 -0
- package/docs/{mdcg → hive}//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 +165 -160
- package/docs/hive//344/273/244/347/211/214/350/257/255/344/271/211/344/277/256/346/255/243_/344/273/262/350/243/201/344/275/215_v0.1.md +76 -0
- package/docs/{mdcg → hive}//345/255/220/344/273/243/347/220/206/351/205/215/347/275/256/346/240/207/345/207/206_v0.5.md +236 -236
- package/docs/hive//345/256/211/345/205/250/345/256/241/350/256/241/345/256/236/351/224/232_v0.1.md +202 -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 -43
- package/docs/hive//346/243/200/347/264/242/347/256/227/346/263/225/345/217/243/345/276/204/345/257/271/347/205/247_v0.1.md +85 -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 -102
- package/docs/hive//350/234/202/345/267/242M6_ingest/345/256/236/346/226/275/350/256/241/345/210/222_v0.1.md +47 -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 +519 -503
- 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 +85 -85
- package/docs/hive//350/234/202/345/267/242/350/256/276/350/256/241_/347/220/206/350/256/272/345/257/271/351/275/220_v0.1.md +191 -0
- package/docs/hive//350/234/202/345/267/242/350/277/255/344/273/243_/345/256/217/350/247/202/344/270/216/347/276/244/344/275/223/350/260/203/345/272/246_v0.1.md +90 -0
- package/docs/images/lingshu-moonlight-covenant-poster-preview.jpg +0 -0
- package/docs/images/lingshu-moonlight-covenant-poster.png +0 -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 -216
- package/docs/mdcg/README/350/257/246/347/273/206/347/211/210_v0.4.10.md +646 -646
- package/docs/mdcg/lingshu_tutorial.html +14449 -14449
- package/docs/mdcg/release_v0.4.11.md +49 -0
- package/docs/mdcg/release_v0.4.5.md +55 -55
- package/docs/mdcg/tool_table_v0.3.0.md +117 -117
- 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 -600
- 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 +40 -40
- 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 +275 -275
- package/docs/mdcg//345/217/221/345/270/203/351/227/250/347/246/201/351/223/276_v0.1.md +54 -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 +130 -130
- 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 +278 -278
- 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 -172
- package/docs/mdcg//347/274/272/345/217/243/345/215/225_P0/346/224/266/345/217/243_v0.1.md +270 -270
- 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 +491 -491
- 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 +340 -340
- 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 +588 -588
- 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 -176
- 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 +81 -81
- package/docs/swarm//350/234/202/347/276/244/344/272/222/350/201/224_v0.1.md +704 -704
- 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 -242
- package/docs/theory//344/270/215/345/217/257/351/235/240/345/256/232/347/220/206/344/270/216/345/244/261/346/225/210/344/274/230/345/205/210/346/241/206/346/236/266_v0.3.md +365 -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 -129
- 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 -134
- package/docs/theory//346/231/272/350/203/275/350/256/2723.4.md +5260 -5260
- 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 -145
- 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 +144 -144
- 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 +198 -198
- 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 -221
- 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 -237
- package/docs//344/270/215/345/217/257/351/235/240/346/200/247/347/220/206/350/256/272_v0.1.md +343 -0
- package/docs//345/217/221/345/270/203/344/273/266/345/233/236/346/272/257/350/257/264/346/230/216_v0.1.md +82 -82
- 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 +28 -2
- package/dsh/README.md +82 -82
- package/dsh/cordis.yml.example +139 -139
- package/dsh/update-lingshu.bat +11 -11
- package/lib/bridge.d.ts +9 -0
- package/lib/bridge.js +35 -0
- package/lib/hooks.js +36 -2
- package/lib/index.js +7 -1
- package/lib/lib/roleplay_web.js +116 -29
- package/lib/lib/token_store.d.ts +7 -1
- package/lib/lib/token_store.js +12 -3
- package/md_cg/__init__.py +7 -7
- package/md_cg/audit.py +379 -368
- package/md_cg/autonomy.py +287 -287
- package/md_cg/backfill.py +1328 -1327
- package/md_cg/backfill_bigdomain.py +34 -34
- package/md_cg/backfill_bucket_zh.py +35 -0
- package/md_cg/bench6_arms.py +410 -410
- package/md_cg/bench6_common.py +230 -230
- package/md_cg/bench6_competitors.py +212 -212
- package/md_cg/bench_axis_domain.py +257 -257
- package/md_cg/bench_blind_comp.py +308 -308
- package/md_cg/bench_e2e_judge.py +532 -0
- package/md_cg/bench_e2e_locomo_qa.py +368 -0
- package/md_cg/bench_e2e_qa.py +256 -0
- package/md_cg/bench_en_atoms_public.py +230 -230
- package/md_cg/bench_governance.py +348 -348
- package/md_cg/bench_lme_zh.py +410 -410
- package/md_cg/bench_locomo.py +121 -121
- package/md_cg/bench_locomo_zh.py +450 -450
- package/md_cg/bench_locomo_zh_public.py +147 -147
- package/md_cg/bench_longmem.py +112 -112
- package/md_cg/bench_membench.py +632 -632
- package/md_cg/bench_p0.py +149 -149
- package/md_cg/bench_progressive.py +287 -287
- package/md_cg/bench_role_views.py +238 -238
- package/md_cg/bench_task_ab.py +243 -243
- package/md_cg/bench_task_ab_llm.py +408 -408
- package/md_cg/bench_unified_en.py +204 -204
- package/md_cg/bench_zh_mad.py +601 -601
- package/md_cg/blindspot_tickets.py +123 -123
- package/md_cg/branches.py +301 -285
- package/md_cg/build_postings.py +73 -73
- package/md_cg/ccgc.py +1006 -948
- package/md_cg/census.py +132 -132
- package/md_cg/chain.py +315 -300
- package/md_cg/codeindex.py +531 -531
- package/md_cg/coldverify.py +292 -292
- package/md_cg/comment_gate.py +337 -337
- package/md_cg/cond_compose.py +190 -190
- package/md_cg/cond_facts.py +154 -154
- package/md_cg/cond_template.json +106 -106
- package/md_cg/condition_anchor.py +142 -142
- package/md_cg/conformance.py +726 -726
- package/md_cg/consistency.py +717 -717
- package/md_cg/consolidate.py +1537 -1439
- package/md_cg/corpus.py +110 -110
- package/md_cg/crosscheck.py +1098 -1097
- package/md_cg/crypto.py +3 -1
- package/md_cg/d_meta.py +310 -310
- package/md_cg/datapath.py +78 -18
- package/md_cg/docindex.py +473 -473
- package/md_cg/eval_common.py +575 -575
- package/md_cg/evidence.py +4 -2
- package/md_cg/evolution.py +477 -477
- package/md_cg/export.py +222 -220
- package/md_cg/forgetting.py +581 -581
- package/md_cg/fsutil.py +377 -329
- package/md_cg/hotcache.py +48 -7
- package/md_cg/hyperedge.py +251 -251
- package/md_cg/identity.py +390 -390
- package/md_cg/insight.py +500 -500
- package/md_cg/interop.py +338 -0
- package/md_cg/judgment_manifest.py +177 -0
- package/md_cg/lexicon/build_cedict_en_zh.py +329 -329
- package/md_cg/lexicon/build_standard_en.py +171 -171
- package/md_cg/lexicon/expand_en_zh.py +211 -211
- package/md_cg/lifecycle.py +272 -272
- package/md_cg/linkref.py +280 -280
- package/md_cg/links.py +140 -107
- package/md_cg/mcp_server.py +403 -55
- package/md_cg/md_whitebox.py +345 -345
- package/md_cg/mdcg.py +783 -233
- package/md_cg/mdcos.py +500 -70
- package/md_cg/metacognition.py +591 -591
- package/md_cg/migrate.py +119 -119
- package/md_cg/migrate_aeis.py +221 -221
- package/md_cg/migrate_roleplay.py +293 -293
- package/md_cg/migrate_wisdom_graph.py +360 -360
- package/md_cg/mreview/__init__.py +25 -25
- package/md_cg/mreview/__main__.py +110 -110
- package/md_cg/mreview/bundle.py +178 -178
- package/md_cg/mreview/candidates.py +262 -262
- package/md_cg/mreview/govern.py +694 -693
- package/md_cg/mreview/locate.py +939 -939
- package/md_cg/mreview/pipeline.py +728 -728
- package/md_cg/mreview/rules/duplication.json +21 -21
- package/md_cg/mreview/rules/field_coverage.json +54 -54
- package/md_cg/mreview/rules/source_license.json +21 -21
- package/md_cg/mreview/rules/template_flow.json +21 -21
- package/md_cg/mreview/ruleset.py +252 -252
- package/md_cg/nodefile.py +575 -575
- package/md_cg/pooling.py +484 -472
- package/md_cg/postings.py +300 -298
- package/md_cg/predict.py +1100 -1100
- package/md_cg/progressive.py +123 -123
- package/md_cg/protect.py +272 -272
- package/md_cg/protocol/md_cg_gate.proto +33 -33
- package/md_cg/protocol.py +372 -372
- package/md_cg/provenance.py +582 -582
- package/md_cg/reach.py +453 -453
- package/md_cg/readcache.py +143 -0
- package/md_cg/reconcile.py +228 -0
- package/md_cg/refindex.py +833 -833
- package/md_cg/refine.py +604 -604
- package/md_cg/review_cli.py +170 -0
- package/md_cg/roleviews.py +89 -89
- package/md_cg/routing.py +393 -365
- package/md_cg/run_tests.py +211 -0
- package/md_cg/scrub.py +13 -3
- package/md_cg/security.py +128 -18
- package/md_cg/self_state.py +1029 -1029
- package/md_cg/selfreport.py +152 -151
- package/md_cg/semantic/__init__.py +10 -10
- package/md_cg/semantic/canonical.py +122 -122
- package/md_cg/semantic/en_normalizer.py +364 -364
- package/md_cg/semantic/en_zh_map.json +28694 -0
- package/md_cg/semantic/export_en_zh_map.py +64 -0
- package/md_cg/semantic/unify.py +45 -0
- package/md_cg/semantic/zh_en_atoms.py +139 -139
- package/md_cg/signer.py +7 -4
- package/md_cg/sources.py +816 -582
- package/md_cg/statushdr.py +179 -179
- package/md_cg/stg.py +54 -37
- package/md_cg/subgraph.py +729 -729
- package/md_cg/sustain.py +35 -5
- package/md_cg/tasks.py +470 -470
- package/md_cg/test_access_hints.py +147 -0
- package/md_cg/test_action_derive.py +203 -203
- package/md_cg/test_audit_rotate.py +270 -270
- package/md_cg/test_autonomy.py +143 -143
- package/md_cg/test_bench_governance.py +102 -102
- package/md_cg/test_blindspot_tickets.py +166 -166
- package/md_cg/test_branch_discard_tombstone.py +136 -0
- package/md_cg/test_branches.py +13 -3
- package/md_cg/test_ccg_perturb.py +184 -184
- package/md_cg/test_ccgc.py +433 -433
- package/md_cg/test_census_prune.py +81 -81
- package/md_cg/test_chain_read_isolate.py +168 -0
- package/md_cg/test_cond_compose_anchors.py +76 -76
- package/md_cg/test_cond_match.py +165 -165
- package/md_cg/test_condition_anchor.py +81 -81
- package/md_cg/test_d_meta.py +412 -412
- package/md_cg/test_datapath_device_name.py +203 -0
- package/md_cg/test_datapath_root.py +199 -199
- package/md_cg/test_emit_negtail_cache.py +156 -0
- package/md_cg/test_en_pipeline.py +22 -2
- package/md_cg/test_gain_gate.py +212 -212
- package/md_cg/test_govern_directread.py +421 -0
- package/md_cg/test_health_scale.py +173 -173
- package/md_cg/test_hive_ingest.py +285 -0
- package/md_cg/test_hot_cold.py +215 -215
- package/md_cg/test_hyperedge.py +245 -245
- package/md_cg/test_i26_empty_first_write.py +116 -0
- package/md_cg/test_i27_e041_identity.py +128 -0
- package/md_cg/test_i28_hotcache_prodpath.py +122 -0
- package/md_cg/test_i32_hotcache_env_key.py +218 -0
- package/md_cg/test_identity_attribution.py +96 -15
- package/md_cg/test_index_durability.py +17 -3
- package/md_cg/test_interop.py +95 -0
- package/md_cg/test_interop_judgment.py +228 -0
- package/md_cg/test_issue39_utf8_stdio.py +273 -0
- package/md_cg/test_lifecycle.py +309 -309
- package/md_cg/test_linkref.py +306 -306
- package/md_cg/test_links_concurrent_write.py +188 -0
- package/md_cg/test_lock.py +43 -43
- package/md_cg/test_md_access_parity.py +255 -255
- package/md_cg/test_md_writepath.py +345 -345
- package/md_cg/test_mdstore_search_parity.py +160 -0
- package/md_cg/test_merge_upsert.py +168 -0
- package/md_cg/test_mr_m2.py +587 -587
- package/md_cg/test_mr_m3.py +710 -710
- package/md_cg/test_mr_m4.py +485 -485
- package/md_cg/test_n123_derive_expiry_chain.py +205 -0
- package/md_cg/test_n130_verify_falsified_protect.py +185 -0
- package/md_cg/test_n131_merge_gate.py +205 -0
- package/md_cg/test_p0.py +250 -250
- package/md_cg/test_p1.py +316 -316
- package/md_cg/test_p10_identity.py +173 -173
- package/md_cg/test_p11_consistency.py +233 -233
- package/md_cg/test_p12_metacognition.py +212 -212
- package/md_cg/test_p13_encryption.py +241 -241
- package/md_cg/test_p14_sustain.py +249 -249
- package/md_cg/test_p15_scrub.py +280 -280
- package/md_cg/test_p16_self_state.py +301 -301
- package/md_cg/test_p17_predict.py +354 -354
- package/md_cg/test_p18_whitebox.py +171 -171
- package/md_cg/test_p19_migrate_roleplay.py +149 -149
- package/md_cg/test_p1x_ref_root.py +160 -0
- package/md_cg/test_p20_evolution.py +315 -315
- package/md_cg/test_p21_tokens.py +293 -270
- package/md_cg/test_p22_theory.py +175 -175
- package/md_cg/test_p23_links.py +311 -311
- package/md_cg/test_p24_evidence.py +227 -227
- package/md_cg/test_p25_weights.py +156 -156
- package/md_cg/test_p26_refindex.py +416 -416
- package/md_cg/test_p27_docindex.py +16 -7
- package/md_cg/test_p28_refcheck.py +305 -305
- package/md_cg/test_p29_session_ingest_export.py +354 -333
- package/md_cg/test_p2_mcp.py +3 -0
- package/md_cg/test_p3.py +11 -2
- package/md_cg/test_p30_maintain.py +330 -330
- package/md_cg/test_p31_insight.py +534 -534
- package/md_cg/test_p32_backfill.py +7 -1
- package/md_cg/test_p33_ccg_wiring.py +293 -293
- package/md_cg/test_p34_crosscheck.py +331 -331
- package/md_cg/test_p35_conditioned_claim.py +252 -252
- package/md_cg/test_p36_kp_align.py +230 -230
- package/md_cg/test_p37_condition_space.py +248 -248
- package/md_cg/test_p38_concurrent_flush.py +102 -0
- package/md_cg/test_p38_contextualize.py +273 -273
- package/md_cg/test_p39_verify_flow.py +153 -0
- package/md_cg/test_p39_vision_evidence.py +369 -369
- package/md_cg/test_p40_refine_worklist.py +241 -241
- package/md_cg/test_p41_evolve_patrol.py +224 -224
- package/md_cg/test_p42_provenance.py +269 -269
- package/md_cg/test_p43_pooling.py +412 -398
- package/md_cg/test_p44_md_whitebox.py +231 -231
- package/md_cg/test_p45_session_identity.py +219 -219
- package/md_cg/test_p46_unit_scope.py +272 -272
- package/md_cg/test_p47_session_view.py +316 -0
- package/md_cg/test_p4_fuzzy.py +223 -223
- package/md_cg/test_p5_semantic.py +226 -226
- package/md_cg/test_p6_consolidate.py +440 -387
- package/md_cg/test_p7_goals_recent.py +202 -202
- package/md_cg/test_p8_subgraph_chain.py +200 -200
- package/md_cg/test_p9_forget_protect.py +231 -231
- package/md_cg/test_predict_beta.py +135 -135
- package/md_cg/test_preflight_failclosed.py +100 -100
- package/md_cg/test_progressive.py +146 -146
- package/md_cg/test_propose_tail_index.py +157 -0
- package/md_cg/test_protocol.py +243 -243
- package/md_cg/test_reach.py +378 -378
- package/md_cg/test_reach_keys.py +201 -201
- package/md_cg/test_read_clip.py +141 -141
- package/md_cg/test_read_scope_b27.py +277 -0
- package/md_cg/test_readcache_default_on.py +168 -0
- package/md_cg/test_readcache_precise_inval.py +270 -0
- package/md_cg/test_readcache_prodpath.py +203 -0
- package/md_cg/test_reconcile_v0.py +294 -0
- package/md_cg/test_retr_gates_prodpath.py +140 -0
- package/md_cg/test_retr_s1.py +344 -340
- package/md_cg/test_retr_s1b.py +276 -209
- package/md_cg/test_retr_s3.py +194 -194
- package/md_cg/test_retr_s4.py +163 -163
- package/md_cg/test_retr_s5.py +200 -200
- package/md_cg/test_retr_s6.py +157 -157
- package/md_cg/test_retr_s7.py +392 -384
- package/md_cg/test_retr_s8_time.py +369 -316
- package/md_cg/test_retr_s9_edges.py +286 -286
- package/md_cg/test_retr_s9_entity_ctx.py +9 -3
- package/md_cg/test_retr_score_once.py +208 -0
- package/md_cg/test_review_conformance.py +367 -367
- package/md_cg/test_review_onepass.py +170 -0
- package/md_cg/test_role_views.py +354 -354
- package/md_cg/test_rrf_graph_seed_cache.py +154 -0
- package/md_cg/test_security_audit.py +155 -0
- package/md_cg/test_security_audit_b26.py +161 -0
- package/md_cg/test_security_audit_v21.py +250 -0
- package/md_cg/test_sem_noise.py +242 -242
- package/md_cg/test_semantic_canonical.py +16 -2
- package/md_cg/test_session_isolation.py +168 -0
- package/md_cg/test_snapshot_autoclose.py +187 -0
- package/md_cg/test_subproc_encoding.py +192 -192
- package/md_cg/test_sustain_mutual.py +153 -153
- package/md_cg/test_tail_watermark_race.py +208 -0
- package/md_cg/test_tasks.py +409 -409
- package/md_cg/test_tenant_env_override_warn.py +139 -0
- package/md_cg/test_tenant_registry_corrupt_warn.py +151 -0
- package/md_cg/test_tool_face.py +189 -189
- package/md_cg/test_transfer.py +180 -180
- package/md_cg/test_trust.py +361 -361
- package/md_cg/test_twophase.py +286 -286
- package/md_cg/test_v14_fixes.py +38 -20
- package/md_cg/test_validity_filter.py +280 -280
- package/md_cg/test_verify_answer.py +138 -138
- package/md_cg/test_verify_dirty_reconcile.py +157 -0
- package/md_cg/test_wisdom_md_store.py +292 -292
- package/md_cg/test_writelimit.py +197 -197
- package/md_cg/test_writepipe.py +214 -214
- package/md_cg/theory.py +6 -3
- package/md_cg/tokens.py +85 -14
- package/md_cg/tool_face.py +260 -260
- package/md_cg/trust.py +986 -950
- package/md_cg/twophase.py +231 -231
- package/md_cg/units.py +668 -667
- package/md_cg/vision_evidence.py +667 -666
- package/md_cg/weights.py +624 -624
- package/md_cg/whitebox.py +527 -527
- package/md_cg/whitebox_kb/__init__.py +37 -37
- package/md_cg/whitebox_kb/aeis_core/__init__.py +42 -42
- package/md_cg/whitebox_kb/aeis_core/semantic.py +280 -280
- package/md_cg/whitebox_kb/aeis_core/textutil.py +13 -13
- package/md_cg/whitebox_kb/engine.py +310 -310
- package/md_cg/whitebox_kb/seed_knowledge//346/231/272/350/203/275/350/256/2723.4.md +5260 -5260
- package/md_cg/whitebox_kb/wisdom/browser_units.py +2631 -2631
- package/md_cg/whitebox_kb/wisdom/causal_discover.py +432 -432
- package/md_cg/whitebox_kb/wisdom/chat_engine.py +1506 -1506
- package/md_cg/whitebox_kb/wisdom/compiler_code_units.py +3033 -3033
- package/md_cg/whitebox_kb/wisdom/condition_algebra.py +112 -112
- package/md_cg/whitebox_kb/wisdom/condition_frame.py +315 -315
- package/md_cg/whitebox_kb/wisdom/condition_kb.py +108 -108
- package/md_cg/whitebox_kb/wisdom/conflict_map.json +4445 -4445
- package/md_cg/whitebox_kb/wisdom/core/lexer.py +512 -512
- package/md_cg/whitebox_kb/wisdom/core/name_checker.py +1023 -1023
- package/md_cg/whitebox_kb/wisdom/cspmn.py +258 -258
- package/md_cg/whitebox_kb/wisdom/csre.py +264 -264
- package/md_cg/whitebox_kb/wisdom/danmaku_audit.py +252 -252
- package/md_cg/whitebox_kb/wisdom/distilled_condition_units.json +6417 -6417
- package/md_cg/whitebox_kb/wisdom/docs/WB-EVAL-20260902b.json +1950 -1950
- package/md_cg/whitebox_kb/wisdom/docs/WB-EVAL-20260902c.json +1296 -1296
- package/md_cg/whitebox_kb/wisdom/docs/whitebox_capability_graph_demo.json +59 -59
- package/md_cg/whitebox_kb/wisdom/graph_db_units.py +3089 -3089
- package/md_cg/whitebox_kb/wisdom/knowledge_points.py +318 -318
- package/md_cg/whitebox_kb/wisdom/md_access.py +470 -470
- package/md_cg/whitebox_kb/wisdom/md_store.py +276 -251
- package/md_cg/whitebox_kb/wisdom/migrate_wisdom.py +476 -476
- package/md_cg/whitebox_kb/wisdom/multilang_ir.py +128 -124
- package/md_cg/whitebox_kb/wisdom/navigate.py +248 -248
- package/md_cg/whitebox_kb/wisdom/neural_retrieve.py +224 -224
- package/md_cg/whitebox_kb/wisdom/os_units.py +2735 -2735
- package/md_cg/whitebox_kb/wisdom/pattern_separation.py +376 -376
- package/md_cg/whitebox_kb/wisdom/prereq_map.json +364 -364
- package/md_cg/whitebox_kb/wisdom/python_code_units.py +2766 -2766
- package/md_cg/whitebox_kb/wisdom/role_solidified.json +7 -7
- package/md_cg/whitebox_kb/wisdom/route_memory.py +244 -244
- package/md_cg/whitebox_kb/wisdom/scene_reconstruction.py +148 -148
- package/md_cg/whitebox_kb/wisdom/snr_report.json +40 -40
- package/md_cg/whitebox_kb/wisdom/test_code_compose_domains.py +7541 -7541
- package/md_cg/whitebox_kb/wisdom/test_compiler_self_bootstrap.py +354 -354
- package/md_cg/whitebox_kb/wisdom/test_ecosystem_assembly.py +158 -158
- package/md_cg/whitebox_kb/wisdom/test_ecosystem_demos.py +193 -193
- package/md_cg/whitebox_kb/wisdom/test_graph_db.py +292 -292
- package/md_cg/whitebox_kb/wisdom/test_python_self_bootstrap.py +160 -160
- package/md_cg/whitebox_kb/wisdom/trigger_words_index.json +4366 -4366
- package/md_cg/whitebox_kb/wisdom/verifier.py +1297 -1297
- package/md_cg/writelimit.py +356 -356
- package/md_cg/writepipe.py +20 -8
- package/package.json +101 -96
- package/skills/plugin.json +54 -54
- package/skills/skills/designer-perspective/SKILL.md +158 -158
- package/skills/skills/designer-perspective/references/01-observation-position.md +66 -66
- package/skills/skills/designer-perspective/references/02-structure-recognition.md +62 -62
- package/skills/skills/designer-perspective/references/03-direction-judgment.md +55 -55
- package/skills/skills/designer-perspective/references/04-qualification-verdict.md +72 -72
- package/skills/skills/designer-perspective/references/05-condition-attribution.md +74 -74
- package/skills/skills/designer-perspective/scripts/designer.py +545 -545
- package/skills/skills/designer-perspective/tests/cases.jsonl +17 -17
- package/skills/skills/designer-perspective/tests/selftest.py +61 -61
- package/skills/skills/lingshu-browser/SKILL.md +60 -60
- package/skills/skills/lingshu-compiler/SKILL.md +56 -56
- package/skills/skills/lingshu-compiler/units/analyze-type-infer/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/check-name-real/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compile-assign/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compile-expr-tree/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compile-full-pipeline/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compile-func-def/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compile-if-then/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compile-logic-expr/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compile-recursive/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compile-scope/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compile-type-check/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compile-while/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-0010c4bf/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-0355bffb/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-0361708a/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-054a0414/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-05a1691a/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-05eeed1e/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-0622a1f6/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-08b54217/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-0a62b70c/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-0ab24d00/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-0e093688/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-0e82b966/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-0ee9b9b9/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-0f3787e6/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-1028685f/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-11897630/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-16661b9b/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-1f722303/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-25be1262/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-2a76ba07/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-2dbea54a/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-2df52f16/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-2ec2c9d2/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-2f8c8f39/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-38378ac7/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-39457d2e/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-39fb5926/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-47f4fbfa/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-4a8cd1f1/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-4b230b6b/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-4cbbda95/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-4d5a68ff/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-56dc9bdd/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-57e76ebe/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-61ff016b/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-63eae588/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-64c4224b/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-66377955/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-674ab4f6/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-6af76fd6/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-6d979db2/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-6de326c0/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-6f1c0eea/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-724d6c9b/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-7690177f/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-7b229a7d/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-7f471b3a/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-815cad08/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-83c61634/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-8c798c81/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-8dd747ac/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-90324d0a/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-94b8d72d/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-94f12231/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-95937c16/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-98a5625b/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-98b4c42f/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-98e3894a/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-9bdfe4b8/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-9d9b4e83/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-9f7be5ad/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-a6daf076/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-a7995a0c/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-a8399248/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-aabbd099/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-afe169d8/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-b0678bda/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-b09bd196/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-b1396e23/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-b9b31ce0/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-c10264a7/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-cb1e8e4b/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-ccafd438/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-cda9c262/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-ce648068/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-cf5776a4/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-d974e5d3/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-e3979fd3/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-eb1cf2b5/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-ecb30d5b/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-f8c8b24b/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-f99fedbe/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-fa8ff5f7/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-fe1b058d/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/lex-chinese-program/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/lex-dao-de-jing/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/lex-nine-chapters/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/vm-arithmetic/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/vm-array-ops/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/vm-closure-call/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/vm-closure-create/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/vm-compare/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/vm-cond-jump/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/vm-cond-space/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/vm-exception/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/vm-func-call/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/vm-loop-run/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/vm-profiling/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/vm-refcount/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/vm-run-loop/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/vm-short-circuit/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/vm-stack-guard/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/vm-stack-ops/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/vm-trust-accum/SKILL.md +45 -45
- package/skills/skills/lingshu-graph/SKILL.md +63 -63
- package/skills/skills/lingshu-net/SKILL.md +48 -48
- package/skills/skills/lingshu-os/SKILL.md +64 -64
- package/skills/skills/lingshu-pylang/SKILL.md +71 -71
- package/src/bridge.ts +33 -0
- package/src/hooks.ts +38 -2
- package/src/index.ts +526 -518
- package/src/lib/datapath.ts +326 -326
- package/src/lib/mdcg_client.ts +413 -413
- package/src/lib/mutual.ts +428 -428
- package/src/lib/prompt_safety.ts +62 -62
- package/src/lib/python_path.ts +71 -71
- package/src/lib/roleplay_web.ts +116 -29
- package/src/lib/token_store.ts +13 -3
- package/src/tools.ts +212 -212
- package/zcode/AGENTS.md +11 -3
- package/zcode/README.md +41 -41
- /package/docs/{mdcg → hive}//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" +0 -0
|
@@ -1,588 +1,588 @@
|
|
|
1
|
-
# 认知图 · 索引与工程规范化 计划 v0.1
|
|
2
|
-
|
|
3
|
-
> 来源:`agi-architectures/外部agi架构可学习优点.md` 复核表 **#12 文档体系** 与 **#8 manifest 元数据规范**
|
|
4
|
-
> (两条均判 DEFER,先决条件分别是「先说清缺哪一份索引」「确认三字段是否真缺」——本文档即对该先决的正面回答)
|
|
5
|
-
> 目标工程:**dsh-memory / md_cg(认知图 + 记忆 OS)**
|
|
6
|
-
> 证据口径:源码级逐行核对(`mcp_server.py` / `codeindex.py` / `mdcg.py` / `nodefile.py` / `sources.py` / `sustain.py`)
|
|
7
|
-
> 时间:2026-09-10
|
|
8
|
-
|
|
9
|
-
---
|
|
10
|
-
|
|
11
|
-
## 判定单
|
|
12
|
-
|
|
13
|
-
```
|
|
14
|
-
条件空间:观测位置=全局(设计者) | 观测工具=md_cg 源码逐行核对 + docs/ 30 份 + 复核表 #12/#8
|
|
15
|
-
| 时间窗口=当前迭代 | 存在约束=零第三方依赖(纯标准库) / md 单一真相源 / 有意剔除神经嵌入 / 不复制实现正文
|
|
16
|
-
白箱三问:依据=每条给文件行级证据(§一) | 条件=先决已核对,未核对项显式标出 | 错后=索引若复制代码正文,
|
|
17
|
-
会造出「第二份真相」,破坏「md 可审计 + git 可 diff」这一存在理由,且代码改了索引不报错(静默漂移)
|
|
18
|
-
裁决:ACCEPT 6 / DEFER 2 / REJECT 2
|
|
19
|
-
依据:条件证据见 §一,非相似度
|
|
20
|
-
缺失维度:观测位置之外的「索引粒度」——文件级 / 符号级 / 段落级三者未定义,本计划定为「符号级(代码) + 章节级(文档)」
|
|
21
|
-
验证路径:R0 收口基线 → R1 ref 协议+回读 → R2 文档索引 → R3 增量+漂移巡检 → R4 互链 → R5 规范化产出
|
|
22
|
-
关注信号:`index_code` 全仓 0 测试;`index_dir` 静默截断;代码节点恒定被判 BLINDSPOT(§二)
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
---
|
|
26
|
-
|
|
27
|
-
## 一、现状核对(事实层)
|
|
28
|
-
|
|
29
|
-
### 1.1 已具备(不要重复造)
|
|
30
|
-
|
|
31
|
-
| 能力 | 证据 |
|
|
32
|
-
|---|---|
|
|
33
|
-
| 条件代码图模块已存在,AST 抽注释/接口 | `md_cg/codeindex.py:1-13`(设计声明)、`:53-75`(`extract`)、`:78-88`(`render`)、`:91-95`(`node_id`)、`:98-118`(`index_dir`) |
|
|
34
|
-
| MCP 已接线 | `md_cg/mcp_server.py:1492-1516`(`cg(op=index_code)`),op 声明在 `:531-532`、`:545-546`、`:616-618` |
|
|
35
|
-
| ref 字段**真的落盘**(不是被 kwargs 吞掉) | `mcp_server.py:1509` 传 `code_ref={path,name,kind,lineno,end}` → `md_cg/mdcg.py:388`(`**extra`)→ `:452`(`fm.update(extra)`) |
|
|
36
|
-
| ref 字段**能解析回来** | `md_cg/nodefile.py:51`(值一律 `json.dumps`)→ `:73`(`json.loads`),嵌套 dict 保真 |
|
|
37
|
-
| ref 字段**能返回给调用方** | `mcp_server.py:730-734`(`_node_view` 返回整个 `frontmatter`) |
|
|
38
|
-
| 已有 op 注册表与权限校验 | `md_cg/tokens.py:56-59`(`ALL_OPS`,含 `index_code`)+ `test_p21_tokens.py` |
|
|
39
|
-
| 增量 watermark 机制有现成范式 | `md_cg/sources.py:219-253`(`_sources.json`:`{source_key:{t,seq,count}}`)、`:257-334`(`ingest` 增量+去重+幂等) |
|
|
40
|
-
| 自愈只修派生物、逐条留痕的纪律已定 | `md_cg/sustain.py:12-14`(诊断只读)、`:277-302`(`_audit` + 幂等修复 + 失败不拖垮进程) |
|
|
41
|
-
| 「非 CCG 正文」有两种既有豁免范式 | `mdcg.py:513-519`(目标节点**按 CCG 格式渲染**以避开 BLINDSPOT)、`mdcos.py:313-316`(`ccg_exempt` legacy 记忆 → DEFER) |
|
|
42
|
-
|
|
43
|
-
### 1.2 真实缺口(本计划要闭合的)
|
|
44
|
-
|
|
45
|
-
| # | 缺口 | 证据(现状) | 严重度 |
|
|
46
|
-
|---|---|---|---|
|
|
47
|
-
| **A** | **文档索引完全不存在**——认知图索引不到 `docs/*.md` 的任何位置 | 全仓无 `.md` 索引模块;`sources.py` 只有 `JsonlSource`(`:49`)/`DSHSessionSource`(`:112`),均吃会话流不吃文档 | 高(正是用户的核心诉求) |
|
|
48
|
-
| **B** | **代码节点恒定被判 BLINDSPOT** | `codeindex.render`(`codeindex.py:80-88`) 只输出 `# path::name (kind, L..)` / `# sig` / `# 注释` / `# doc:`,**不含 CCG 5 要素标记**;而 `judge_qualification` 第一步就是 `ccg_completeness` 不全 → `BLINDSPOT`(`mdcg.py:709-714`),且该判定在**检索路径上逐条调用**(`mdcos.py:746`、`:869`) | **最高(存得进、判不了、白索引)** |
|
|
49
|
-
| **C** | **只能索引,不能回读**——没有「按 ref 打开源文件正文」的 op | `mcp_server.py:1492-1516` 只写不读;无 ref 解析/定位入口 | 高 |
|
|
50
|
-
| **D** | **无漂移/悬空检测**——源码改了、行号漂了,索引不知道 | 无 ref 校验代码;`sustain.diagnose`(`sustain.py:141-266`) 的巡检项里无 ref | 高(静默故障,正是方案自己警惕的那类) |
|
|
51
|
-
| **E** | **只认 `.py`** | `codeindex.py:19` `SUFFIX = (".py",)`;本仓 `src/*.ts`、`test/*.ts`、`scripts/*.ps1`、`cordova/cordis*.yml` 全在索引外 | 中 |
|
|
52
|
-
| **F** | **全量重扫 + 静默截断** | `codeindex.py:107-108`:达到 `max_files`/`max_items` **直接 return**,调用方 `mcp_server.py:1513` 只回 `indexed/error_count`,不报 `truncated` → 索引不全但不告警 | 中(静默不全) |
|
|
53
|
-
| **G** | **零测试** | `index_code` 仅出现在 `tokens.py:59` 与 `mcp_server.py`(5 处);`test_*.py` 中 **0 命中** | 中 |
|
|
54
|
-
| **H** | **code 图与 doc 图不互链** | `links.py` 是蜂群信任(`links.py:1-20`),与代码/文档无关;代码节点 tags 仅 `["code","code:<kind>"]`(`mcp_server.py:1505`),无指向文档的边 | 中(「互链」是索引的价值上限) |
|
|
55
|
-
| **I** | **工程规范化的三份索引文件缺位** | `docs/` 30 份含方案/评分/工具表,但**无模块索引、无功能矩阵、无文件清单**;33 个 MCP 工具的 `inputSchema`(`mcp_server.py:96-690`) 无 timeout/执行语义;无跨进程可读的 `status.json` | 中(= 复核表 #12/#8 本体) |
|
|
56
|
-
|
|
57
|
-
### 1.3 已核对但**尚未核实**的点(诚实登记,不当作事实)
|
|
58
|
-
|
|
59
|
-
1. ~~`cg.add` 未显式设 `role`(`mcp_server.py:1503-1511`),代码/文档节点会落到哪个 role 档、是否进正排——**未验证**,R1 必须实测。~~
|
|
60
|
-
→ **已实测(R1)**:代码节点 frontmatter **不带 `role` 字段**(`index_code` 未传),但检索/资格判定不依赖 `role`——实测 `op=read` 能命中并按 CCG 完整度判 **ACCEPT**(`tags=["code","code:<kind>"]`、`verification_basis` 落盘)。故 `role` 缺口不影响 R1 目标,留待 R5(FEATURE_MATRIX)统一裁定标签体系。
|
|
61
|
-
2. `index_dir` 的 `condition_space.observation_position = path.split("/")[0]`(`mcp_server.py:1506-1507`) 生成的桶分布是否健康(对齐 `routing` 分区健康度自检)——**未验证**。
|
|
62
|
-
3. ~~文档节点的层与密级(`knowledge` vs `contextual`、`public` vs 继承目录)——**未裁定**,R2 前定。~~
|
|
63
|
-
→ **已裁定(R2)**:layer 默认 `knowledge`(与代码节点同层,保证进默认召回;`contextual` 表示情境绑定会过期,会让文档掉出默认召回);密级**默认 `internal` 且显式写入 frontmatter**(不依赖节点默认值——审计时看不出意图),路径段命中私有提示词时**再保守降为 `private`**(只可能更严,宁可漏召回不可泄漏)。实测 `internal:3 / private:1`。详见 §十三-3。
|
|
64
|
-
|
|
65
|
-
---
|
|
66
|
-
|
|
67
|
-
## 二、为什么必须先修 B(避免把预算花在错误的地方)
|
|
68
|
-
|
|
69
|
-
当前 `index_code` 的产出**在检索路径上是残废的**:
|
|
70
|
-
|
|
71
|
-
```
|
|
72
|
-
judge_qualification(mdcg.py:709-714)
|
|
73
|
-
第 1 步:ccg_completeness(content) 不全 → 直接返回 BLINDSPOT
|
|
74
|
-
↑ codeindex.render 不产 CCG 标记(codeindex.py:80-88)
|
|
75
|
-
↑ 第 3 步「verification_basis 缺失 → DEFER」根本走不到,
|
|
76
|
-
尽管 mcp_server.py:1508 已正确填了 verification_basis="compiler"
|
|
77
|
-
```
|
|
78
|
-
|
|
79
|
-
同仓已有两个正确范式可抄:目标节点**按 CCG 格式渲染**(`mdcg.py:513-519`,注释直写「保证 judge_qualification 能给出 ACCEPT 而非 BLINDSPOT」)、legacy 记忆走 `ccg_exempt`(`mdcos.py:313-316`)。
|
|
80
|
-
|
|
81
|
-
**结论**:索引层的第一个 PR 不是「加更多后缀」,而是**让渲染产物符合 CCG 5 要素**。否则索引量涨 10 倍,可用性仍是 0。
|
|
82
|
-
|
|
83
|
-
---
|
|
84
|
-
|
|
85
|
-
## 三、目标(可验收的三句话)
|
|
86
|
-
|
|
87
|
-
1. **回得去**:任何一条被检索到的节点,都能给出行级可定位的位置——
|
|
88
|
-
代码 → `<path>:<lineno>-<end>`;文档 → `<path>#<heading-path>:<lineno>-<end>`。
|
|
89
|
-
2. **找得到**:`.py` / `.md` 全量可索引(`.ts` 为弱提取器),不漏召回,且索引结果能回答「这条功能在文档里怎么写的 / 对应哪段代码」。
|
|
90
|
-
3. **守得住**:索引是**派生物**——可删可重建;源文件变更后能被巡检发现(漂移/悬空),且修复不改源文件、逐条留痕。
|
|
91
|
-
|
|
92
|
-
---
|
|
93
|
-
|
|
94
|
-
## 四、设计:统一 ref 协议
|
|
95
|
-
|
|
96
|
-
沿用 `code_ref` 的形状,扩成 `ref` 协议,作为 frontmatter 一等字段(`nodefile` 已支持任意 JSON 值,无需改格式层)。
|
|
97
|
-
|
|
98
|
-
```jsonc
|
|
99
|
-
// 代码节点
|
|
100
|
-
"code_ref": {"path":"md_cg/mdcg.py","name":"MdCG.add","kind":"def",
|
|
101
|
-
"lineno":383,"end":462,"lang":"py","hash":"9f2c1ab3de44"}
|
|
102
|
-
|
|
103
|
-
// 文档节点
|
|
104
|
-
"doc_ref": {"path":"docs/mdcg/认知图_MD目录方案_v0.1.md","heading":"9. 分阶段实施(稳健)",
|
|
105
|
-
"heading_path":["9. 分阶段实施(稳健)"],"level":2,
|
|
106
|
-
"lineno":491,"end":513,"anchor":"9-分阶段实施稳健","hash":"41ab77c0e1d9"}
|
|
107
|
-
```
|
|
108
|
-
|
|
109
|
-
**不变量(写进代码注释与测试)**
|
|
110
|
-
- 正文**永不复制实现/全文**——只存「索引块 + ref 指路」(对齐 `codeindex.py:4-6` 的既有纪律)。
|
|
111
|
-
- `hash` = 被引用行的 sha1 前 12 位,用于漂移判定(不是内容寻址,是变更探测)。
|
|
112
|
-
- 索引是派生物:`_refindex.json` 丢了/坏了,能从源文件全量重建。
|
|
113
|
-
- 源文件**只读**:索引一轮后源文件 mtime 全不变(对齐 P0 的 mtime 只读断言)。
|
|
114
|
-
|
|
115
|
-
**CCG 渲染模板(修 B 的关键)**
|
|
116
|
-
|
|
117
|
-
代码节点(`功能名/子功能/执行/验证方式/不适用条件` 五个必需标记齐 → `complete=True`):
|
|
118
|
-
|
|
119
|
-
```
|
|
120
|
-
# 功能名:{name}({kind})
|
|
121
|
-
# 生效条件:大域={top_dir};检索「{name}」或「{path}」
|
|
122
|
-
# 子功能:{所属模块/类} · {docstring 首段≤120字}
|
|
123
|
-
# 执行:{sig}
|
|
124
|
-
# 验证方式:compiler(AST 已解析,非人工声明)
|
|
125
|
-
# 不适用条件:其它大域的同名符号
|
|
126
|
-
```
|
|
127
|
-
|
|
128
|
-
文档节点:
|
|
129
|
-
|
|
130
|
-
```
|
|
131
|
-
# 功能名:{heading}
|
|
132
|
-
# 生效条件:文档={rel_path};检索「{heading}」或正文关键词
|
|
133
|
-
# 子功能:{父 heading 路径}
|
|
134
|
-
# 执行:{首段摘要≤200字}
|
|
135
|
-
# 验证方式:data(原始文档为准)
|
|
136
|
-
# 不适用条件:其它文档的同名标题
|
|
137
|
-
```
|
|
138
|
-
|
|
139
|
-
> 取舍:docstring/首段进 `子功能` 行而不另起 `# doc:` 行,是为了让**单条节点的全部可检索信息都在 CCG 结构内**,避免出现「检索能命中、判定看不见」的字段。
|
|
140
|
-
|
|
141
|
-
---
|
|
142
|
-
|
|
143
|
-
## 五、模块改动表
|
|
144
|
-
|
|
145
|
-
| 文件 | 动作 | 职责 |
|
|
146
|
-
|---|---|---|
|
|
147
|
-
| `md_cg/refindex.py` | **新增** | ref 协议常量 + 提取器注册表 + 统一 `index_dir` 调度 + `_refindex.json` 增量 + `check_refs()` 漂移巡检 + `read_ref()` 回读 + **节点级对账** `prune_orphans()`(过期代)/ `prune_dangling()`(悬空)(R3.1)+ `skip_dirs` 透传(R3.2) |
|
|
148
|
-
| `md_cg/docindex.py` | **新增** | `.md` 章节切块:ATX 标题栈、heading_path、行号区间、首段摘要、anchor;`skip_dirs` 追加排除(R3.2,复用 `codeindex.skip_matcher`) |
|
|
149
|
-
| `md_cg/codeindex.py` | **改造** | ①`render()` 改产 CCG 5 要素(修 B)②后缀注册化(`.py` 走 AST、`.ts/.js` 走弱提取器)③`index_dir` 返回 `truncated` 与 `files_scanned`(修 F)④`node_id` 保持稳定(重复索引幂等)⑤`skip_matcher()` + `skip_dirs`(R3.2,两条链路的唯一实现) |
|
|
150
|
-
| `md_cg/mcp_server.py` | **扩** | 新增 `op=ref`(`action=read\|check\|stat\|prune`,兼回读 / 巡检 / 清退);`op=index_code` 保留为别名并透传 `truncated`;`op=index_doc`;op 枚举与 `:542-546` 同步;`index_code`/`index_doc` 索引后自动节点级对账(`prune` 默认开、`prune_dry_run` 可预演,R3.1);`skip_dirs` 透传 + `skipped_dirs` 回报(R3.2) |
|
|
151
|
-
| `md_cg/tokens.py` | **改** | `ALL_OPS`(`:56-59`) 加 `ref` / `index_doc`(漏改会被权限层直接拒),`test_p21_tokens.py` 同步 |
|
|
152
|
-
| `md_cg/sustain.py` | **扩** | `diagnose` 增巡检项 `ref_stale` / `ref_dangling`,`heal` 动作为「重建索引」(派生物,符合现有自愈边界 `:12-14`) |
|
|
153
|
-
| `md_cg/mdcos.py` | **扩** | 检索结果带 `ref` 与 `ref_kind`(读侧不改召回逻辑,只加字段) |
|
|
154
|
-
| `md_cg/test_p26_refindex.py` | **新增** | 见 §七 |
|
|
155
|
-
|
|
156
|
-
---
|
|
157
|
-
|
|
158
|
-
## 六、分阶段实施
|
|
159
|
-
|
|
160
|
-
| 阶段 | 做什么 | 验证 | 状态 |
|
|
161
|
-
|---|---|---|---|
|
|
162
|
-
| **R0** | ✅ **已完成**(§十一 取证纪录):全量 28 用例基线取证 + 修 4 条缺陷(`corpus.reset_root` 全量 rmtree / 3 处编码) | 28/28 全绿;P0 25/25、P1 38/38、P11 27/27、P12 32/32 | ✅ |
|
|
163
|
-
| **R1** | ✅ **已完成**(§十二 取证纪录):`codeindex.render` 改产 CCG 6 行、`EXTRACTORS` 后缀注册化(AST 精确 / TS-JS 弱提取)、`op=ref` 回读 + 漂移检测、`truncated`/`skipped_suffixes` 显式上报;实测 §1.3-1(role 档位) | ✅ 代码节点判定 **ACCEPT**(不再 BLINDSPOT);`op=ref` 回读文本与源区间**逐字一致**、`hash_match` 检出漂移 | ✅ |
|
|
164
|
-
| **R2** | ✅ **已完成**(§十三 取证纪录):`docindex.py`(章节切块 + CCG 渲染 + `doc_ref`)+ `op=index_doc`;裁 §1.3-3(层/密级) | ✅ `docs/` 全部 md(实测 **19/19**,非静默跳过)切块可定位;`docs/mdcg/认知图_MD目录方案_v0.1.md` 的 §9 表被判 **ACCEPT** 且给出行号 `L491-L516` | ✅ |
|
|
165
|
-
| **R3** | ✅ **已完成**(§十四 取证纪录):`_refindex.json` 增量(抄 `sources.Ingestor` 范式)+ `check_refs` 漂移巡检 + `truncated` 显式上报 + 接入 `sustain.diagnose` | ✅ 改源文件一行 → `ref check` 报 stale(重建后消除);删源 → dangling;`max_files` 触发时 `truncated=true` 且写进水位;`diagnose` 报 `ref_stale`、`heal` 的 `rebuild_refs` 修复 | ✅ |
|
|
166
|
-
| **R3.1** | ✅ **已完成**(§十五 取证纪录,2026-09-12):节点级对账——`prune_orphans`(改标题产生的过期代)+ `prune_dangling`(源已删的悬空节点),接入 `index_code`/`index_doc` 与 `op=ref action=prune` | ✅ P27 §10/§10b 全绿;真实现场悬空 **20 → 0**(`LIVE_HITS=0`/`TRASH_HITS=20`);受影响面 7 模块 6 绿 1 环境红 | ✅ |
|
|
167
|
-
| **R3.2** | ✅ **已完成**(§十五 取证纪录,2026-09-12):索引器**追加排除** `skip_dirs`(`index_code`/`index_doc`)——把 `.gitignore` 已忽略却物理留存的实验产物挡在门外,替代「调大 `max_files`」这一掩盖手段 | ✅ P27 **75/0** 全绿(§9 由 `500/2388` 红转 `30/30`);新增 §9b 验证匹配口径 / 只增不减 / 可审计;受影响面 10 模块全绿 | ✅ |
|
|
168
|
-
| **R4** | 修 H:code↔doc 互链(文档中出现的符号名/路径 → 建边指向 code 节点;反向亦然) | 「某功能」检索能同时给出代码位置与文档位置 | ⬜ |
|
|
169
|
-
| **R5** | 修 I(工程规范化产出,见 §九) | 三份索引文件可由 `index_ref` **自动生成**,非手写 | ⬜ |
|
|
170
|
-
| DEFER | `.ps1/.vbs/.yml` 提取器;跨副本身份下的 ref 可见性 | 先决:R4 完成且互链被实际使用 | ⬜ |
|
|
171
|
-
|
|
172
|
-
---
|
|
173
|
-
|
|
174
|
-
## 七、验收(`test_p26_refindex.py`,对齐既有 `test_pNN` 脚本式 `check()` 风格)
|
|
175
|
-
|
|
176
|
-
1. **ref 落盘**:代码/文档节点的 `frontmatter.code_ref`/`doc_ref` 五字段完整,且 `loads` 往返不丢(`nodefile.py:73`)。
|
|
177
|
-
2. **CCG 合规(修 B 的守门断言)**:`ccg_completeness(content)["complete"] is True`;`judge_qualification(...)["state"] != BLINDSPOT`。
|
|
178
|
-
3. **回读一致**:`read_ref(node_id)` 返回的区间文本与该文件当前内容**逐字相同**(非「看起来对」)。
|
|
179
|
-
4. **漂移检测**:在目标文件插入 1 行 → `ref check` 报 `stale`;删除该符号 → 报 `dangling`;两者都不抛异常、都不改源文件。
|
|
180
|
-
5. **只读契约**:索引一轮前后,被索引的 `.py`/`.md` **mtime 全不变**、源文件 hash 不变。
|
|
181
|
-
6. **幂等**:同一目录索引两次 → 节点数不变、`node_id` 稳定、`_refindex.json` 不膨胀。
|
|
182
|
-
7. **不复制正文**:代码节点 content 内「函数体语句」0 命中(如断言不含 `return ` 之外的可执行行);content 长度 ≤ 阈值。
|
|
183
|
-
8. **截断诚实**:`max_files=1` 时 `truncated is True` 且给出建议值。
|
|
184
|
-
9. **文档切块**:heading_path 与行号可定位;正文含 `---` 分隔线不污染 frontmatter(`nodefile.py:19-23` 的既有纪律不回归)。
|
|
185
|
-
10. **权限**:`op=ref` 走 `tokens` 校验;无权限 actor 读取越权层的 ref 应被拒。
|
|
186
|
-
11. **新 op 注册**:`tokens.ALL_OPS` 与 `mcp_server` 的 op 枚举一致(防漏改)。
|
|
187
|
-
|
|
188
|
-
---
|
|
189
|
-
|
|
190
|
-
## 八、风险与代价
|
|
191
|
-
|
|
192
|
-
| 风险 | 后果 | 处理 |
|
|
193
|
-
|---|---|---|
|
|
194
|
-
| `.md` 切块过细 | 30 份文档切出上千节点,污染正排、拖慢检索 | 只切 `level<=3`;正文 <200 字的 section 合并进父节点;先跑一次统计再定阈值 |
|
|
195
|
-
| 行号漂移 | ref 指向错误代码(**静默**) | `hash` + `check_refs` 巡检 + 挂 `sustain.diagnose`;文档不承诺「永久有效」,只承诺「可检出」 |
|
|
196
|
-
| 双份真相 | 索引与源文件各自演化 | 索引是派生物、可重建;doc 索引**不存全文** |
|
|
197
|
-
| `.ts` 无 AST | 质量劣于 `.py` | 标准库无 TS parser → v0.1 用「行首 regex + 上邻注释」**弱提取器**,代码里显式标注「弱」,不冒充 AST |
|
|
198
|
-
| 密级泄漏 | 私有文档被索引进公开层 | 显式声明 `sensitivity`,不依赖默认值;越权写入由 `security` 事前拦截(`sources.py:296-307` 已有同款范式) |
|
|
199
|
-
| 索引自身成单点 | `_refindex.json` 损坏导致增量失效 | 降为纯派生物,可全量重建(对齐 `_index.json` 的既有裁定) |
|
|
200
|
-
|
|
201
|
-
---
|
|
202
|
-
|
|
203
|
-
## 九、对复核表 #12 / #8 的正面回答(DEFER → ACCEPT)
|
|
204
|
-
|
|
205
|
-
复核表 #12 的先决是「**先说清缺哪一份索引**」。答案:
|
|
206
|
-
|
|
207
|
-
| #12 提的三份文件 | 对应本计划 | 关键增量 |
|
|
208
|
-
|---|---|---|
|
|
209
|
-
| `DOCUMENTATION_INDEX`(文档索引) | **R2 `docindex.py` + R5 产出** | 不是手写目录,而是**认知图可检索的 doc 节点**(带 heading_path + 行号) |
|
|
210
|
-
| `FEATURE_MATRIX`(功能矩阵) | **R5 产出** | 矩阵由 `index_ref` 自动生成:`工具 × op × 实现文件:行 × 测试文件`,并**校验「每个 op 都能找到测试」**(`index_code` 的 0 覆盖正是这条能抓出来的) |
|
|
211
|
-
| `FILE_INVENTORY`(文件清单) | **R5 产出(自动生成)** | 由索引自动产出而非手写——这才能避免「清单与代码脱节」这一常规失败 |
|
|
212
|
-
|
|
213
|
-
复核表 #8(manifest:timeout / protocol / 执行语义)同样可转 ACCEPT,且**用协议原生字段而非自造**:
|
|
214
|
-
MCP 规范本身有 `annotations`(`readOnlyHint` / `destructiveHint` / `idempotentHint` / `openWorldHint`)——这恰好是「执行语义分类」;
|
|
215
|
-
md_cg 侧再补 `timeout_ms` 与 `needs_clearance` 两个扩展字段。裁剪到 R5,随三份索引文件一起产出。
|
|
216
|
-
|
|
217
|
-
---
|
|
218
|
-
|
|
219
|
-
## 十、诚实边界
|
|
220
|
-
|
|
221
|
-
1. **本计划是「可索引」,不是「可理解」**:doc/code 索引仍是**确定性静态索引**,不含语义蕴含;跨表述的「这段代码其实实现了那段文档的意思」不解决(与 `memory_score.md:36` 「有意剔除神经嵌入」一致)。
|
|
222
|
-
2. **索引质量上限 = 源文件注释质量**:白箱路线的固有代价。没有 docstring 的函数只能索引到签名。
|
|
223
|
-
3. **`hash` 不是内容寻址**:只做变更探测,不保证「内容相同即同节点」。
|
|
224
|
-
4. ~~R0 有一个待闭环疑点~~ → **已在 §十一 闭环**。「`test_p1` EXIT 1 且输出仅 165 字节」的根因与 `test_p0` 同一类:受管环境下 `os.remove` 被环境 shim 接管,
|
|
225
|
-
删除量越阈即中断进程;且失败形态是**静默截断 + 非零退出**,不是一条 FAIL。现已改为不清空目录(原子覆盖 + 等号守门),并全量复跑验证。
|
|
226
|
-
5. **当前工作区未提交**:`docs/mdcg/认知图_MD目录方案_v0.1.md`、`bench_p0.py`、`census.py`、`test_p0.py`、`test_p1.py` 已改,`md_cg/corpus.py` 未跟踪——R0 应先收口提交,再动索引层。
|
|
227
|
-
|
|
228
|
-
---
|
|
229
|
-
|
|
230
|
-
## 十一、R0 取证纪录(已完成 · 2026-09-10)
|
|
231
|
-
|
|
232
|
-
### 11.1 基线复跑结果(修前 vs 修后)
|
|
233
|
-
|
|
234
|
-
| 用例 | 修前 | 修后 | 说明 |
|
|
235
|
-
|---|---|---|---|
|
|
236
|
-
| `test_p0` | ❌ EXIT 1,输出 **443 字节**,**无任何 FAIL 项** | ✅ **25/25** | 死在第一条断言之前 |
|
|
237
|
-
| `test_p1` | ✅ EXIT 0(偶发 ❌ EXIT 1 / 165 字节) | ✅ **38/38** | 新增守门断言 1 条 |
|
|
238
|
-
| `test_p11_consistency` | ❌ EXIT 1,输出 725 字节 | ✅ **27/27** | **此前从未真正执行过** |
|
|
239
|
-
| `test_p12_metacognition` | ❌ EXIT 1,输出 725 字节 | ✅ **32/32** | **此前从未真正执行过** |
|
|
240
|
-
| 全量 28 个用例 | 非零退出 2 个 | ✅ **非零退出 0 个** | — |
|
|
241
|
-
|
|
242
|
-
### 11.2 缺陷 1(已修 · 严重):测试「重跑 ≡ 首跑」的前提是脆的,且失败不可见
|
|
243
|
-
|
|
244
|
-
**现象**:`test_p0` 输出仅 443 字节:
|
|
245
|
-
```
|
|
246
|
-
[风险1] 分桶键退化 …
|
|
247
|
-
[safe-delete][SAFE_DELETE_BULK_CONFIRM_REQUIRED] {"count":722,"threshold":500,"scope":"turn", …}
|
|
248
|
-
```
|
|
249
|
-
**根因链(闭合)**:`test_p0.py:68` → `corpus.reset_root(CONC_ROOT)` → `shutil.rmtree(722 文件)`
|
|
250
|
-
→ 环境 shim(`sitecustomize._safe_remove`)逐个接管删除 → 累计 722 > 阈值 500 → 拒绝并中断进程。
|
|
251
|
-
|
|
252
|
-
**铁证**:`_md_cg_p0_conc` 实测 **722 文件**,与报错的 `"count":722` 完全吻合;
|
|
253
|
-
`_md_cg_p0` 实测 **0 文件**——说明第一个 `reset_root(ROOT)` 已清空+重建成功,死在第二个;
|
|
254
|
-
`_md_cg_p1` 实测 **350 文件**(< 500)故通过——**只差 150 就到阈值**,这正是历史上「P1 EXIT 1 / 165 字节」的同源形态。
|
|
255
|
-
|
|
256
|
-
**关键点:文档早已裁决过**。`docs/mdcg/认知图_MD目录方案_v0.1.md:365-367` 原文:
|
|
257
|
-
> 幂等性:迁移按 node id 原子覆盖写,可反复执行,**不需要先清空目录**。(这不只是便利:清空目录依赖 `rmtree`,
|
|
258
|
-
> 在有安全策略的环境下会被拦截,而"必须先删除才能重跑"的迁移脚本本身就是个脆弱设计。)
|
|
259
|
-
|
|
260
|
-
而同一份文档 `:350` 又写「测试前 `corpus.reset_root(root)` 清空」,`corpus.py:84-88` 实现的正是清空版——
|
|
261
|
-
**实现踩中了文档自己警告的坑,且文档内部自相矛盾**。
|
|
262
|
-
|
|
263
|
-
**修法**(按文档已裁决的设计修实现,非新发明):`reset_root` 默认**不删目录**,只保证存在;
|
|
264
|
-
重跑不变性改由两个可观测事实守住——① 写入侧同 id 原子覆盖(`seed()` 固定 id);
|
|
265
|
-
② **等号**断言(`test_p0`「索引节点数 == 语料期望数」由无害升级为守门者;
|
|
266
|
-
`test_p1` 新增「knowledge 层 == 336 + 5」守门断言,实测通过)。
|
|
267
|
-
|
|
268
|
-
### 11.3 缺陷 2(已修):两个用例因 stdout 编码**从未真正跑过**
|
|
269
|
-
|
|
270
|
-
`test_p11_consistency.py` / `test_p12_metacognition.py` 缺 `sys.stdout.reconfigure(encoding="utf-8")`。
|
|
271
|
-
Windows 默认 gbk 控制台下打印「d²D/dt²」的 `²`(U+00B2) 直接 `UnicodeEncodeError`,
|
|
272
|
-
**崩在第一条断言之前**,输出只剩 725 字节。仓库其实已有该约定(`test_p0.py:246-249` 的注释甚至
|
|
273
|
-
预言了「只落盘 444 字节」这一失败形态),这两个用例只是漏抄了。**修后 P11 27/27、P12 32/32。**
|
|
274
|
-
|
|
275
|
-
### 11.4 缺陷 3/4(已修):读子进程输出未指定 encoding
|
|
276
|
-
|
|
277
|
-
- `md_cg/audit.py:167-169`(`_verify_code` 的「实测」路径):`text=True` 退回 locale(gbk) → 被测命令
|
|
278
|
-
只要输出非 gbk 字节,读取线程即抛 `UnicodeDecodeError` → `p.stdout/p.stderr` 可能为空 →
|
|
279
|
-
`:174` 的失败证据丢失,「实测失败」退化成**一句没有依据的 REJECT**。
|
|
280
|
-
- `md_cg/test_p22_theory.py:156-159`(CLI 子进程):同类隐患。
|
|
281
|
-
|
|
282
|
-
两处均补 `encoding="utf-8", errors="replace"`,对齐 `whitebox.py:93-97` 的正确写法。
|
|
283
|
-
|
|
284
|
-
### 11.5 环境事实(不修,但必须记录)
|
|
285
|
-
|
|
286
|
-
装探针抓现场(`subprocess.Popen.__init__` 打点)确认:**受管环境下 `os.remove` 每次都要 spawn 外部进程**
|
|
287
|
-
(`node.exe safe-delete-bulk-guard.cjs` → 判定 → `genie-trash/win32-x64.exe` → 移动;
|
|
288
|
-
其间还会经 `platform.machine()` → `platform.win32_ver()` → `subprocess.check_output('ver')` 再 spawn 一次)。
|
|
289
|
-
|
|
290
|
-
后果三条,均记入工程规范化的前提:
|
|
291
|
-
1. **删除 ≠ 真删**,而是移入回收站;批量删除有 500 阈值门。
|
|
292
|
-
2. `fsutil.ShardedLog.clear`(`fsutil.py:249`) 逐文件 `os.remove` 是**性能热点**(`rebuild_index` 每次都调)。
|
|
293
|
-
3. `test_p1` 输出尾部那条 `UnicodeDecodeError … _readerthread` 异常**来自环境 shim 自身**
|
|
294
|
-
(守护线程抛出,不影响退出码,但污染输出、会让 CI 误判)。**仓库侧无法修**,只能记录。
|
|
295
|
-
|
|
296
|
-
**已验证非仓库问题**:全仓 `platform.*` 使用数 = **0**,该 spawn 由环境 shim 发起。
|
|
297
|
-
|
|
298
|
-
---
|
|
299
|
-
|
|
300
|
-
## 十二、R1 取证纪录(已完成 · 2026-09-10)
|
|
301
|
-
|
|
302
|
-
### 12.1 交付物与改动清单
|
|
303
|
-
|
|
304
|
-
| 文件 | 改动 | 对应计划项 |
|
|
305
|
-
|---|---|---|
|
|
306
|
-
| `md_cg/codeindex.py` | `render()` 由「非 CCG 注释行」改为输出 **CCG 6 行**;`_walk_defs()` 按源码顺序并携带父级类名;新增 `EXTRACTORS` 后缀注册表(`.py`→AST 精确 / `.ts·.tsx·.js·.mjs·.cjs`→弱提取);`region_hash()` 收敛为**唯一实现**;`index_dir` 返回 `(items, errors, stats)`(含 `truncated`/`skipped_suffixes`);无提取器后缀**显式 `raise ValueError`** | 修 B、修 E(部分) |
|
|
307
|
-
| `md_cg/mcp_server.py` | `op=index_code` 透传 stats、落 `verification_basis`/`code_ref`(含 `lang`/`precise`/`hash`/`root`);新增 `_ref_call` 回读源区间 + `hash_match` 漂移检测;op 枚举与描述加入 `ref`,新增 `ref`/`root` 参数 | 修 C |
|
|
308
|
-
| `md_cg/tokens.py` | `ALL_OPS` 收录 `ref` | §七-11(防漏改) |
|
|
309
|
-
| `md_cg/test_p26_refindex.py` | 新增验收用例(47 断言,全绿) | §七 |
|
|
310
|
-
|
|
311
|
-
**未改动**:`mdcg.py`(`add` 的 `**extra` 已能承载 `code_ref`,无需改签名)、`nodefile.py`(CCG 判定逻辑本就正确,问题在渲染侧)。
|
|
312
|
-
|
|
313
|
-
### 12.2 关键缺陷与修法:所有代码节点「恒定 BLINDSPOT」
|
|
314
|
-
|
|
315
|
-
**现象**:改造前 `index_code` 索引出的代码节点**存得进、判不了、检索不到**——`judge_qualification` 第一条 `ccg_completeness` 即判 BLINDSPOT。
|
|
316
|
-
|
|
317
|
-
**根因(闭合)**:旧 `render()` 只产 `# path::name` / `# sig` 等**非 CCG 行**,不含「功能名 / 生效条件 / 子功能 / 执行 / 验证方式 / 不适用条件」5 要素中任何一项 → `nodefile.CCG_REQUIRED` 缺项 → `complete=False` → BLINDSPOT。
|
|
318
|
-
即:**渲染层不产 CCG,却要求判定层按 CCG 判**——两侧契约错配。
|
|
319
|
-
|
|
320
|
-
**修法**:`render()` 产出完整 CCG 6 行(5 要素 + `# 位置`),`verification_basis` 显式落 `frontmatter`(`.py`→`compiler`、弱提取→`other`),使节点在「CCG 完整 + 基底已声明」下判 **ACCEPT**。
|
|
321
|
-
|
|
322
|
-
**守门断言**(`test_p26` §4/§5):`ccg_completeness(render(item))["complete"] is True`;`op=read` 真实检索链路命中且 `state == "ACCEPT"`(Python 与 JS 各一例)。
|
|
323
|
-
|
|
324
|
-
### 12.3 `op=ref` 回读:索引侧与回读侧**共用同一哈希实现**
|
|
325
|
-
|
|
326
|
-
- `codeindex.region_hash(lines, lineno, end)` 为唯一实现;索引侧写入 `code_ref.hash`,回读侧 `_ref_call` 重算并比对 → `hash_match`。
|
|
327
|
-
- **实测**:未改动 → `hash_match=True`/`stale=False`;改源(保持行数不变只换实现)→ `hash_match=False`/`stale=True`;**重跑 `index_code` → 漂移消除**(可恢复)。
|
|
328
|
-
- 边界**均显式报错,不静默读错位置**:缺 `root` → `ref 未记录 root…`;非代码节点 → `该节点没有 code_ref…`;节点不存在 → `节点不存在:…`。
|
|
329
|
-
- **只读契约**:索引一轮前后被索引源文件 **mtime 全不变**(§七-5)。
|
|
330
|
-
|
|
331
|
-
### 12.4 「不静默」三处落地(修 E 部分)
|
|
332
|
-
|
|
333
|
-
| 旧行为 | 新行为 | 断言 |
|
|
334
|
-
|---|---|---|
|
|
335
|
-
| `max_files` 触发时静默返回不完整结果 | `truncated=True` + `truncated_reason`(如 `files=1>=max_files=1`)+ `note` 警告 | §九 |
|
|
336
|
-
| 扫到无提取器后缀静默丢弃 | `stats.skipped_suffixes` 显式列出(实测 `['.txt']`) | §二 |
|
|
337
|
-
| 语法错误文件被吞 | 进 `errors`(实测 `bad.py: bad.py:1: invalid syntax`) | §十 |
|
|
338
|
-
|
|
339
|
-
### 12.5 §七 验收对照
|
|
340
|
-
|
|
341
|
-
| 验收项 | 状态 | 说明 |
|
|
342
|
-
|---|---|---|
|
|
343
|
-
| 1 ref 落盘(`code_ref` 字段完整) | ✅ | 9 字段(path/name/kind/lineno/end/lang/precise/hash/root) |
|
|
344
|
-
| 2 CCG 合规(`complete is True`、非 BLINDSPOT) | ✅ | 实测 ACCEPT |
|
|
345
|
-
| 3 回读逐字一致 | ✅ | 文本含 `def compute_energy` 与实现行 |
|
|
346
|
-
| 4 漂移检测 | ⚠️ 部分 | `hash_match` 漂移 ✅;`ref check` CLI 报 `stale`/`dangling` 属 **R3** |
|
|
347
|
-
| 5 只读契约(mtime 不变) | ✅ | — |
|
|
348
|
-
| 6 幂等(节点数不变、id 稳定) | ✅ | 12 == 12;`_refindex.json` 属 **R3** |
|
|
349
|
-
| 7 不复制正文 | ✅ | content 不含函数体语句 |
|
|
350
|
-
| 8 截断诚实 | ✅ | — |
|
|
351
|
-
| 9 文档切块 | ⏭️ | 属 **R2**(`docindex.py`) |
|
|
352
|
-
| 10 权限(`op=ref` 走 `tokens`) | ✅ | `ops_allow` 未含 `ref` 即被 `require_op` 拒 |
|
|
353
|
-
| 11 新 op 注册一致 | ✅ | `ALL_OPS` 含 `ref`/`index_code` |
|
|
354
|
-
|
|
355
|
-
### 12.6 回归
|
|
356
|
-
|
|
357
|
-
全量 **29 个用例模块**(R0 的 28 + 新增 `test_p26_refindex`)**非零退出 0 个**;`test_p26_refindex` **47/47** 全绿。
|
|
358
|
-
|
|
359
|
-
---
|
|
360
|
-
|
|
361
|
-
## 十三、R2 取证纪录(已完成 · 2026-09-10)
|
|
362
|
-
|
|
363
|
-
### 13.1 交付物与改动清单
|
|
364
|
-
|
|
365
|
-
| 文件 | 改动 | 对应计划项 |
|
|
366
|
-
|---|---|---|
|
|
367
|
-
| `md_cg/docindex.py` | **新增**:`.md` 章节切块(ATX 标题栈 + `heading_path` + 行号区间 + `anchor` + 首段摘要);`render()` 产 **CCG 7 行**(5 要素 + 验证方式 + 位置);`doc_ref` 11 字段;`_region_hash` 委托 `codeindex.region_hash`(**唯一实现**);`index_dir(root, patterns, ...)` 返回 `(items, errors, stats)`(含 `truncated`/`truncated_reason`/`skipped_suffixes`);无提取器后缀**显式 `raise ValueError`** | 修 A、修 C(文档侧) |
|
|
368
|
-
| `md_cg/mcp_server.py` | 新增 `op=index_doc` 分支(逐条 `cg.add(..., layer=, tags=["doc","doc:md","level:<n>"], verification_basis="data", sensitivity=, doc_ref=)`);`_ref_call` 的 `ref_kind` 扩为 `code_ref`/`doc_ref`;op 枚举与描述加入 `index_doc`;新增 `sensitivity` 参数 | 修 C |
|
|
369
|
-
| `md_cg/tokens.py` | `ALL_OPS` 收录 `index_doc`(`... "index_code","index_doc","ref","theory","link"`) | §七-11(防漏改) |
|
|
370
|
-
| `md_cg/test_p27_docindex.py` | **新增** 验收用例(**49 断言,全绿**) | §七 |
|
|
371
|
-
|
|
372
|
-
**未改动**:`mdcg.py`(`add` 的 `**extra` 已能承载 `doc_ref`,与 `code_ref` 同路径)、`nodefile.py`(CCG 判定逻辑本就正确,问题在渲染侧)、`mdcos.py`(检索侧无需改——文档节点与代码节点走同一判定路径)。
|
|
373
|
-
|
|
374
|
-
### 13.2 §七-9 文档切块:三条硬约束的落地与实测
|
|
375
|
-
|
|
376
|
-
md 不是代码,切块风险集中在「把不该当标题的当标题」。三处均实测守门:
|
|
377
|
-
|
|
378
|
-
| 硬约束 | 若不处理 | 处理与实测 |
|
|
379
|
-
|---|---|---|
|
|
380
|
-
| **围栏代码块内 `#` 非标题** | `docs/` 里大量 python/shell 片段带 `#` 注释 → 一节被切成假标题、行号错位 | `_headings()` 带围栏跟踪(``` / ~~~ 状态机)。实测:含假标题的样例仍只切出 `['9. 分阶段实施', '代码示例', '总览']`,**假标题零泄漏**,且父节区间**未被假标题截断** |
|
|
381
|
-
| **开头 YAML frontmatter 不当正文** | 切块起点落在 `---` 里,首节点区间从第 1 行开始 | `_body_start()` 跳过 frontmatter。实测:`总览` 节点 `lineno=6`(跳过 YAML) |
|
|
382
|
-
| **正文 `---` 不污染 frontmatter** | 文档正文分隔线被 `nodefile` 当 frontmatter 边界 → `doc_ref` 读不回 | 校验 `doc_ref` 字段可正常 `loads` 往返(`['总览','9. 分阶段实施']`),与 `nodefile.py:19-23` 既有纪律一致、不回归 |
|
|
383
|
-
|
|
384
|
-
**层级与合并**:只切 `level<=3`(实测条目层级 `[1,2,2]`);直接正文 <200 字且无子节的小节点**合并进父节摘要**(实测「表格与正文说明。」与围栏代码片段均并入父节,**不丢内容**),避免「一行小标题」也成节点(计划 §八 风险项 1)。
|
|
385
|
-
|
|
386
|
-
### 13.3 §1.3-3 裁定:层与密级(详见 §1.3-3 的落地)
|
|
387
|
-
|
|
388
|
-
- **layer = `knowledge`(默认)**:文档是可回读、可漂移检测的**参照知识**,与代码节点同层,保证进默认召回;`contextual` 意为情境绑定、会过期,用在此处会让文档掉出默认召回。实测 guide 节点 `layer=knowledge`。
|
|
389
|
-
- **sensitivity = `internal`(默认)+ 显式写入 frontmatter**:不依赖节点默认值(`mdcos` 读隔离取的是 `fm.sensitivity`;不显式写=靠默认兜底,审计看不出意图)。实测 guide 节点 frontmatter 密级显式为 `internal`。
|
|
390
|
-
- **私有路径段 → 保守降 `private`**:`PRIVATE_HINTS=("private","secret","internal","未公开","私有","内部")` 命中路径段即降级;**只可能更严,不更松**。实测 `guide/`(internal 3)+ `private/`(private 1)→ 分布 `{'internal':3,'private':1}`。
|
|
391
|
-
- **可显式覆盖**:`op=index_doc` 传 `sensitivity="public"` 可整批覆盖(实测 4 节点全 `public`),覆盖后 frontmatter 同步。
|
|
392
|
-
|
|
393
|
-
### 13.4 `doc_ref` 回读与漂移(与 `code_ref` 共用唯一哈希实现)
|
|
394
|
-
|
|
395
|
-
- `doc_ref` 落 **11 字段**:`path/heading/heading_path/level/lineno/end/anchor/hash/lang/precise/root`。
|
|
396
|
-
- `op=ref` 回读:索引侧与回读侧**共用 `codeindex.region_hash`**(`docindex._region_hash` 只是委托,杜绝两套实现漂移)。实测未改动 → `hash_match=True`;改文档一段 → `hash_match=False`(**stale 可检出**);**重跑 `index_doc` → 漂移消除**(派生物可重建)。
|
|
397
|
-
- **只读契约**:索引一轮前后被索引 `.md` 源文件不被触碰(测试断言通过)。
|
|
398
|
-
- **边界显式报错、不静默**:无提取器后缀 `raise ValueError`;`max_files` 触发时 `truncated=True` + `truncated_reason="files=1>=max_files=1"` + `note` 警告。
|
|
399
|
-
|
|
400
|
-
### 13.5 §七 验收对照
|
|
401
|
-
|
|
402
|
-
| 验收项 | 状态 | 说明 |
|
|
403
|
-
|---|---|---|
|
|
404
|
-
| 1 ref 落盘(`doc_ref` 字段完整) | ✅ | 11 字段完整、`loads` 往返不丢 |
|
|
405
|
-
| 2 CCG 合规(`complete is True`、非 BLINDSPOT) | ✅ | 文档节点实测 `ACCEPT` |
|
|
406
|
-
| 3 回读逐字一致 | ✅ | 回读文本 = 源章节(含标题与表格行) |
|
|
407
|
-
| 4 漂移检测 | ⚠️ 部分 | `hash_match` 漂移 ✅ + 重跑消除 ✅;`ref check` CLI/`_refindex.json` 报 `stale`/`dangling` 属 **R3** |
|
|
408
|
-
| 5 只读契约(mtime 不变) | ✅ | — |
|
|
409
|
-
| 6 幂等(节点数不变、id 稳定) | ✅ | `304 == 304`;id 集合稳定(按 id 原子覆盖,不清目录) |
|
|
410
|
-
| 7 不复制正文 | ✅ | 不逐字复制整节;「执行」栏摘要封顶 ≤200 字 |
|
|
411
|
-
| 8 截断诚实 | ✅ | — |
|
|
412
|
-
| **9 文档切块** | ✅ | 三硬约束全过(§13.2);§9 真实章节行号 `491 == 491` |
|
|
413
|
-
| 10 权限(`op=ref` 走 `tokens`) | ✅ | R1 已覆盖,文档节点同路径 |
|
|
414
|
-
| 11 新 op 注册一致 | ✅ | `ALL_OPS` 含 `index_doc`;工具 op 枚举 == `ALL_OPS`(`only-schema=[] only-ops=[]`) |
|
|
415
|
-
|
|
416
|
-
### 13.6 回归
|
|
417
|
-
|
|
418
|
-
全量 **30 个用例模块**(R1 的 29 + 新增 `test_p27_docindex`)**非零退出 0 个**;`test_p27_docindex` **49/49** 全绿。
|
|
419
|
-
|
|
420
|
-
### 13.7 诚实边界(R2 新增)
|
|
421
|
-
|
|
422
|
-
1. **计数口径**:计划 §六 R2 目标原写「`docs/` 30 份」——那是把 `docs/` 下非 md 也计入的早期估计;**实际 md 文件 19 份**,R2 实测 `files=19/19`(无静默跳过)。后续阶段一律以「按后缀注册表实际匹配数」为准,不再用估计值。
|
|
423
|
-
2. **文档索引仍是确定性的位置索引**,不含语义:能回答「这段写在哪个文档哪一节」,不能回答「这段文档说的是哪段代码」(后者属 R4 互链,且仍不做语义蕴含)。
|
|
424
|
-
3. **`doc_ref` 不承诺永久有效**,只承诺「可检出」(`hash` 变更探测)。
|
|
425
|
-
|
|
426
|
-
---
|
|
427
|
-
|
|
428
|
-
## 十四、R3 取证纪录(已完成 · 2026-09-10)
|
|
429
|
-
|
|
430
|
-
### 14.1 交付物与改动清单
|
|
431
|
-
|
|
432
|
-
| 文件 | 改动 | 对应计划项 |
|
|
433
|
-
|---|---|---|
|
|
434
|
-
| `md_cg/refindex.py` | **新增**:统一 ref 协议(`REF_KEYS` / `registry()` / `kind_of_path()` / `extract()`,无提取器后缀**显式 `raise`**);`Ledger` 索引水位(`_refindex.json`,**键 = 源文件绝对路径**,逐文件记 `(size, mtime)` + 节点区间 + **源大域 `root`**);`index_dir()` 统一调度(`fresh`/`on_file` 钩子 + 增量跳过 + 截断留痕 + `prune`/`reconcile` 对账);`probe_ref()`/`read_ref()` 回读;`check_refs()` 漂移·悬空巡检;`rebuild()` 修复动作 | 修 D、修 F |
|
|
435
|
-
| `md_cg/codeindex.py` / `md_cg/docindex.py` | `index_dir` 加 `fresh`/`on_file` 钩子与 `skipped_unchanged` 计数(默认 `None` → 与改造前**逐字一致**) | 修 F |
|
|
436
|
-
| `md_cg/mcp_server.py` | `op=index_code`/`index_doc` 改走 `refindex.index_dir` + `refindex.add_items`(**落盘唯一实现**);新增 `incremental`/`max_items` 参数(传 `sensitivity` 时自动退回全量);`_ref_call` 重写为 `action=read\|check\|stat`,**未知 action `raise`**;`read` 与检索结果带 `ref`/`ref_kind` | 修 D、修 F |
|
|
437
|
-
| `md_cg/sustain.py` | `diagnose` 增 `ref_stale`/`ref_dangling`/`ref_check_truncated` 巡检项与 `ref_checked`/`ref_stale`/`ref_dangling` stats;`heal` 增 `rebuild_refs` 动作(派生物,符合既有自愈边界) | 修 D 接入 |
|
|
438
|
-
| `md_cg/test_p28_refcheck.py` | **新增** 验收用例(**45 断言,全绿**) | §七 |
|
|
439
|
-
|
|
440
|
-
### 14.2 三个真实缺陷(首轮实测 39 通过 / 6 失败 → 修后 45/0)
|
|
441
|
-
|
|
442
|
-
首轮失败不是断言太严,而是**实现确实错**,逐条留证:
|
|
443
|
-
|
|
444
|
-
1. **巡检把一切都误判成 `dangling`(严重)**:`Ledger.record()` 没记源大域,`check_refs` 拿 `ledger.root`(= 认知图根)去拼 `root/rel`;而源在别处(如临时目录 / 任意被索引的大域),于是**文件永远找不到**。实测:改源一行后 `stale=[]`、`dangling=[alpha.py,beta.py,...]`,`sustain.diagnose` 因此报 `ref_dangling` 而非 `ref_stale`。
|
|
445
|
-
→ 逐文件记 `root`,巡检用**每个源文件自己的 root**;且键改绝对路径。
|
|
446
|
-
2. **水位不会遗忘死根**:键若按 `rel`(或虽按绝对路径但从不清理),源大域整个搬迁/删除后条目永久残留。因 `node_id` 与绝对路径无关,`nodes` 过滤挡不住这些幽灵条目,巡检会报**永不消失的 dangling**(`heal` 也治不好,因为条目不在任何会被重建的 root 下)。实测:连跑两次用例 → 水位 `files=4`(含上一轮已删临时目录的 2 条),`重跑后 dangling 消除` 失败。
|
|
447
|
-
→ 新增 `prune()`(剪掉源大域已不存在的条目,真悬空由节点自带 ref 兜底)+ `reconcile(root, kind, seen)`(一次**完整**索引后,本 root 下没扫到的旧条目剪掉)。**截断的索引不对账**——没扫完 ≠ 剩下的都消失了。
|
|
448
|
-
3. **落盘字节抖动**:`updated_at`/`ts` 用 `time.time()` 全精度浮点,`float` 最短 repr 的小数位长度会变 → 重跑 3 次体积 `1156 → 1157`,「不膨胀」判据假失败。→ 时间戳压到 **1 位小数**(`_now()`),体积稳定(实测 `1504 → 1504`)。
|
|
449
|
-
|
|
450
|
-
### 14.3 增量(修 F):未变文件不重切
|
|
451
|
-
|
|
452
|
-
- `_refindex.json` 按 `(size, mtime)` 双等判「未变」,`incremental=True` 时**不读盘**直接跳过,计入 `skipped_unchanged`。
|
|
453
|
-
- 实测:首轮全量 `indexed=6 / files=2`;第二轮 `incremental=true` → `skipped_unchanged=2 / indexed=0`,**节点数不变(幂等)**;不传 `incremental` 时 `skipped_unchanged=0`(默认仍全量,保证不漏召回);两轮 `id` 集合稳定。
|
|
454
|
-
- 水位键 = **源文件绝对路径**:一份认知图可索引多个大域,只按 `rel` 记会在 `alpha.py` 这类同名文件上互相覆盖(静默丢水位)。
|
|
455
|
-
|
|
456
|
-
### 14.4 漂移 / 悬空(修 D):唯一判定实现
|
|
457
|
-
|
|
458
|
-
- 回读与巡检**共用 `probe_ref` / `codeindex.region_hash`**,杜绝「回读说没漂、巡检说有漂」。`stale` 判据是**内容哈希**,不是 mtime——只 `touch` 不改内容,报 `ok` 才对(段⑧ 首版用例正是写入了与索引**相同**的正文,属用例缺陷,已改为第二个真实漂移变体)。
|
|
459
|
-
- 实测:改源一行(`lineno 5..7` 区间)→ `check` `ok=false`、`stale=[alpha.py ×2]`;重跑 `index_code` → `stale=[]`;删 `beta.py` → `dangling=[beta.py ×2]`;恢复源并重建 → `dangling=[]`。
|
|
460
|
-
- **只读契约**:巡检前后被索引源文件 `mtime` 不变;`heal` 也不修改源文件(源里仍是改后的正文)。
|
|
461
|
-
- **快路径**:ledger 判「未变」的文件不读盘直接判 ok;变了的才读一次按记录区间重算哈希。ledger 覆盖不到的节点再回退逐节点探测(`only_tagged=True` 只扫带 `code`/`doc` 标签者)。
|
|
462
|
-
|
|
463
|
-
### 14.5 截断不静默 + 接入 sustain
|
|
464
|
-
|
|
465
|
-
- `max_files=1` → `truncated=true`、`truncated_reason="files=1>=max_files=1"`、`note` 带 ⚠;并写进水位 `last_index.truncated=true`,重跑全量后回 `false`(`action=stat` 可见)。
|
|
466
|
-
- `sustain.diagnose`:改源后 `issues` 含 `ref_stale`、`stats.ref_stale=2` 且带 `ref_checked`;`heal` 动作含 `rebuild_refs`(实测 `['rebuild_refs','flush_index']`),heal 后 `ref_stale` 消除、源文件未被修改。
|
|
467
|
-
- 巡检超过 `max_nodes` 时报 `ref_check_truncated`(覆盖缺口显式说出,而非静默截断)。
|
|
468
|
-
|
|
469
|
-
### 14.6 §七 验收对照
|
|
470
|
-
|
|
471
|
-
| 验收项 | 状态 | 说明 |
|
|
472
|
-
|---|---|---|
|
|
473
|
-
| 1 ref 落盘字段完整 | ✅ | `code_ref` 9 字段 / `doc_ref` 11 字段,注册表覆盖两类 |
|
|
474
|
-
| 2 CCG 合规(非 BLINDSPOT) | ✅ | 由 `index_code`/`index_doc` 产出的节点实测 `ACCEPT`(R1/R2 已覆盖) |
|
|
475
|
-
| 3 回读逐字一致 | ✅ | 回读文本 = 源区间,`hash_match=true` |
|
|
476
|
-
| **4 漂移检测** | ✅ | `ref check` 报 `stale`;重建后消除;纯 mtime 变更**不**误报(§14.4) |
|
|
477
|
-
| **5 悬空检测** | ✅ | 删源报 `dangling`;恢复重建后消除;死根条目被 `prune` |
|
|
478
|
-
| 6 只读契约(mtime 不变) | ✅ | 巡检与 heal 均不改源文件 |
|
|
479
|
-
| 7 幂等(节点数不变、id 稳定) | ✅ | 增量/全量重跑节点数与 id 集合均稳定 |
|
|
480
|
-
| **8 截断诚实** | ✅ | `truncated` + `truncated_reason` + 写进水位 `last_index` |
|
|
481
|
-
| 9 增量不重切 | ✅ | `skipped_unchanged=2 / indexed=0` |
|
|
482
|
-
| 10 权限(`op=ref` 走 `tokens`) | ✅ | `ALL_OPS` 仍收录 `ref`/`index_code`;未知 action 不静默成功 |
|
|
483
|
-
| 11 派生物不膨胀 | ✅ | `_refindex.json` `< 20KB`;重跑 3 次字节数不变;`files` 无幽灵条目 |
|
|
484
|
-
|
|
485
|
-
### 14.7 回归
|
|
486
|
-
|
|
487
|
-
全量 **31 个用例模块**(R2 的 30 + 新增 `test_p28_refcheck`)**非零退出 0 个**;`test_p28_refcheck` **45/45** 全绿;`test_p26_refindex` 47/47、`test_p27_docindex` 49/49 无回归;`refindex.py` 改动文件 lint 0 错误。
|
|
488
|
-
|
|
489
|
-
### 14.8 诚实边界(R3 新增)
|
|
490
|
-
|
|
491
|
-
1. **`stale` 只承诺「可检出」,不承诺「自动定位到改了哪一行」**:报的是区间哈希不匹配 + 原区间坐标,不做行级 diff。
|
|
492
|
-
2. **水位是派生物缓存,可随时删除**:`prune`/`reconcile` 让它有界,但语义上它只是提速;删掉后巡检退回逐节点探测,结论不变。
|
|
493
|
-
3. **多根同名文件**:键为绝对路径,同名文件分属不同大域**互不覆盖**;但同一文件被两个大域同时索引时,`nodes` 会指向同一组 id,最后写入者胜(与索引自身语义一致)。
|
|
494
|
-
4. **`max_files` 截断时不对账**(§14.2-2),因此截断轮次后水位会短暂保留未扫文件的旧条目——这是**保守**选择:宁可多留,不可误删。
|
|
495
|
-
|
|
496
|
-
---
|
|
497
|
-
|
|
498
|
-
## 十五、R3.1 / R3.2 取证纪录(已完成 · 2026-09-12):节点级对账 + 索引追加排除
|
|
499
|
-
|
|
500
|
-
> **为什么单开一节而不并进 R3**:R3 只做了**水位层**对账(`Ledger.reconcile`/`prune` 剪的是 `_refindex.json` 的水位条目),
|
|
501
|
-
> **节点层**两类残留无人处置——`node_id = sha1(相对路径 + "#" + heading_path)`,文档标题一变整篇 id 全量重算,
|
|
502
|
-
> 于是同 path 的旧代节点与新代节点并存(一份文档召回两份);源文件一删,节点永久 `dangling`。
|
|
503
|
-
> §十四 验收项 5 的原话是「删源报 `dangling`」——**悬空当时只承诺「可检出」,本节补上「可处置」**。
|
|
504
|
-
|
|
505
|
-
### 15.1 交付物与改动清单
|
|
506
|
-
|
|
507
|
-
| 文件 | 动作 | 职责 |
|
|
508
|
-
|---|---|---|
|
|
509
|
-
| `md_cg/refindex.py` | **扩** | 节点级对账节:`_norm_root`/`_same_root`/`_norm_rel`(大域与路径归一)+ `_forget_many`(逐条软删、受保护拦下、缺 `forget` 能力显式报错)+ `prune_orphans`(清退同 root+path 的过期代)+ `prune_dangling`(清退悬空) |
|
|
510
|
-
| `md_cg/mcp_server.py` | **扩** | `op=ref` 增 `action=prune`/`prune_dangling`;`index_code`/`index_doc` 返回前接 `_prune_after_index`(读 `prune` 默认 True、`prune_dry_run` 预演);schema 增 `prune`/`prune_dry_run`/`roots` |
|
|
511
|
-
| `md_cg/test_p27_docindex.py` | **扩** | 新增【10】节点级对账、【10b】悬空清退;`cg` 由 `MdCG` 改 `MdCGOS`(对齐生产路径) |
|
|
512
|
-
|
|
513
|
-
### 15.2 两个真实缺陷(首轮实测暴露,逐条留证)
|
|
514
|
-
|
|
515
|
-
1. **对账静默假成功(严重)**:`cg.index['nodes']` 是 `mdcg._scan_nodes()` 产出的**元数据快照**(path/layer/tags/importance/edges…),**不含 `ref`**——`ref` 只写在节点 frontmatter 里。首版直接对索引条目调 `ref_of()`,**恒返回 `('', None)`**,于是「清退了 0 条」却报 `ok=True`。实测 `prune_orphans count=0`、`prune_dangling candidates=0`。
|
|
516
|
-
→ 改为 `node = cg.get(nid)` 取回节点再 `ref_of(node)`,并加 `doc`/`code` 标签预筛(与 `check_refs` 同口径,免得为全库每条记忆都读一次盘)。
|
|
517
|
-
2. **`forget` 能力分层被忽略**:`forget` 属 **`MdCGOS` 层**(保护裁决 + 回收站 + 删除清单),基础层 `MdCG` 没有任何删除原语。首版测试用 `MdCG(ROOT)`,实测 `'MdCG' object has no attribute 'forget'`。
|
|
518
|
-
→ ①`_forget_many` 缺能力时**显式返回错误、不静默跳过**(文案点名「生产路径是 MdCGOS,测试请用 MdCGOS」);②测试改 `MdCGOS` 构造。
|
|
519
|
-
→ 与 P43 的教训同构:**双实现类(`MdCG`/`MdCGOS`)必须双路覆盖**,只测一条路径等于留静默盲区。
|
|
520
|
-
|
|
521
|
-
### 15.3 三条保守约束(宁可不删,不可误删)
|
|
522
|
-
|
|
523
|
-
- **只清「本次真正重切过」的文件**:`prune_orphans` 的判定范围限于 `items` 里的 path;增量索引跳过的未变文件不进判定——否则会把完好节点整片误删。
|
|
524
|
-
- **截断轮次不清退**:没扫完 ≠ 剩下的都过期。`_prune_after_index` 见 `truncated=True` 直接返回,不算、不删。
|
|
525
|
-
- **探测失败不算悬空**:`probe_ref` 抛异常即跳过,不把「读不到」当成「源已删」。
|
|
526
|
-
|
|
527
|
-
### 15.4 真实现场处置(`AEIS/data/mdcg`)
|
|
528
|
-
|
|
529
|
-
常驻 MCP 进程加载的仍是旧代码(`op=ref` 只认 `read|check|stat`),故按**工作纪律第7条**走等价兜底:python 直调同一模块(身份从 CodeBuddy `mcp.json` 读取,不落盘、不回显令牌)。
|
|
530
|
-
|
|
531
|
-
| 步骤 | 命令 | 实测 |
|
|
532
|
-
|---|---|---|
|
|
533
|
-
| 巡检 | `op=ref action=check max_nodes=20000` | `checked=5456`、`dangling=20`、`stale=42`、`truncated=false` |
|
|
534
|
-
| 预演 | `action=prune_dangling dry_run=true` | `scanned=10136`、`candidates=20`、`count=20` |
|
|
535
|
-
| 清退 | `action=prune_dangling`(designer 身份,`can_admin=true`) | `count=20`,全部软删进 `trash/` |
|
|
536
|
-
| 复核 | 同 `dry_run` 再跑 + 落盘核对 | `candidates=0`;20 个 `node_id` **`LIVE_HITS=0` / `TRASH_HITS=20`**;`trash/` 与 `_deletions.jsonl` 均 36 条 |
|
|
537
|
-
|
|
538
|
-
悬空来源单一:4 个已删的临时脚本(`_tmp_v3check.py`×2、`cmp_rows.py`×4、`dump_rows.py`×2、`vision.py`×12),非业务记忆,清退无信息损失且可 `op=restore` 回滚。
|
|
539
|
-
|
|
540
|
-
### 15.5 诚实边界(R3.1 新增)
|
|
541
|
-
|
|
542
|
-
1. **对账是 O(带标签节点数) 次读盘**:`ref` 不在索引快照里,只能逐节点 `cg.get()`。每次 `index_code`/`index_doc` 都会带上这一遍开销(本仓 ~500 节点级、现场 10k 节点级;后者秒级但非零成本)。若成热点,正解是把 `ref` 摘要进 `_scan_nodes` 快照,而非放松判定。
|
|
543
|
-
2. **只处置「孤儿」与「悬空」,不处置 `stale`**:漂移(源已改)用重建解决(重跑索引或 `sustain.heal`),不靠清退——删掉再索引等于绕远路。
|
|
544
|
-
3. **`stale` 与「孤儿」判据不同**:孤儿靠 `node_id` 重算比对(结构性),不看内容哈希;内容已变但 id 未变的节点不属孤儿。
|
|
545
|
-
4. **本仓 `test_p27` §9 的计数断言曾在本机为红**(`files=500/2388`,唯一失败项,非 R3.1 改动引起):`.gitignore` 已整目录忽略的 `docs/experiments/`(实测 2358 个 md 于 `docs/experiments/md-cg-gate-probe/`)物理仍在盘上,把 `docs/` 从 30 个 md 撑到 2388,越过 `max_files=500`。**处置口径已裁定并落地 → 见 §15.7(R3.2)**:给索引器加**追加排除**,而非调大上限。裁后 §9 由 `500/2388` 转为 `30/30` 全绿。
|
|
546
|
-
|
|
547
|
-
### 15.6 回归(R3.1)
|
|
548
|
-
|
|
549
|
-
受影响面 7 模块:`test_p9_forget_protect` 37/0、`test_p14_sustain` 44/0、`test_p21_tokens` 31/0、`test_p26_refindex` 55/0、`test_p28_refcheck` 45/0、`test_p2_mcp` 63/0 **全绿**;`test_p27_docindex` **68/1**(当时唯一红项为 §15.5-4 的环境事实,非代码缺陷)。
|
|
550
|
-
|
|
551
|
-
### 15.7 R3.2:`skip_dirs` 追加排除(原 §15.5-4 的裁定落地)
|
|
552
|
-
|
|
553
|
-
**问题**:索引的「扫描范围」只有**下限**(`SKIP_DIRS` 硬编码)没有**上限**——`.gitignore` 已整目录忽略的实验产物物理仍留在盘上,就照样被扫。本仓 `docs/` 真实内容 30 个 md,被 `docs/experiments/` 的 2358 个撑到 2388,直接压爆 `max_files=500`。此前唯一的应对是「调大 `max_files`」,那是**掩盖**:上限调到 2500,下次再多一批产物同样爆。
|
|
554
|
-
|
|
555
|
-
**交付物**:
|
|
556
|
-
|
|
557
|
-
| 文件 | 动作 | 职责 |
|
|
558
|
-
|---|---|---|
|
|
559
|
-
| `md_cg/codeindex.py` | **扩** | `skip_matcher(skip_dirs)` → `(hit, rules)`;`index_dir(..., skip_dirs=None)` 在 `os.walk` 处**追加**剪枝;`stats` 增 `skip_dirs`(规则回显)/ `skipped_dirs`(实际排掉的目录) |
|
|
560
|
-
| `md_cg/docindex.py` | **扩** | 同签名同语义,复用 `codeindex.skip_matcher`(**唯一实现**,避免两条链路口径漂移) |
|
|
561
|
-
| `md_cg/refindex.py` | **扩** | `index_dir(..., skip_dirs=None)` 透传给底层 |
|
|
562
|
-
| `md_cg/mcp_server.py` | **扩** | `index_code`/`index_doc` 透传 `skip_dirs`;`_skip_dirs_report()` 把「排掉了什么」压成 `skipped_dirs` / `skipped_dirs_count` / `skip_dirs` 回报;schema 增 `skip_dirs` |
|
|
563
|
-
| `md_cg/test_p27_docindex.py` | **扩** | §9 改用 `skip_dirs=["experiments"]`;新增 §9b 专测匹配口径 / 只增不减 / 可审计 |
|
|
564
|
-
|
|
565
|
-
**三条设计裁定**:
|
|
566
|
-
|
|
567
|
-
1. **只增不减**:`skip_dirs` 在 `SKIP_DIRS` **同一处**生效,调用方只能追加,**不能关掉** `.git`/`.venv`/`node_modules` 这类内置保护——否则一次参数写错就能把版本库元数据索引进认知图。
|
|
568
|
-
2. **排除不许静默**(与「截断不许静默」同一纪律):命中目录写进 `stats["skipped_dirs"]`(就地追加、不依赖末尾汇总,所以**截断提前 return 时也带得走**),MCP 层再压成可读回报。调用方据此能把「源文件真的少了」与「被规则排掉了」区分开。
|
|
569
|
-
3. **两类匹配口径可混用**:含 `/` 按**相对 root 的路径**(`docs/experiments` 只排这一处);不含 `/` 按**目录名**(`experiments` 排任意层级同名目录)。反斜杠 / 首尾斜杠先归一——否则「规则传了却不生效」又是一次静默失配。默认 `None` 时 `hit is None`、走原路径,**行为与改造前逐字一致**(同 `fresh`/`on_file` 纪律)。
|
|
570
|
-
|
|
571
|
-
**实测**(`test_p27` §9 / §9b):
|
|
572
|
-
- 本仓 `docs/`:`files=30/30`、`truncated=false`,`skipped_dirs=['experiments']`(此前 `files=500/2388` 已截断)。
|
|
573
|
-
- 合成树(`keep.md` + 两层 `experiments/*` + `sub/experiments/y.md` + `node_modules/pkg/n.md`):不给规则 → `files=4`「内置排除照旧」;`skip_dirs=["experiments"]` → `files=1` 且 `skipped=['docs/experiments','sub/experiments']`(**同时证明 `node_modules` 仍被排,即只增不减**);`skip_dirs=["docs/experiments"]` → `files=2` 只排这一处;`skip_dirs=["docs\\experiments\\"]` → `files=2`(归一后仍生效)。
|
|
574
|
-
|
|
575
|
-
### 15.8 诚实边界(R3.2 新增)
|
|
576
|
-
|
|
577
|
-
1. **按路径的规则不跨 root**:`docs/experiments` 是「相对本次 root」的路径,换个 root 就不再匹配;跨仓复用规则要用不含 `/` 的目录名形式。
|
|
578
|
-
2. **同名目录一网打尽**:`skip_dirs=["experiments"]` 会排掉**任意层级**的同名目录。这正是它对 `2_ai` 那种多子项目树好用的原因,但也意味着「只想排一处」必须写全路径。
|
|
579
|
-
3. **排除只影响扫描,不清理既有节点**:已在库、但本次被排掉的目录里的节点**不会**因此消失(`prune_orphans` 的判定范围限于本次真正重切过的 path,见 §15.3-1)。若要清掉,需先不带 `skip_dirs` 跑一次让它们成为孤儿,或直接 `prune_dangling`。
|
|
580
|
-
4. **`max_files` 仍是安全上限而非目标值**:`skip_dirs` 解决「噪声挤占额度」,不解决「真实内容超过 500」——真超了应当**调大 `max_files`**(那是真实内容),而不是继续加排除。
|
|
581
|
-
|
|
582
|
-
### 15.9 回归(R3.2)
|
|
583
|
-
|
|
584
|
-
受影响面 10 模块**全绿**:`test_p2_mcp` 63/0、`test_p9_forget_protect` 37/0、`test_p14_sustain` 44/0、`test_p21_tokens` 31/0、`test_p26_refindex` 55/0、`test_p27_docindex` **75/0**、`test_p28_refcheck` 45/0、`test_p29_session_ingest_export` 48/0、`test_p30_maintain` 55/0、`test_p32_backfill` 58/0。改动文件 lint 0 错误。
|
|
585
|
-
|
|
586
|
-
---
|
|
587
|
-
|
|
588
|
-
*计划生成:2026-09-10 · 证据:§一 全部为源码行级核对结果 · 未核对项已单列 §1.3 / §十 · §十一 为 R0 实测取证 · §十二 为 R1 实测取证 · §十三 为 R2 实测取证 · §十四 为 R3 实测取证 · §十五 为 R3.1 / R3.2 实测取证*
|
|
1
|
+
# 认知图 · 索引与工程规范化 计划 v0.1
|
|
2
|
+
|
|
3
|
+
> 来源:`agi-architectures/外部agi架构可学习优点.md` 复核表 **#12 文档体系** 与 **#8 manifest 元数据规范**
|
|
4
|
+
> (两条均判 DEFER,先决条件分别是「先说清缺哪一份索引」「确认三字段是否真缺」——本文档即对该先决的正面回答)
|
|
5
|
+
> 目标工程:**dsh-memory / md_cg(认知图 + 记忆 OS)**
|
|
6
|
+
> 证据口径:源码级逐行核对(`mcp_server.py` / `codeindex.py` / `mdcg.py` / `nodefile.py` / `sources.py` / `sustain.py`)
|
|
7
|
+
> 时间:2026-09-10
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## 判定单
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
条件空间:观测位置=全局(设计者) | 观测工具=md_cg 源码逐行核对 + docs/ 30 份 + 复核表 #12/#8
|
|
15
|
+
| 时间窗口=当前迭代 | 存在约束=零第三方依赖(纯标准库) / md 单一真相源 / 有意剔除神经嵌入 / 不复制实现正文
|
|
16
|
+
白箱三问:依据=每条给文件行级证据(§一) | 条件=先决已核对,未核对项显式标出 | 错后=索引若复制代码正文,
|
|
17
|
+
会造出「第二份真相」,破坏「md 可审计 + git 可 diff」这一存在理由,且代码改了索引不报错(静默漂移)
|
|
18
|
+
裁决:ACCEPT 6 / DEFER 2 / REJECT 2
|
|
19
|
+
依据:条件证据见 §一,非相似度
|
|
20
|
+
缺失维度:观测位置之外的「索引粒度」——文件级 / 符号级 / 段落级三者未定义,本计划定为「符号级(代码) + 章节级(文档)」
|
|
21
|
+
验证路径:R0 收口基线 → R1 ref 协议+回读 → R2 文档索引 → R3 增量+漂移巡检 → R4 互链 → R5 规范化产出
|
|
22
|
+
关注信号:`index_code` 全仓 0 测试;`index_dir` 静默截断;代码节点恒定被判 BLINDSPOT(§二)
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## 一、现状核对(事实层)
|
|
28
|
+
|
|
29
|
+
### 1.1 已具备(不要重复造)
|
|
30
|
+
|
|
31
|
+
| 能力 | 证据 |
|
|
32
|
+
|---|---|
|
|
33
|
+
| 条件代码图模块已存在,AST 抽注释/接口 | `md_cg/codeindex.py:1-13`(设计声明)、`:53-75`(`extract`)、`:78-88`(`render`)、`:91-95`(`node_id`)、`:98-118`(`index_dir`) |
|
|
34
|
+
| MCP 已接线 | `md_cg/mcp_server.py:1492-1516`(`cg(op=index_code)`),op 声明在 `:531-532`、`:545-546`、`:616-618` |
|
|
35
|
+
| ref 字段**真的落盘**(不是被 kwargs 吞掉) | `mcp_server.py:1509` 传 `code_ref={path,name,kind,lineno,end}` → `md_cg/mdcg.py:388`(`**extra`)→ `:452`(`fm.update(extra)`) |
|
|
36
|
+
| ref 字段**能解析回来** | `md_cg/nodefile.py:51`(值一律 `json.dumps`)→ `:73`(`json.loads`),嵌套 dict 保真 |
|
|
37
|
+
| ref 字段**能返回给调用方** | `mcp_server.py:730-734`(`_node_view` 返回整个 `frontmatter`) |
|
|
38
|
+
| 已有 op 注册表与权限校验 | `md_cg/tokens.py:56-59`(`ALL_OPS`,含 `index_code`)+ `test_p21_tokens.py` |
|
|
39
|
+
| 增量 watermark 机制有现成范式 | `md_cg/sources.py:219-253`(`_sources.json`:`{source_key:{t,seq,count}}`)、`:257-334`(`ingest` 增量+去重+幂等) |
|
|
40
|
+
| 自愈只修派生物、逐条留痕的纪律已定 | `md_cg/sustain.py:12-14`(诊断只读)、`:277-302`(`_audit` + 幂等修复 + 失败不拖垮进程) |
|
|
41
|
+
| 「非 CCG 正文」有两种既有豁免范式 | `mdcg.py:513-519`(目标节点**按 CCG 格式渲染**以避开 BLINDSPOT)、`mdcos.py:313-316`(`ccg_exempt` legacy 记忆 → DEFER) |
|
|
42
|
+
|
|
43
|
+
### 1.2 真实缺口(本计划要闭合的)
|
|
44
|
+
|
|
45
|
+
| # | 缺口 | 证据(现状) | 严重度 |
|
|
46
|
+
|---|---|---|---|
|
|
47
|
+
| **A** | **文档索引完全不存在**——认知图索引不到 `docs/*.md` 的任何位置 | 全仓无 `.md` 索引模块;`sources.py` 只有 `JsonlSource`(`:49`)/`DSHSessionSource`(`:112`),均吃会话流不吃文档 | 高(正是用户的核心诉求) |
|
|
48
|
+
| **B** | **代码节点恒定被判 BLINDSPOT** | `codeindex.render`(`codeindex.py:80-88`) 只输出 `# path::name (kind, L..)` / `# sig` / `# 注释` / `# doc:`,**不含 CCG 5 要素标记**;而 `judge_qualification` 第一步就是 `ccg_completeness` 不全 → `BLINDSPOT`(`mdcg.py:709-714`),且该判定在**检索路径上逐条调用**(`mdcos.py:746`、`:869`) | **最高(存得进、判不了、白索引)** |
|
|
49
|
+
| **C** | **只能索引,不能回读**——没有「按 ref 打开源文件正文」的 op | `mcp_server.py:1492-1516` 只写不读;无 ref 解析/定位入口 | 高 |
|
|
50
|
+
| **D** | **无漂移/悬空检测**——源码改了、行号漂了,索引不知道 | 无 ref 校验代码;`sustain.diagnose`(`sustain.py:141-266`) 的巡检项里无 ref | 高(静默故障,正是方案自己警惕的那类) |
|
|
51
|
+
| **E** | **只认 `.py`** | `codeindex.py:19` `SUFFIX = (".py",)`;本仓 `src/*.ts`、`test/*.ts`、`scripts/*.ps1`、`cordova/cordis*.yml` 全在索引外 | 中 |
|
|
52
|
+
| **F** | **全量重扫 + 静默截断** | `codeindex.py:107-108`:达到 `max_files`/`max_items` **直接 return**,调用方 `mcp_server.py:1513` 只回 `indexed/error_count`,不报 `truncated` → 索引不全但不告警 | 中(静默不全) |
|
|
53
|
+
| **G** | **零测试** | `index_code` 仅出现在 `tokens.py:59` 与 `mcp_server.py`(5 处);`test_*.py` 中 **0 命中** | 中 |
|
|
54
|
+
| **H** | **code 图与 doc 图不互链** | `links.py` 是蜂群信任(`links.py:1-20`),与代码/文档无关;代码节点 tags 仅 `["code","code:<kind>"]`(`mcp_server.py:1505`),无指向文档的边 | 中(「互链」是索引的价值上限) |
|
|
55
|
+
| **I** | **工程规范化的三份索引文件缺位** | `docs/` 30 份含方案/评分/工具表,但**无模块索引、无功能矩阵、无文件清单**;33 个 MCP 工具的 `inputSchema`(`mcp_server.py:96-690`) 无 timeout/执行语义;无跨进程可读的 `status.json` | 中(= 复核表 #12/#8 本体) |
|
|
56
|
+
|
|
57
|
+
### 1.3 已核对但**尚未核实**的点(诚实登记,不当作事实)
|
|
58
|
+
|
|
59
|
+
1. ~~`cg.add` 未显式设 `role`(`mcp_server.py:1503-1511`),代码/文档节点会落到哪个 role 档、是否进正排——**未验证**,R1 必须实测。~~
|
|
60
|
+
→ **已实测(R1)**:代码节点 frontmatter **不带 `role` 字段**(`index_code` 未传),但检索/资格判定不依赖 `role`——实测 `op=read` 能命中并按 CCG 完整度判 **ACCEPT**(`tags=["code","code:<kind>"]`、`verification_basis` 落盘)。故 `role` 缺口不影响 R1 目标,留待 R5(FEATURE_MATRIX)统一裁定标签体系。
|
|
61
|
+
2. `index_dir` 的 `condition_space.observation_position = path.split("/")[0]`(`mcp_server.py:1506-1507`) 生成的桶分布是否健康(对齐 `routing` 分区健康度自检)——**未验证**。
|
|
62
|
+
3. ~~文档节点的层与密级(`knowledge` vs `contextual`、`public` vs 继承目录)——**未裁定**,R2 前定。~~
|
|
63
|
+
→ **已裁定(R2)**:layer 默认 `knowledge`(与代码节点同层,保证进默认召回;`contextual` 表示情境绑定会过期,会让文档掉出默认召回);密级**默认 `internal` 且显式写入 frontmatter**(不依赖节点默认值——审计时看不出意图),路径段命中私有提示词时**再保守降为 `private`**(只可能更严,宁可漏召回不可泄漏)。实测 `internal:3 / private:1`。详见 §十三-3。
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## 二、为什么必须先修 B(避免把预算花在错误的地方)
|
|
68
|
+
|
|
69
|
+
当前 `index_code` 的产出**在检索路径上是残废的**:
|
|
70
|
+
|
|
71
|
+
```
|
|
72
|
+
judge_qualification(mdcg.py:709-714)
|
|
73
|
+
第 1 步:ccg_completeness(content) 不全 → 直接返回 BLINDSPOT
|
|
74
|
+
↑ codeindex.render 不产 CCG 标记(codeindex.py:80-88)
|
|
75
|
+
↑ 第 3 步「verification_basis 缺失 → DEFER」根本走不到,
|
|
76
|
+
尽管 mcp_server.py:1508 已正确填了 verification_basis="compiler"
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
同仓已有两个正确范式可抄:目标节点**按 CCG 格式渲染**(`mdcg.py:513-519`,注释直写「保证 judge_qualification 能给出 ACCEPT 而非 BLINDSPOT」)、legacy 记忆走 `ccg_exempt`(`mdcos.py:313-316`)。
|
|
80
|
+
|
|
81
|
+
**结论**:索引层的第一个 PR 不是「加更多后缀」,而是**让渲染产物符合 CCG 5 要素**。否则索引量涨 10 倍,可用性仍是 0。
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
## 三、目标(可验收的三句话)
|
|
86
|
+
|
|
87
|
+
1. **回得去**:任何一条被检索到的节点,都能给出行级可定位的位置——
|
|
88
|
+
代码 → `<path>:<lineno>-<end>`;文档 → `<path>#<heading-path>:<lineno>-<end>`。
|
|
89
|
+
2. **找得到**:`.py` / `.md` 全量可索引(`.ts` 为弱提取器),不漏召回,且索引结果能回答「这条功能在文档里怎么写的 / 对应哪段代码」。
|
|
90
|
+
3. **守得住**:索引是**派生物**——可删可重建;源文件变更后能被巡检发现(漂移/悬空),且修复不改源文件、逐条留痕。
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
## 四、设计:统一 ref 协议
|
|
95
|
+
|
|
96
|
+
沿用 `code_ref` 的形状,扩成 `ref` 协议,作为 frontmatter 一等字段(`nodefile` 已支持任意 JSON 值,无需改格式层)。
|
|
97
|
+
|
|
98
|
+
```jsonc
|
|
99
|
+
// 代码节点
|
|
100
|
+
"code_ref": {"path":"md_cg/mdcg.py","name":"MdCG.add","kind":"def",
|
|
101
|
+
"lineno":383,"end":462,"lang":"py","hash":"9f2c1ab3de44"}
|
|
102
|
+
|
|
103
|
+
// 文档节点
|
|
104
|
+
"doc_ref": {"path":"docs/mdcg/认知图_MD目录方案_v0.1.md","heading":"9. 分阶段实施(稳健)",
|
|
105
|
+
"heading_path":["9. 分阶段实施(稳健)"],"level":2,
|
|
106
|
+
"lineno":491,"end":513,"anchor":"9-分阶段实施稳健","hash":"41ab77c0e1d9"}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
**不变量(写进代码注释与测试)**
|
|
110
|
+
- 正文**永不复制实现/全文**——只存「索引块 + ref 指路」(对齐 `codeindex.py:4-6` 的既有纪律)。
|
|
111
|
+
- `hash` = 被引用行的 sha1 前 12 位,用于漂移判定(不是内容寻址,是变更探测)。
|
|
112
|
+
- 索引是派生物:`_refindex.json` 丢了/坏了,能从源文件全量重建。
|
|
113
|
+
- 源文件**只读**:索引一轮后源文件 mtime 全不变(对齐 P0 的 mtime 只读断言)。
|
|
114
|
+
|
|
115
|
+
**CCG 渲染模板(修 B 的关键)**
|
|
116
|
+
|
|
117
|
+
代码节点(`功能名/子功能/执行/验证方式/不适用条件` 五个必需标记齐 → `complete=True`):
|
|
118
|
+
|
|
119
|
+
```
|
|
120
|
+
# 功能名:{name}({kind})
|
|
121
|
+
# 生效条件:大域={top_dir};检索「{name}」或「{path}」
|
|
122
|
+
# 子功能:{所属模块/类} · {docstring 首段≤120字}
|
|
123
|
+
# 执行:{sig}
|
|
124
|
+
# 验证方式:compiler(AST 已解析,非人工声明)
|
|
125
|
+
# 不适用条件:其它大域的同名符号
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
文档节点:
|
|
129
|
+
|
|
130
|
+
```
|
|
131
|
+
# 功能名:{heading}
|
|
132
|
+
# 生效条件:文档={rel_path};检索「{heading}」或正文关键词
|
|
133
|
+
# 子功能:{父 heading 路径}
|
|
134
|
+
# 执行:{首段摘要≤200字}
|
|
135
|
+
# 验证方式:data(原始文档为准)
|
|
136
|
+
# 不适用条件:其它文档的同名标题
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
> 取舍:docstring/首段进 `子功能` 行而不另起 `# doc:` 行,是为了让**单条节点的全部可检索信息都在 CCG 结构内**,避免出现「检索能命中、判定看不见」的字段。
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
## 五、模块改动表
|
|
144
|
+
|
|
145
|
+
| 文件 | 动作 | 职责 |
|
|
146
|
+
|---|---|---|
|
|
147
|
+
| `md_cg/refindex.py` | **新增** | ref 协议常量 + 提取器注册表 + 统一 `index_dir` 调度 + `_refindex.json` 增量 + `check_refs()` 漂移巡检 + `read_ref()` 回读 + **节点级对账** `prune_orphans()`(过期代)/ `prune_dangling()`(悬空)(R3.1)+ `skip_dirs` 透传(R3.2) |
|
|
148
|
+
| `md_cg/docindex.py` | **新增** | `.md` 章节切块:ATX 标题栈、heading_path、行号区间、首段摘要、anchor;`skip_dirs` 追加排除(R3.2,复用 `codeindex.skip_matcher`) |
|
|
149
|
+
| `md_cg/codeindex.py` | **改造** | ①`render()` 改产 CCG 5 要素(修 B)②后缀注册化(`.py` 走 AST、`.ts/.js` 走弱提取器)③`index_dir` 返回 `truncated` 与 `files_scanned`(修 F)④`node_id` 保持稳定(重复索引幂等)⑤`skip_matcher()` + `skip_dirs`(R3.2,两条链路的唯一实现) |
|
|
150
|
+
| `md_cg/mcp_server.py` | **扩** | 新增 `op=ref`(`action=read\|check\|stat\|prune`,兼回读 / 巡检 / 清退);`op=index_code` 保留为别名并透传 `truncated`;`op=index_doc`;op 枚举与 `:542-546` 同步;`index_code`/`index_doc` 索引后自动节点级对账(`prune` 默认开、`prune_dry_run` 可预演,R3.1);`skip_dirs` 透传 + `skipped_dirs` 回报(R3.2) |
|
|
151
|
+
| `md_cg/tokens.py` | **改** | `ALL_OPS`(`:56-59`) 加 `ref` / `index_doc`(漏改会被权限层直接拒),`test_p21_tokens.py` 同步 |
|
|
152
|
+
| `md_cg/sustain.py` | **扩** | `diagnose` 增巡检项 `ref_stale` / `ref_dangling`,`heal` 动作为「重建索引」(派生物,符合现有自愈边界 `:12-14`) |
|
|
153
|
+
| `md_cg/mdcos.py` | **扩** | 检索结果带 `ref` 与 `ref_kind`(读侧不改召回逻辑,只加字段) |
|
|
154
|
+
| `md_cg/test_p26_refindex.py` | **新增** | 见 §七 |
|
|
155
|
+
|
|
156
|
+
---
|
|
157
|
+
|
|
158
|
+
## 六、分阶段实施
|
|
159
|
+
|
|
160
|
+
| 阶段 | 做什么 | 验证 | 状态 |
|
|
161
|
+
|---|---|---|---|
|
|
162
|
+
| **R0** | ✅ **已完成**(§十一 取证纪录):全量 28 用例基线取证 + 修 4 条缺陷(`corpus.reset_root` 全量 rmtree / 3 处编码) | 28/28 全绿;P0 25/25、P1 38/38、P11 27/27、P12 32/32 | ✅ |
|
|
163
|
+
| **R1** | ✅ **已完成**(§十二 取证纪录):`codeindex.render` 改产 CCG 6 行、`EXTRACTORS` 后缀注册化(AST 精确 / TS-JS 弱提取)、`op=ref` 回读 + 漂移检测、`truncated`/`skipped_suffixes` 显式上报;实测 §1.3-1(role 档位) | ✅ 代码节点判定 **ACCEPT**(不再 BLINDSPOT);`op=ref` 回读文本与源区间**逐字一致**、`hash_match` 检出漂移 | ✅ |
|
|
164
|
+
| **R2** | ✅ **已完成**(§十三 取证纪录):`docindex.py`(章节切块 + CCG 渲染 + `doc_ref`)+ `op=index_doc`;裁 §1.3-3(层/密级) | ✅ `docs/` 全部 md(实测 **19/19**,非静默跳过)切块可定位;`docs/mdcg/认知图_MD目录方案_v0.1.md` 的 §9 表被判 **ACCEPT** 且给出行号 `L491-L516` | ✅ |
|
|
165
|
+
| **R3** | ✅ **已完成**(§十四 取证纪录):`_refindex.json` 增量(抄 `sources.Ingestor` 范式)+ `check_refs` 漂移巡检 + `truncated` 显式上报 + 接入 `sustain.diagnose` | ✅ 改源文件一行 → `ref check` 报 stale(重建后消除);删源 → dangling;`max_files` 触发时 `truncated=true` 且写进水位;`diagnose` 报 `ref_stale`、`heal` 的 `rebuild_refs` 修复 | ✅ |
|
|
166
|
+
| **R3.1** | ✅ **已完成**(§十五 取证纪录,2026-09-12):节点级对账——`prune_orphans`(改标题产生的过期代)+ `prune_dangling`(源已删的悬空节点),接入 `index_code`/`index_doc` 与 `op=ref action=prune` | ✅ P27 §10/§10b 全绿;真实现场悬空 **20 → 0**(`LIVE_HITS=0`/`TRASH_HITS=20`);受影响面 7 模块 6 绿 1 环境红 | ✅ |
|
|
167
|
+
| **R3.2** | ✅ **已完成**(§十五 取证纪录,2026-09-12):索引器**追加排除** `skip_dirs`(`index_code`/`index_doc`)——把 `.gitignore` 已忽略却物理留存的实验产物挡在门外,替代「调大 `max_files`」这一掩盖手段 | ✅ P27 **75/0** 全绿(§9 由 `500/2388` 红转 `30/30`);新增 §9b 验证匹配口径 / 只增不减 / 可审计;受影响面 10 模块全绿 | ✅ |
|
|
168
|
+
| **R4** | 修 H:code↔doc 互链(文档中出现的符号名/路径 → 建边指向 code 节点;反向亦然) | 「某功能」检索能同时给出代码位置与文档位置 | ⬜ |
|
|
169
|
+
| **R5** | 修 I(工程规范化产出,见 §九) | 三份索引文件可由 `index_ref` **自动生成**,非手写 | ⬜ |
|
|
170
|
+
| DEFER | `.ps1/.vbs/.yml` 提取器;跨副本身份下的 ref 可见性 | 先决:R4 完成且互链被实际使用 | ⬜ |
|
|
171
|
+
|
|
172
|
+
---
|
|
173
|
+
|
|
174
|
+
## 七、验收(`test_p26_refindex.py`,对齐既有 `test_pNN` 脚本式 `check()` 风格)
|
|
175
|
+
|
|
176
|
+
1. **ref 落盘**:代码/文档节点的 `frontmatter.code_ref`/`doc_ref` 五字段完整,且 `loads` 往返不丢(`nodefile.py:73`)。
|
|
177
|
+
2. **CCG 合规(修 B 的守门断言)**:`ccg_completeness(content)["complete"] is True`;`judge_qualification(...)["state"] != BLINDSPOT`。
|
|
178
|
+
3. **回读一致**:`read_ref(node_id)` 返回的区间文本与该文件当前内容**逐字相同**(非「看起来对」)。
|
|
179
|
+
4. **漂移检测**:在目标文件插入 1 行 → `ref check` 报 `stale`;删除该符号 → 报 `dangling`;两者都不抛异常、都不改源文件。
|
|
180
|
+
5. **只读契约**:索引一轮前后,被索引的 `.py`/`.md` **mtime 全不变**、源文件 hash 不变。
|
|
181
|
+
6. **幂等**:同一目录索引两次 → 节点数不变、`node_id` 稳定、`_refindex.json` 不膨胀。
|
|
182
|
+
7. **不复制正文**:代码节点 content 内「函数体语句」0 命中(如断言不含 `return ` 之外的可执行行);content 长度 ≤ 阈值。
|
|
183
|
+
8. **截断诚实**:`max_files=1` 时 `truncated is True` 且给出建议值。
|
|
184
|
+
9. **文档切块**:heading_path 与行号可定位;正文含 `---` 分隔线不污染 frontmatter(`nodefile.py:19-23` 的既有纪律不回归)。
|
|
185
|
+
10. **权限**:`op=ref` 走 `tokens` 校验;无权限 actor 读取越权层的 ref 应被拒。
|
|
186
|
+
11. **新 op 注册**:`tokens.ALL_OPS` 与 `mcp_server` 的 op 枚举一致(防漏改)。
|
|
187
|
+
|
|
188
|
+
---
|
|
189
|
+
|
|
190
|
+
## 八、风险与代价
|
|
191
|
+
|
|
192
|
+
| 风险 | 后果 | 处理 |
|
|
193
|
+
|---|---|---|
|
|
194
|
+
| `.md` 切块过细 | 30 份文档切出上千节点,污染正排、拖慢检索 | 只切 `level<=3`;正文 <200 字的 section 合并进父节点;先跑一次统计再定阈值 |
|
|
195
|
+
| 行号漂移 | ref 指向错误代码(**静默**) | `hash` + `check_refs` 巡检 + 挂 `sustain.diagnose`;文档不承诺「永久有效」,只承诺「可检出」 |
|
|
196
|
+
| 双份真相 | 索引与源文件各自演化 | 索引是派生物、可重建;doc 索引**不存全文** |
|
|
197
|
+
| `.ts` 无 AST | 质量劣于 `.py` | 标准库无 TS parser → v0.1 用「行首 regex + 上邻注释」**弱提取器**,代码里显式标注「弱」,不冒充 AST |
|
|
198
|
+
| 密级泄漏 | 私有文档被索引进公开层 | 显式声明 `sensitivity`,不依赖默认值;越权写入由 `security` 事前拦截(`sources.py:296-307` 已有同款范式) |
|
|
199
|
+
| 索引自身成单点 | `_refindex.json` 损坏导致增量失效 | 降为纯派生物,可全量重建(对齐 `_index.json` 的既有裁定) |
|
|
200
|
+
|
|
201
|
+
---
|
|
202
|
+
|
|
203
|
+
## 九、对复核表 #12 / #8 的正面回答(DEFER → ACCEPT)
|
|
204
|
+
|
|
205
|
+
复核表 #12 的先决是「**先说清缺哪一份索引**」。答案:
|
|
206
|
+
|
|
207
|
+
| #12 提的三份文件 | 对应本计划 | 关键增量 |
|
|
208
|
+
|---|---|---|
|
|
209
|
+
| `DOCUMENTATION_INDEX`(文档索引) | **R2 `docindex.py` + R5 产出** | 不是手写目录,而是**认知图可检索的 doc 节点**(带 heading_path + 行号) |
|
|
210
|
+
| `FEATURE_MATRIX`(功能矩阵) | **R5 产出** | 矩阵由 `index_ref` 自动生成:`工具 × op × 实现文件:行 × 测试文件`,并**校验「每个 op 都能找到测试」**(`index_code` 的 0 覆盖正是这条能抓出来的) |
|
|
211
|
+
| `FILE_INVENTORY`(文件清单) | **R5 产出(自动生成)** | 由索引自动产出而非手写——这才能避免「清单与代码脱节」这一常规失败 |
|
|
212
|
+
|
|
213
|
+
复核表 #8(manifest:timeout / protocol / 执行语义)同样可转 ACCEPT,且**用协议原生字段而非自造**:
|
|
214
|
+
MCP 规范本身有 `annotations`(`readOnlyHint` / `destructiveHint` / `idempotentHint` / `openWorldHint`)——这恰好是「执行语义分类」;
|
|
215
|
+
md_cg 侧再补 `timeout_ms` 与 `needs_clearance` 两个扩展字段。裁剪到 R5,随三份索引文件一起产出。
|
|
216
|
+
|
|
217
|
+
---
|
|
218
|
+
|
|
219
|
+
## 十、诚实边界
|
|
220
|
+
|
|
221
|
+
1. **本计划是「可索引」,不是「可理解」**:doc/code 索引仍是**确定性静态索引**,不含语义蕴含;跨表述的「这段代码其实实现了那段文档的意思」不解决(与 `memory_score.md:36` 「有意剔除神经嵌入」一致)。
|
|
222
|
+
2. **索引质量上限 = 源文件注释质量**:白箱路线的固有代价。没有 docstring 的函数只能索引到签名。
|
|
223
|
+
3. **`hash` 不是内容寻址**:只做变更探测,不保证「内容相同即同节点」。
|
|
224
|
+
4. ~~R0 有一个待闭环疑点~~ → **已在 §十一 闭环**。「`test_p1` EXIT 1 且输出仅 165 字节」的根因与 `test_p0` 同一类:受管环境下 `os.remove` 被环境 shim 接管,
|
|
225
|
+
删除量越阈即中断进程;且失败形态是**静默截断 + 非零退出**,不是一条 FAIL。现已改为不清空目录(原子覆盖 + 等号守门),并全量复跑验证。
|
|
226
|
+
5. **当前工作区未提交**:`docs/mdcg/认知图_MD目录方案_v0.1.md`、`bench_p0.py`、`census.py`、`test_p0.py`、`test_p1.py` 已改,`md_cg/corpus.py` 未跟踪——R0 应先收口提交,再动索引层。
|
|
227
|
+
|
|
228
|
+
---
|
|
229
|
+
|
|
230
|
+
## 十一、R0 取证纪录(已完成 · 2026-09-10)
|
|
231
|
+
|
|
232
|
+
### 11.1 基线复跑结果(修前 vs 修后)
|
|
233
|
+
|
|
234
|
+
| 用例 | 修前 | 修后 | 说明 |
|
|
235
|
+
|---|---|---|---|
|
|
236
|
+
| `test_p0` | ❌ EXIT 1,输出 **443 字节**,**无任何 FAIL 项** | ✅ **25/25** | 死在第一条断言之前 |
|
|
237
|
+
| `test_p1` | ✅ EXIT 0(偶发 ❌ EXIT 1 / 165 字节) | ✅ **38/38** | 新增守门断言 1 条 |
|
|
238
|
+
| `test_p11_consistency` | ❌ EXIT 1,输出 725 字节 | ✅ **27/27** | **此前从未真正执行过** |
|
|
239
|
+
| `test_p12_metacognition` | ❌ EXIT 1,输出 725 字节 | ✅ **32/32** | **此前从未真正执行过** |
|
|
240
|
+
| 全量 28 个用例 | 非零退出 2 个 | ✅ **非零退出 0 个** | — |
|
|
241
|
+
|
|
242
|
+
### 11.2 缺陷 1(已修 · 严重):测试「重跑 ≡ 首跑」的前提是脆的,且失败不可见
|
|
243
|
+
|
|
244
|
+
**现象**:`test_p0` 输出仅 443 字节:
|
|
245
|
+
```
|
|
246
|
+
[风险1] 分桶键退化 …
|
|
247
|
+
[safe-delete][SAFE_DELETE_BULK_CONFIRM_REQUIRED] {"count":722,"threshold":500,"scope":"turn", …}
|
|
248
|
+
```
|
|
249
|
+
**根因链(闭合)**:`test_p0.py:68` → `corpus.reset_root(CONC_ROOT)` → `shutil.rmtree(722 文件)`
|
|
250
|
+
→ 环境 shim(`sitecustomize._safe_remove`)逐个接管删除 → 累计 722 > 阈值 500 → 拒绝并中断进程。
|
|
251
|
+
|
|
252
|
+
**铁证**:`_md_cg_p0_conc` 实测 **722 文件**,与报错的 `"count":722` 完全吻合;
|
|
253
|
+
`_md_cg_p0` 实测 **0 文件**——说明第一个 `reset_root(ROOT)` 已清空+重建成功,死在第二个;
|
|
254
|
+
`_md_cg_p1` 实测 **350 文件**(< 500)故通过——**只差 150 就到阈值**,这正是历史上「P1 EXIT 1 / 165 字节」的同源形态。
|
|
255
|
+
|
|
256
|
+
**关键点:文档早已裁决过**。`docs/mdcg/认知图_MD目录方案_v0.1.md:365-367` 原文:
|
|
257
|
+
> 幂等性:迁移按 node id 原子覆盖写,可反复执行,**不需要先清空目录**。(这不只是便利:清空目录依赖 `rmtree`,
|
|
258
|
+
> 在有安全策略的环境下会被拦截,而"必须先删除才能重跑"的迁移脚本本身就是个脆弱设计。)
|
|
259
|
+
|
|
260
|
+
而同一份文档 `:350` 又写「测试前 `corpus.reset_root(root)` 清空」,`corpus.py:84-88` 实现的正是清空版——
|
|
261
|
+
**实现踩中了文档自己警告的坑,且文档内部自相矛盾**。
|
|
262
|
+
|
|
263
|
+
**修法**(按文档已裁决的设计修实现,非新发明):`reset_root` 默认**不删目录**,只保证存在;
|
|
264
|
+
重跑不变性改由两个可观测事实守住——① 写入侧同 id 原子覆盖(`seed()` 固定 id);
|
|
265
|
+
② **等号**断言(`test_p0`「索引节点数 == 语料期望数」由无害升级为守门者;
|
|
266
|
+
`test_p1` 新增「knowledge 层 == 336 + 5」守门断言,实测通过)。
|
|
267
|
+
|
|
268
|
+
### 11.3 缺陷 2(已修):两个用例因 stdout 编码**从未真正跑过**
|
|
269
|
+
|
|
270
|
+
`test_p11_consistency.py` / `test_p12_metacognition.py` 缺 `sys.stdout.reconfigure(encoding="utf-8")`。
|
|
271
|
+
Windows 默认 gbk 控制台下打印「d²D/dt²」的 `²`(U+00B2) 直接 `UnicodeEncodeError`,
|
|
272
|
+
**崩在第一条断言之前**,输出只剩 725 字节。仓库其实已有该约定(`test_p0.py:246-249` 的注释甚至
|
|
273
|
+
预言了「只落盘 444 字节」这一失败形态),这两个用例只是漏抄了。**修后 P11 27/27、P12 32/32。**
|
|
274
|
+
|
|
275
|
+
### 11.4 缺陷 3/4(已修):读子进程输出未指定 encoding
|
|
276
|
+
|
|
277
|
+
- `md_cg/audit.py:167-169`(`_verify_code` 的「实测」路径):`text=True` 退回 locale(gbk) → 被测命令
|
|
278
|
+
只要输出非 gbk 字节,读取线程即抛 `UnicodeDecodeError` → `p.stdout/p.stderr` 可能为空 →
|
|
279
|
+
`:174` 的失败证据丢失,「实测失败」退化成**一句没有依据的 REJECT**。
|
|
280
|
+
- `md_cg/test_p22_theory.py:156-159`(CLI 子进程):同类隐患。
|
|
281
|
+
|
|
282
|
+
两处均补 `encoding="utf-8", errors="replace"`,对齐 `whitebox.py:93-97` 的正确写法。
|
|
283
|
+
|
|
284
|
+
### 11.5 环境事实(不修,但必须记录)
|
|
285
|
+
|
|
286
|
+
装探针抓现场(`subprocess.Popen.__init__` 打点)确认:**受管环境下 `os.remove` 每次都要 spawn 外部进程**
|
|
287
|
+
(`node.exe safe-delete-bulk-guard.cjs` → 判定 → `genie-trash/win32-x64.exe` → 移动;
|
|
288
|
+
其间还会经 `platform.machine()` → `platform.win32_ver()` → `subprocess.check_output('ver')` 再 spawn 一次)。
|
|
289
|
+
|
|
290
|
+
后果三条,均记入工程规范化的前提:
|
|
291
|
+
1. **删除 ≠ 真删**,而是移入回收站;批量删除有 500 阈值门。
|
|
292
|
+
2. `fsutil.ShardedLog.clear`(`fsutil.py:249`) 逐文件 `os.remove` 是**性能热点**(`rebuild_index` 每次都调)。
|
|
293
|
+
3. `test_p1` 输出尾部那条 `UnicodeDecodeError … _readerthread` 异常**来自环境 shim 自身**
|
|
294
|
+
(守护线程抛出,不影响退出码,但污染输出、会让 CI 误判)。**仓库侧无法修**,只能记录。
|
|
295
|
+
|
|
296
|
+
**已验证非仓库问题**:全仓 `platform.*` 使用数 = **0**,该 spawn 由环境 shim 发起。
|
|
297
|
+
|
|
298
|
+
---
|
|
299
|
+
|
|
300
|
+
## 十二、R1 取证纪录(已完成 · 2026-09-10)
|
|
301
|
+
|
|
302
|
+
### 12.1 交付物与改动清单
|
|
303
|
+
|
|
304
|
+
| 文件 | 改动 | 对应计划项 |
|
|
305
|
+
|---|---|---|
|
|
306
|
+
| `md_cg/codeindex.py` | `render()` 由「非 CCG 注释行」改为输出 **CCG 6 行**;`_walk_defs()` 按源码顺序并携带父级类名;新增 `EXTRACTORS` 后缀注册表(`.py`→AST 精确 / `.ts·.tsx·.js·.mjs·.cjs`→弱提取);`region_hash()` 收敛为**唯一实现**;`index_dir` 返回 `(items, errors, stats)`(含 `truncated`/`skipped_suffixes`);无提取器后缀**显式 `raise ValueError`** | 修 B、修 E(部分) |
|
|
307
|
+
| `md_cg/mcp_server.py` | `op=index_code` 透传 stats、落 `verification_basis`/`code_ref`(含 `lang`/`precise`/`hash`/`root`);新增 `_ref_call` 回读源区间 + `hash_match` 漂移检测;op 枚举与描述加入 `ref`,新增 `ref`/`root` 参数 | 修 C |
|
|
308
|
+
| `md_cg/tokens.py` | `ALL_OPS` 收录 `ref` | §七-11(防漏改) |
|
|
309
|
+
| `md_cg/test_p26_refindex.py` | 新增验收用例(47 断言,全绿) | §七 |
|
|
310
|
+
|
|
311
|
+
**未改动**:`mdcg.py`(`add` 的 `**extra` 已能承载 `code_ref`,无需改签名)、`nodefile.py`(CCG 判定逻辑本就正确,问题在渲染侧)。
|
|
312
|
+
|
|
313
|
+
### 12.2 关键缺陷与修法:所有代码节点「恒定 BLINDSPOT」
|
|
314
|
+
|
|
315
|
+
**现象**:改造前 `index_code` 索引出的代码节点**存得进、判不了、检索不到**——`judge_qualification` 第一条 `ccg_completeness` 即判 BLINDSPOT。
|
|
316
|
+
|
|
317
|
+
**根因(闭合)**:旧 `render()` 只产 `# path::name` / `# sig` 等**非 CCG 行**,不含「功能名 / 生效条件 / 子功能 / 执行 / 验证方式 / 不适用条件」5 要素中任何一项 → `nodefile.CCG_REQUIRED` 缺项 → `complete=False` → BLINDSPOT。
|
|
318
|
+
即:**渲染层不产 CCG,却要求判定层按 CCG 判**——两侧契约错配。
|
|
319
|
+
|
|
320
|
+
**修法**:`render()` 产出完整 CCG 6 行(5 要素 + `# 位置`),`verification_basis` 显式落 `frontmatter`(`.py`→`compiler`、弱提取→`other`),使节点在「CCG 完整 + 基底已声明」下判 **ACCEPT**。
|
|
321
|
+
|
|
322
|
+
**守门断言**(`test_p26` §4/§5):`ccg_completeness(render(item))["complete"] is True`;`op=read` 真实检索链路命中且 `state == "ACCEPT"`(Python 与 JS 各一例)。
|
|
323
|
+
|
|
324
|
+
### 12.3 `op=ref` 回读:索引侧与回读侧**共用同一哈希实现**
|
|
325
|
+
|
|
326
|
+
- `codeindex.region_hash(lines, lineno, end)` 为唯一实现;索引侧写入 `code_ref.hash`,回读侧 `_ref_call` 重算并比对 → `hash_match`。
|
|
327
|
+
- **实测**:未改动 → `hash_match=True`/`stale=False`;改源(保持行数不变只换实现)→ `hash_match=False`/`stale=True`;**重跑 `index_code` → 漂移消除**(可恢复)。
|
|
328
|
+
- 边界**均显式报错,不静默读错位置**:缺 `root` → `ref 未记录 root…`;非代码节点 → `该节点没有 code_ref…`;节点不存在 → `节点不存在:…`。
|
|
329
|
+
- **只读契约**:索引一轮前后被索引源文件 **mtime 全不变**(§七-5)。
|
|
330
|
+
|
|
331
|
+
### 12.4 「不静默」三处落地(修 E 部分)
|
|
332
|
+
|
|
333
|
+
| 旧行为 | 新行为 | 断言 |
|
|
334
|
+
|---|---|---|
|
|
335
|
+
| `max_files` 触发时静默返回不完整结果 | `truncated=True` + `truncated_reason`(如 `files=1>=max_files=1`)+ `note` 警告 | §九 |
|
|
336
|
+
| 扫到无提取器后缀静默丢弃 | `stats.skipped_suffixes` 显式列出(实测 `['.txt']`) | §二 |
|
|
337
|
+
| 语法错误文件被吞 | 进 `errors`(实测 `bad.py: bad.py:1: invalid syntax`) | §十 |
|
|
338
|
+
|
|
339
|
+
### 12.5 §七 验收对照
|
|
340
|
+
|
|
341
|
+
| 验收项 | 状态 | 说明 |
|
|
342
|
+
|---|---|---|
|
|
343
|
+
| 1 ref 落盘(`code_ref` 字段完整) | ✅ | 9 字段(path/name/kind/lineno/end/lang/precise/hash/root) |
|
|
344
|
+
| 2 CCG 合规(`complete is True`、非 BLINDSPOT) | ✅ | 实测 ACCEPT |
|
|
345
|
+
| 3 回读逐字一致 | ✅ | 文本含 `def compute_energy` 与实现行 |
|
|
346
|
+
| 4 漂移检测 | ⚠️ 部分 | `hash_match` 漂移 ✅;`ref check` CLI 报 `stale`/`dangling` 属 **R3** |
|
|
347
|
+
| 5 只读契约(mtime 不变) | ✅ | — |
|
|
348
|
+
| 6 幂等(节点数不变、id 稳定) | ✅ | 12 == 12;`_refindex.json` 属 **R3** |
|
|
349
|
+
| 7 不复制正文 | ✅ | content 不含函数体语句 |
|
|
350
|
+
| 8 截断诚实 | ✅ | — |
|
|
351
|
+
| 9 文档切块 | ⏭️ | 属 **R2**(`docindex.py`) |
|
|
352
|
+
| 10 权限(`op=ref` 走 `tokens`) | ✅ | `ops_allow` 未含 `ref` 即被 `require_op` 拒 |
|
|
353
|
+
| 11 新 op 注册一致 | ✅ | `ALL_OPS` 含 `ref`/`index_code` |
|
|
354
|
+
|
|
355
|
+
### 12.6 回归
|
|
356
|
+
|
|
357
|
+
全量 **29 个用例模块**(R0 的 28 + 新增 `test_p26_refindex`)**非零退出 0 个**;`test_p26_refindex` **47/47** 全绿。
|
|
358
|
+
|
|
359
|
+
---
|
|
360
|
+
|
|
361
|
+
## 十三、R2 取证纪录(已完成 · 2026-09-10)
|
|
362
|
+
|
|
363
|
+
### 13.1 交付物与改动清单
|
|
364
|
+
|
|
365
|
+
| 文件 | 改动 | 对应计划项 |
|
|
366
|
+
|---|---|---|
|
|
367
|
+
| `md_cg/docindex.py` | **新增**:`.md` 章节切块(ATX 标题栈 + `heading_path` + 行号区间 + `anchor` + 首段摘要);`render()` 产 **CCG 7 行**(5 要素 + 验证方式 + 位置);`doc_ref` 11 字段;`_region_hash` 委托 `codeindex.region_hash`(**唯一实现**);`index_dir(root, patterns, ...)` 返回 `(items, errors, stats)`(含 `truncated`/`truncated_reason`/`skipped_suffixes`);无提取器后缀**显式 `raise ValueError`** | 修 A、修 C(文档侧) |
|
|
368
|
+
| `md_cg/mcp_server.py` | 新增 `op=index_doc` 分支(逐条 `cg.add(..., layer=, tags=["doc","doc:md","level:<n>"], verification_basis="data", sensitivity=, doc_ref=)`);`_ref_call` 的 `ref_kind` 扩为 `code_ref`/`doc_ref`;op 枚举与描述加入 `index_doc`;新增 `sensitivity` 参数 | 修 C |
|
|
369
|
+
| `md_cg/tokens.py` | `ALL_OPS` 收录 `index_doc`(`... "index_code","index_doc","ref","theory","link"`) | §七-11(防漏改) |
|
|
370
|
+
| `md_cg/test_p27_docindex.py` | **新增** 验收用例(**49 断言,全绿**) | §七 |
|
|
371
|
+
|
|
372
|
+
**未改动**:`mdcg.py`(`add` 的 `**extra` 已能承载 `doc_ref`,与 `code_ref` 同路径)、`nodefile.py`(CCG 判定逻辑本就正确,问题在渲染侧)、`mdcos.py`(检索侧无需改——文档节点与代码节点走同一判定路径)。
|
|
373
|
+
|
|
374
|
+
### 13.2 §七-9 文档切块:三条硬约束的落地与实测
|
|
375
|
+
|
|
376
|
+
md 不是代码,切块风险集中在「把不该当标题的当标题」。三处均实测守门:
|
|
377
|
+
|
|
378
|
+
| 硬约束 | 若不处理 | 处理与实测 |
|
|
379
|
+
|---|---|---|
|
|
380
|
+
| **围栏代码块内 `#` 非标题** | `docs/` 里大量 python/shell 片段带 `#` 注释 → 一节被切成假标题、行号错位 | `_headings()` 带围栏跟踪(``` / ~~~ 状态机)。实测:含假标题的样例仍只切出 `['9. 分阶段实施', '代码示例', '总览']`,**假标题零泄漏**,且父节区间**未被假标题截断** |
|
|
381
|
+
| **开头 YAML frontmatter 不当正文** | 切块起点落在 `---` 里,首节点区间从第 1 行开始 | `_body_start()` 跳过 frontmatter。实测:`总览` 节点 `lineno=6`(跳过 YAML) |
|
|
382
|
+
| **正文 `---` 不污染 frontmatter** | 文档正文分隔线被 `nodefile` 当 frontmatter 边界 → `doc_ref` 读不回 | 校验 `doc_ref` 字段可正常 `loads` 往返(`['总览','9. 分阶段实施']`),与 `nodefile.py:19-23` 既有纪律一致、不回归 |
|
|
383
|
+
|
|
384
|
+
**层级与合并**:只切 `level<=3`(实测条目层级 `[1,2,2]`);直接正文 <200 字且无子节的小节点**合并进父节摘要**(实测「表格与正文说明。」与围栏代码片段均并入父节,**不丢内容**),避免「一行小标题」也成节点(计划 §八 风险项 1)。
|
|
385
|
+
|
|
386
|
+
### 13.3 §1.3-3 裁定:层与密级(详见 §1.3-3 的落地)
|
|
387
|
+
|
|
388
|
+
- **layer = `knowledge`(默认)**:文档是可回读、可漂移检测的**参照知识**,与代码节点同层,保证进默认召回;`contextual` 意为情境绑定、会过期,用在此处会让文档掉出默认召回。实测 guide 节点 `layer=knowledge`。
|
|
389
|
+
- **sensitivity = `internal`(默认)+ 显式写入 frontmatter**:不依赖节点默认值(`mdcos` 读隔离取的是 `fm.sensitivity`;不显式写=靠默认兜底,审计看不出意图)。实测 guide 节点 frontmatter 密级显式为 `internal`。
|
|
390
|
+
- **私有路径段 → 保守降 `private`**:`PRIVATE_HINTS=("private","secret","internal","未公开","私有","内部")` 命中路径段即降级;**只可能更严,不更松**。实测 `guide/`(internal 3)+ `private/`(private 1)→ 分布 `{'internal':3,'private':1}`。
|
|
391
|
+
- **可显式覆盖**:`op=index_doc` 传 `sensitivity="public"` 可整批覆盖(实测 4 节点全 `public`),覆盖后 frontmatter 同步。
|
|
392
|
+
|
|
393
|
+
### 13.4 `doc_ref` 回读与漂移(与 `code_ref` 共用唯一哈希实现)
|
|
394
|
+
|
|
395
|
+
- `doc_ref` 落 **11 字段**:`path/heading/heading_path/level/lineno/end/anchor/hash/lang/precise/root`。
|
|
396
|
+
- `op=ref` 回读:索引侧与回读侧**共用 `codeindex.region_hash`**(`docindex._region_hash` 只是委托,杜绝两套实现漂移)。实测未改动 → `hash_match=True`;改文档一段 → `hash_match=False`(**stale 可检出**);**重跑 `index_doc` → 漂移消除**(派生物可重建)。
|
|
397
|
+
- **只读契约**:索引一轮前后被索引 `.md` 源文件不被触碰(测试断言通过)。
|
|
398
|
+
- **边界显式报错、不静默**:无提取器后缀 `raise ValueError`;`max_files` 触发时 `truncated=True` + `truncated_reason="files=1>=max_files=1"` + `note` 警告。
|
|
399
|
+
|
|
400
|
+
### 13.5 §七 验收对照
|
|
401
|
+
|
|
402
|
+
| 验收项 | 状态 | 说明 |
|
|
403
|
+
|---|---|---|
|
|
404
|
+
| 1 ref 落盘(`doc_ref` 字段完整) | ✅ | 11 字段完整、`loads` 往返不丢 |
|
|
405
|
+
| 2 CCG 合规(`complete is True`、非 BLINDSPOT) | ✅ | 文档节点实测 `ACCEPT` |
|
|
406
|
+
| 3 回读逐字一致 | ✅ | 回读文本 = 源章节(含标题与表格行) |
|
|
407
|
+
| 4 漂移检测 | ⚠️ 部分 | `hash_match` 漂移 ✅ + 重跑消除 ✅;`ref check` CLI/`_refindex.json` 报 `stale`/`dangling` 属 **R3** |
|
|
408
|
+
| 5 只读契约(mtime 不变) | ✅ | — |
|
|
409
|
+
| 6 幂等(节点数不变、id 稳定) | ✅ | `304 == 304`;id 集合稳定(按 id 原子覆盖,不清目录) |
|
|
410
|
+
| 7 不复制正文 | ✅ | 不逐字复制整节;「执行」栏摘要封顶 ≤200 字 |
|
|
411
|
+
| 8 截断诚实 | ✅ | — |
|
|
412
|
+
| **9 文档切块** | ✅ | 三硬约束全过(§13.2);§9 真实章节行号 `491 == 491` |
|
|
413
|
+
| 10 权限(`op=ref` 走 `tokens`) | ✅ | R1 已覆盖,文档节点同路径 |
|
|
414
|
+
| 11 新 op 注册一致 | ✅ | `ALL_OPS` 含 `index_doc`;工具 op 枚举 == `ALL_OPS`(`only-schema=[] only-ops=[]`) |
|
|
415
|
+
|
|
416
|
+
### 13.6 回归
|
|
417
|
+
|
|
418
|
+
全量 **30 个用例模块**(R1 的 29 + 新增 `test_p27_docindex`)**非零退出 0 个**;`test_p27_docindex` **49/49** 全绿。
|
|
419
|
+
|
|
420
|
+
### 13.7 诚实边界(R2 新增)
|
|
421
|
+
|
|
422
|
+
1. **计数口径**:计划 §六 R2 目标原写「`docs/` 30 份」——那是把 `docs/` 下非 md 也计入的早期估计;**实际 md 文件 19 份**,R2 实测 `files=19/19`(无静默跳过)。后续阶段一律以「按后缀注册表实际匹配数」为准,不再用估计值。
|
|
423
|
+
2. **文档索引仍是确定性的位置索引**,不含语义:能回答「这段写在哪个文档哪一节」,不能回答「这段文档说的是哪段代码」(后者属 R4 互链,且仍不做语义蕴含)。
|
|
424
|
+
3. **`doc_ref` 不承诺永久有效**,只承诺「可检出」(`hash` 变更探测)。
|
|
425
|
+
|
|
426
|
+
---
|
|
427
|
+
|
|
428
|
+
## 十四、R3 取证纪录(已完成 · 2026-09-10)
|
|
429
|
+
|
|
430
|
+
### 14.1 交付物与改动清单
|
|
431
|
+
|
|
432
|
+
| 文件 | 改动 | 对应计划项 |
|
|
433
|
+
|---|---|---|
|
|
434
|
+
| `md_cg/refindex.py` | **新增**:统一 ref 协议(`REF_KEYS` / `registry()` / `kind_of_path()` / `extract()`,无提取器后缀**显式 `raise`**);`Ledger` 索引水位(`_refindex.json`,**键 = 源文件绝对路径**,逐文件记 `(size, mtime)` + 节点区间 + **源大域 `root`**);`index_dir()` 统一调度(`fresh`/`on_file` 钩子 + 增量跳过 + 截断留痕 + `prune`/`reconcile` 对账);`probe_ref()`/`read_ref()` 回读;`check_refs()` 漂移·悬空巡检;`rebuild()` 修复动作 | 修 D、修 F |
|
|
435
|
+
| `md_cg/codeindex.py` / `md_cg/docindex.py` | `index_dir` 加 `fresh`/`on_file` 钩子与 `skipped_unchanged` 计数(默认 `None` → 与改造前**逐字一致**) | 修 F |
|
|
436
|
+
| `md_cg/mcp_server.py` | `op=index_code`/`index_doc` 改走 `refindex.index_dir` + `refindex.add_items`(**落盘唯一实现**);新增 `incremental`/`max_items` 参数(传 `sensitivity` 时自动退回全量);`_ref_call` 重写为 `action=read\|check\|stat`,**未知 action `raise`**;`read` 与检索结果带 `ref`/`ref_kind` | 修 D、修 F |
|
|
437
|
+
| `md_cg/sustain.py` | `diagnose` 增 `ref_stale`/`ref_dangling`/`ref_check_truncated` 巡检项与 `ref_checked`/`ref_stale`/`ref_dangling` stats;`heal` 增 `rebuild_refs` 动作(派生物,符合既有自愈边界) | 修 D 接入 |
|
|
438
|
+
| `md_cg/test_p28_refcheck.py` | **新增** 验收用例(**45 断言,全绿**) | §七 |
|
|
439
|
+
|
|
440
|
+
### 14.2 三个真实缺陷(首轮实测 39 通过 / 6 失败 → 修后 45/0)
|
|
441
|
+
|
|
442
|
+
首轮失败不是断言太严,而是**实现确实错**,逐条留证:
|
|
443
|
+
|
|
444
|
+
1. **巡检把一切都误判成 `dangling`(严重)**:`Ledger.record()` 没记源大域,`check_refs` 拿 `ledger.root`(= 认知图根)去拼 `root/rel`;而源在别处(如临时目录 / 任意被索引的大域),于是**文件永远找不到**。实测:改源一行后 `stale=[]`、`dangling=[alpha.py,beta.py,...]`,`sustain.diagnose` 因此报 `ref_dangling` 而非 `ref_stale`。
|
|
445
|
+
→ 逐文件记 `root`,巡检用**每个源文件自己的 root**;且键改绝对路径。
|
|
446
|
+
2. **水位不会遗忘死根**:键若按 `rel`(或虽按绝对路径但从不清理),源大域整个搬迁/删除后条目永久残留。因 `node_id` 与绝对路径无关,`nodes` 过滤挡不住这些幽灵条目,巡检会报**永不消失的 dangling**(`heal` 也治不好,因为条目不在任何会被重建的 root 下)。实测:连跑两次用例 → 水位 `files=4`(含上一轮已删临时目录的 2 条),`重跑后 dangling 消除` 失败。
|
|
447
|
+
→ 新增 `prune()`(剪掉源大域已不存在的条目,真悬空由节点自带 ref 兜底)+ `reconcile(root, kind, seen)`(一次**完整**索引后,本 root 下没扫到的旧条目剪掉)。**截断的索引不对账**——没扫完 ≠ 剩下的都消失了。
|
|
448
|
+
3. **落盘字节抖动**:`updated_at`/`ts` 用 `time.time()` 全精度浮点,`float` 最短 repr 的小数位长度会变 → 重跑 3 次体积 `1156 → 1157`,「不膨胀」判据假失败。→ 时间戳压到 **1 位小数**(`_now()`),体积稳定(实测 `1504 → 1504`)。
|
|
449
|
+
|
|
450
|
+
### 14.3 增量(修 F):未变文件不重切
|
|
451
|
+
|
|
452
|
+
- `_refindex.json` 按 `(size, mtime)` 双等判「未变」,`incremental=True` 时**不读盘**直接跳过,计入 `skipped_unchanged`。
|
|
453
|
+
- 实测:首轮全量 `indexed=6 / files=2`;第二轮 `incremental=true` → `skipped_unchanged=2 / indexed=0`,**节点数不变(幂等)**;不传 `incremental` 时 `skipped_unchanged=0`(默认仍全量,保证不漏召回);两轮 `id` 集合稳定。
|
|
454
|
+
- 水位键 = **源文件绝对路径**:一份认知图可索引多个大域,只按 `rel` 记会在 `alpha.py` 这类同名文件上互相覆盖(静默丢水位)。
|
|
455
|
+
|
|
456
|
+
### 14.4 漂移 / 悬空(修 D):唯一判定实现
|
|
457
|
+
|
|
458
|
+
- 回读与巡检**共用 `probe_ref` / `codeindex.region_hash`**,杜绝「回读说没漂、巡检说有漂」。`stale` 判据是**内容哈希**,不是 mtime——只 `touch` 不改内容,报 `ok` 才对(段⑧ 首版用例正是写入了与索引**相同**的正文,属用例缺陷,已改为第二个真实漂移变体)。
|
|
459
|
+
- 实测:改源一行(`lineno 5..7` 区间)→ `check` `ok=false`、`stale=[alpha.py ×2]`;重跑 `index_code` → `stale=[]`;删 `beta.py` → `dangling=[beta.py ×2]`;恢复源并重建 → `dangling=[]`。
|
|
460
|
+
- **只读契约**:巡检前后被索引源文件 `mtime` 不变;`heal` 也不修改源文件(源里仍是改后的正文)。
|
|
461
|
+
- **快路径**:ledger 判「未变」的文件不读盘直接判 ok;变了的才读一次按记录区间重算哈希。ledger 覆盖不到的节点再回退逐节点探测(`only_tagged=True` 只扫带 `code`/`doc` 标签者)。
|
|
462
|
+
|
|
463
|
+
### 14.5 截断不静默 + 接入 sustain
|
|
464
|
+
|
|
465
|
+
- `max_files=1` → `truncated=true`、`truncated_reason="files=1>=max_files=1"`、`note` 带 ⚠;并写进水位 `last_index.truncated=true`,重跑全量后回 `false`(`action=stat` 可见)。
|
|
466
|
+
- `sustain.diagnose`:改源后 `issues` 含 `ref_stale`、`stats.ref_stale=2` 且带 `ref_checked`;`heal` 动作含 `rebuild_refs`(实测 `['rebuild_refs','flush_index']`),heal 后 `ref_stale` 消除、源文件未被修改。
|
|
467
|
+
- 巡检超过 `max_nodes` 时报 `ref_check_truncated`(覆盖缺口显式说出,而非静默截断)。
|
|
468
|
+
|
|
469
|
+
### 14.6 §七 验收对照
|
|
470
|
+
|
|
471
|
+
| 验收项 | 状态 | 说明 |
|
|
472
|
+
|---|---|---|
|
|
473
|
+
| 1 ref 落盘字段完整 | ✅ | `code_ref` 9 字段 / `doc_ref` 11 字段,注册表覆盖两类 |
|
|
474
|
+
| 2 CCG 合规(非 BLINDSPOT) | ✅ | 由 `index_code`/`index_doc` 产出的节点实测 `ACCEPT`(R1/R2 已覆盖) |
|
|
475
|
+
| 3 回读逐字一致 | ✅ | 回读文本 = 源区间,`hash_match=true` |
|
|
476
|
+
| **4 漂移检测** | ✅ | `ref check` 报 `stale`;重建后消除;纯 mtime 变更**不**误报(§14.4) |
|
|
477
|
+
| **5 悬空检测** | ✅ | 删源报 `dangling`;恢复重建后消除;死根条目被 `prune` |
|
|
478
|
+
| 6 只读契约(mtime 不变) | ✅ | 巡检与 heal 均不改源文件 |
|
|
479
|
+
| 7 幂等(节点数不变、id 稳定) | ✅ | 增量/全量重跑节点数与 id 集合均稳定 |
|
|
480
|
+
| **8 截断诚实** | ✅ | `truncated` + `truncated_reason` + 写进水位 `last_index` |
|
|
481
|
+
| 9 增量不重切 | ✅ | `skipped_unchanged=2 / indexed=0` |
|
|
482
|
+
| 10 权限(`op=ref` 走 `tokens`) | ✅ | `ALL_OPS` 仍收录 `ref`/`index_code`;未知 action 不静默成功 |
|
|
483
|
+
| 11 派生物不膨胀 | ✅ | `_refindex.json` `< 20KB`;重跑 3 次字节数不变;`files` 无幽灵条目 |
|
|
484
|
+
|
|
485
|
+
### 14.7 回归
|
|
486
|
+
|
|
487
|
+
全量 **31 个用例模块**(R2 的 30 + 新增 `test_p28_refcheck`)**非零退出 0 个**;`test_p28_refcheck` **45/45** 全绿;`test_p26_refindex` 47/47、`test_p27_docindex` 49/49 无回归;`refindex.py` 改动文件 lint 0 错误。
|
|
488
|
+
|
|
489
|
+
### 14.8 诚实边界(R3 新增)
|
|
490
|
+
|
|
491
|
+
1. **`stale` 只承诺「可检出」,不承诺「自动定位到改了哪一行」**:报的是区间哈希不匹配 + 原区间坐标,不做行级 diff。
|
|
492
|
+
2. **水位是派生物缓存,可随时删除**:`prune`/`reconcile` 让它有界,但语义上它只是提速;删掉后巡检退回逐节点探测,结论不变。
|
|
493
|
+
3. **多根同名文件**:键为绝对路径,同名文件分属不同大域**互不覆盖**;但同一文件被两个大域同时索引时,`nodes` 会指向同一组 id,最后写入者胜(与索引自身语义一致)。
|
|
494
|
+
4. **`max_files` 截断时不对账**(§14.2-2),因此截断轮次后水位会短暂保留未扫文件的旧条目——这是**保守**选择:宁可多留,不可误删。
|
|
495
|
+
|
|
496
|
+
---
|
|
497
|
+
|
|
498
|
+
## 十五、R3.1 / R3.2 取证纪录(已完成 · 2026-09-12):节点级对账 + 索引追加排除
|
|
499
|
+
|
|
500
|
+
> **为什么单开一节而不并进 R3**:R3 只做了**水位层**对账(`Ledger.reconcile`/`prune` 剪的是 `_refindex.json` 的水位条目),
|
|
501
|
+
> **节点层**两类残留无人处置——`node_id = sha1(相对路径 + "#" + heading_path)`,文档标题一变整篇 id 全量重算,
|
|
502
|
+
> 于是同 path 的旧代节点与新代节点并存(一份文档召回两份);源文件一删,节点永久 `dangling`。
|
|
503
|
+
> §十四 验收项 5 的原话是「删源报 `dangling`」——**悬空当时只承诺「可检出」,本节补上「可处置」**。
|
|
504
|
+
|
|
505
|
+
### 15.1 交付物与改动清单
|
|
506
|
+
|
|
507
|
+
| 文件 | 动作 | 职责 |
|
|
508
|
+
|---|---|---|
|
|
509
|
+
| `md_cg/refindex.py` | **扩** | 节点级对账节:`_norm_root`/`_same_root`/`_norm_rel`(大域与路径归一)+ `_forget_many`(逐条软删、受保护拦下、缺 `forget` 能力显式报错)+ `prune_orphans`(清退同 root+path 的过期代)+ `prune_dangling`(清退悬空) |
|
|
510
|
+
| `md_cg/mcp_server.py` | **扩** | `op=ref` 增 `action=prune`/`prune_dangling`;`index_code`/`index_doc` 返回前接 `_prune_after_index`(读 `prune` 默认 True、`prune_dry_run` 预演);schema 增 `prune`/`prune_dry_run`/`roots` |
|
|
511
|
+
| `md_cg/test_p27_docindex.py` | **扩** | 新增【10】节点级对账、【10b】悬空清退;`cg` 由 `MdCG` 改 `MdCGOS`(对齐生产路径) |
|
|
512
|
+
|
|
513
|
+
### 15.2 两个真实缺陷(首轮实测暴露,逐条留证)
|
|
514
|
+
|
|
515
|
+
1. **对账静默假成功(严重)**:`cg.index['nodes']` 是 `mdcg._scan_nodes()` 产出的**元数据快照**(path/layer/tags/importance/edges…),**不含 `ref`**——`ref` 只写在节点 frontmatter 里。首版直接对索引条目调 `ref_of()`,**恒返回 `('', None)`**,于是「清退了 0 条」却报 `ok=True`。实测 `prune_orphans count=0`、`prune_dangling candidates=0`。
|
|
516
|
+
→ 改为 `node = cg.get(nid)` 取回节点再 `ref_of(node)`,并加 `doc`/`code` 标签预筛(与 `check_refs` 同口径,免得为全库每条记忆都读一次盘)。
|
|
517
|
+
2. **`forget` 能力分层被忽略**:`forget` 属 **`MdCGOS` 层**(保护裁决 + 回收站 + 删除清单),基础层 `MdCG` 没有任何删除原语。首版测试用 `MdCG(ROOT)`,实测 `'MdCG' object has no attribute 'forget'`。
|
|
518
|
+
→ ①`_forget_many` 缺能力时**显式返回错误、不静默跳过**(文案点名「生产路径是 MdCGOS,测试请用 MdCGOS」);②测试改 `MdCGOS` 构造。
|
|
519
|
+
→ 与 P43 的教训同构:**双实现类(`MdCG`/`MdCGOS`)必须双路覆盖**,只测一条路径等于留静默盲区。
|
|
520
|
+
|
|
521
|
+
### 15.3 三条保守约束(宁可不删,不可误删)
|
|
522
|
+
|
|
523
|
+
- **只清「本次真正重切过」的文件**:`prune_orphans` 的判定范围限于 `items` 里的 path;增量索引跳过的未变文件不进判定——否则会把完好节点整片误删。
|
|
524
|
+
- **截断轮次不清退**:没扫完 ≠ 剩下的都过期。`_prune_after_index` 见 `truncated=True` 直接返回,不算、不删。
|
|
525
|
+
- **探测失败不算悬空**:`probe_ref` 抛异常即跳过,不把「读不到」当成「源已删」。
|
|
526
|
+
|
|
527
|
+
### 15.4 真实现场处置(`AEIS/data/mdcg`)
|
|
528
|
+
|
|
529
|
+
常驻 MCP 进程加载的仍是旧代码(`op=ref` 只认 `read|check|stat`),故按**工作纪律第7条**走等价兜底:python 直调同一模块(身份从 CodeBuddy `mcp.json` 读取,不落盘、不回显令牌)。
|
|
530
|
+
|
|
531
|
+
| 步骤 | 命令 | 实测 |
|
|
532
|
+
|---|---|---|
|
|
533
|
+
| 巡检 | `op=ref action=check max_nodes=20000` | `checked=5456`、`dangling=20`、`stale=42`、`truncated=false` |
|
|
534
|
+
| 预演 | `action=prune_dangling dry_run=true` | `scanned=10136`、`candidates=20`、`count=20` |
|
|
535
|
+
| 清退 | `action=prune_dangling`(designer 身份,`can_admin=true`) | `count=20`,全部软删进 `trash/` |
|
|
536
|
+
| 复核 | 同 `dry_run` 再跑 + 落盘核对 | `candidates=0`;20 个 `node_id` **`LIVE_HITS=0` / `TRASH_HITS=20`**;`trash/` 与 `_deletions.jsonl` 均 36 条 |
|
|
537
|
+
|
|
538
|
+
悬空来源单一:4 个已删的临时脚本(`_tmp_v3check.py`×2、`cmp_rows.py`×4、`dump_rows.py`×2、`vision.py`×12),非业务记忆,清退无信息损失且可 `op=restore` 回滚。
|
|
539
|
+
|
|
540
|
+
### 15.5 诚实边界(R3.1 新增)
|
|
541
|
+
|
|
542
|
+
1. **对账是 O(带标签节点数) 次读盘**:`ref` 不在索引快照里,只能逐节点 `cg.get()`。每次 `index_code`/`index_doc` 都会带上这一遍开销(本仓 ~500 节点级、现场 10k 节点级;后者秒级但非零成本)。若成热点,正解是把 `ref` 摘要进 `_scan_nodes` 快照,而非放松判定。
|
|
543
|
+
2. **只处置「孤儿」与「悬空」,不处置 `stale`**:漂移(源已改)用重建解决(重跑索引或 `sustain.heal`),不靠清退——删掉再索引等于绕远路。
|
|
544
|
+
3. **`stale` 与「孤儿」判据不同**:孤儿靠 `node_id` 重算比对(结构性),不看内容哈希;内容已变但 id 未变的节点不属孤儿。
|
|
545
|
+
4. **本仓 `test_p27` §9 的计数断言曾在本机为红**(`files=500/2388`,唯一失败项,非 R3.1 改动引起):`.gitignore` 已整目录忽略的 `docs/experiments/`(实测 2358 个 md 于 `docs/experiments/md-cg-gate-probe/`)物理仍在盘上,把 `docs/` 从 30 个 md 撑到 2388,越过 `max_files=500`。**处置口径已裁定并落地 → 见 §15.7(R3.2)**:给索引器加**追加排除**,而非调大上限。裁后 §9 由 `500/2388` 转为 `30/30` 全绿。
|
|
546
|
+
|
|
547
|
+
### 15.6 回归(R3.1)
|
|
548
|
+
|
|
549
|
+
受影响面 7 模块:`test_p9_forget_protect` 37/0、`test_p14_sustain` 44/0、`test_p21_tokens` 31/0、`test_p26_refindex` 55/0、`test_p28_refcheck` 45/0、`test_p2_mcp` 63/0 **全绿**;`test_p27_docindex` **68/1**(当时唯一红项为 §15.5-4 的环境事实,非代码缺陷)。
|
|
550
|
+
|
|
551
|
+
### 15.7 R3.2:`skip_dirs` 追加排除(原 §15.5-4 的裁定落地)
|
|
552
|
+
|
|
553
|
+
**问题**:索引的「扫描范围」只有**下限**(`SKIP_DIRS` 硬编码)没有**上限**——`.gitignore` 已整目录忽略的实验产物物理仍留在盘上,就照样被扫。本仓 `docs/` 真实内容 30 个 md,被 `docs/experiments/` 的 2358 个撑到 2388,直接压爆 `max_files=500`。此前唯一的应对是「调大 `max_files`」,那是**掩盖**:上限调到 2500,下次再多一批产物同样爆。
|
|
554
|
+
|
|
555
|
+
**交付物**:
|
|
556
|
+
|
|
557
|
+
| 文件 | 动作 | 职责 |
|
|
558
|
+
|---|---|---|
|
|
559
|
+
| `md_cg/codeindex.py` | **扩** | `skip_matcher(skip_dirs)` → `(hit, rules)`;`index_dir(..., skip_dirs=None)` 在 `os.walk` 处**追加**剪枝;`stats` 增 `skip_dirs`(规则回显)/ `skipped_dirs`(实际排掉的目录) |
|
|
560
|
+
| `md_cg/docindex.py` | **扩** | 同签名同语义,复用 `codeindex.skip_matcher`(**唯一实现**,避免两条链路口径漂移) |
|
|
561
|
+
| `md_cg/refindex.py` | **扩** | `index_dir(..., skip_dirs=None)` 透传给底层 |
|
|
562
|
+
| `md_cg/mcp_server.py` | **扩** | `index_code`/`index_doc` 透传 `skip_dirs`;`_skip_dirs_report()` 把「排掉了什么」压成 `skipped_dirs` / `skipped_dirs_count` / `skip_dirs` 回报;schema 增 `skip_dirs` |
|
|
563
|
+
| `md_cg/test_p27_docindex.py` | **扩** | §9 改用 `skip_dirs=["experiments"]`;新增 §9b 专测匹配口径 / 只增不减 / 可审计 |
|
|
564
|
+
|
|
565
|
+
**三条设计裁定**:
|
|
566
|
+
|
|
567
|
+
1. **只增不减**:`skip_dirs` 在 `SKIP_DIRS` **同一处**生效,调用方只能追加,**不能关掉** `.git`/`.venv`/`node_modules` 这类内置保护——否则一次参数写错就能把版本库元数据索引进认知图。
|
|
568
|
+
2. **排除不许静默**(与「截断不许静默」同一纪律):命中目录写进 `stats["skipped_dirs"]`(就地追加、不依赖末尾汇总,所以**截断提前 return 时也带得走**),MCP 层再压成可读回报。调用方据此能把「源文件真的少了」与「被规则排掉了」区分开。
|
|
569
|
+
3. **两类匹配口径可混用**:含 `/` 按**相对 root 的路径**(`docs/experiments` 只排这一处);不含 `/` 按**目录名**(`experiments` 排任意层级同名目录)。反斜杠 / 首尾斜杠先归一——否则「规则传了却不生效」又是一次静默失配。默认 `None` 时 `hit is None`、走原路径,**行为与改造前逐字一致**(同 `fresh`/`on_file` 纪律)。
|
|
570
|
+
|
|
571
|
+
**实测**(`test_p27` §9 / §9b):
|
|
572
|
+
- 本仓 `docs/`:`files=30/30`、`truncated=false`,`skipped_dirs=['experiments']`(此前 `files=500/2388` 已截断)。
|
|
573
|
+
- 合成树(`keep.md` + 两层 `experiments/*` + `sub/experiments/y.md` + `node_modules/pkg/n.md`):不给规则 → `files=4`「内置排除照旧」;`skip_dirs=["experiments"]` → `files=1` 且 `skipped=['docs/experiments','sub/experiments']`(**同时证明 `node_modules` 仍被排,即只增不减**);`skip_dirs=["docs/experiments"]` → `files=2` 只排这一处;`skip_dirs=["docs\\experiments\\"]` → `files=2`(归一后仍生效)。
|
|
574
|
+
|
|
575
|
+
### 15.8 诚实边界(R3.2 新增)
|
|
576
|
+
|
|
577
|
+
1. **按路径的规则不跨 root**:`docs/experiments` 是「相对本次 root」的路径,换个 root 就不再匹配;跨仓复用规则要用不含 `/` 的目录名形式。
|
|
578
|
+
2. **同名目录一网打尽**:`skip_dirs=["experiments"]` 会排掉**任意层级**的同名目录。这正是它对 `2_ai` 那种多子项目树好用的原因,但也意味着「只想排一处」必须写全路径。
|
|
579
|
+
3. **排除只影响扫描,不清理既有节点**:已在库、但本次被排掉的目录里的节点**不会**因此消失(`prune_orphans` 的判定范围限于本次真正重切过的 path,见 §15.3-1)。若要清掉,需先不带 `skip_dirs` 跑一次让它们成为孤儿,或直接 `prune_dangling`。
|
|
580
|
+
4. **`max_files` 仍是安全上限而非目标值**:`skip_dirs` 解决「噪声挤占额度」,不解决「真实内容超过 500」——真超了应当**调大 `max_files`**(那是真实内容),而不是继续加排除。
|
|
581
|
+
|
|
582
|
+
### 15.9 回归(R3.2)
|
|
583
|
+
|
|
584
|
+
受影响面 10 模块**全绿**:`test_p2_mcp` 63/0、`test_p9_forget_protect` 37/0、`test_p14_sustain` 44/0、`test_p21_tokens` 31/0、`test_p26_refindex` 55/0、`test_p27_docindex` **75/0**、`test_p28_refcheck` 45/0、`test_p29_session_ingest_export` 48/0、`test_p30_maintain` 55/0、`test_p32_backfill` 58/0。改动文件 lint 0 错误。
|
|
585
|
+
|
|
586
|
+
---
|
|
587
|
+
|
|
588
|
+
*计划生成:2026-09-10 · 证据:§一 全部为源码行级核对结果 · 未核对项已单列 §1.3 / §十 · §十一 为 R0 实测取证 · §十二 为 R1 实测取证 · §十三 为 R2 实测取证 · §十四 为 R3 实测取证 · §十五 为 R3.1 / R3.2 实测取证*
|