@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
package/md_cg/mreview/govern.py
CHANGED
|
@@ -1,694 +1,695 @@
|
|
|
1
|
-
# -*- coding: utf-8 -*-
|
|
2
|
-
"""记忆评审流水线 · 级 4:M4 落库治理(确定性部分 · rule 驱动 · 永不删除)。
|
|
3
|
-
|
|
4
|
-
真源:`docs/记忆评审系统_立项设计与施工交接_20260915.md` §3 第 4 级 + §6 验收 + §7 负面清单。
|
|
5
|
-
本模块只做**机械可裁决**的治理动作(回填等);LLM 语义类(合并 / 降权)不在其列——
|
|
6
|
-
那属 M2 建议 + designer 终裁,本轮不落地。
|
|
7
|
-
|
|
8
|
-
**规则驱动,引擎不写死**:回填哪个字段、作用于哪些层,不写在本文件里,从 M1 规则库
|
|
9
|
-
(`rules/*.json`,数据真源)读——`R-ROLE-MISSING` 的 `matcher.layer` 定作用域、
|
|
10
|
-
`mechanical[check=field_absent].field` 定字段。改靶子 = 改数据,引擎冻结。
|
|
11
|
-
|
|
12
|
-
**来源链(只搬运已声明证据)**:
|
|
13
|
-
|
|
14
|
-
① `tags` `role:<值>` 标签——显式成文声明;
|
|
15
|
-
② `map` `frontmatter.writer` → role 的**声明映射表**(`WRITER_ROLE_MAP`)。
|
|
16
|
-
当前**故意为空**:库内无任何成文映射能把 writer(`MDCG_ACTOR`:
|
|
17
|
-
dsh-memory / codebuddy / zcode / designer-cli…)落到
|
|
18
|
-
`mdcos.ALL_ROLES`;`scripts/review_cli.py` 的
|
|
19
|
-
`Principal(role="designer")` 属**权限角色**(`tokens.ROLE_SPECS`),
|
|
20
|
-
与节点 role 是两套词汇,混填即污染域。故留空 + 通道保留,待终裁补表;
|
|
21
|
-
③ `layer_default` 层默认投影——真源 = `mdcos.MdCGOS.add` 成文语义「默认 role=None
|
|
22
|
-
(知识)」,即缺省来源 = knowledge。该档是「隐含默认的显式化」,
|
|
23
|
-
断言强度高于 ①②,故单独分档上报(`by_source`);只认显式成文声明时
|
|
24
|
-
用 `sources=("tags","map")` 关掉它。
|
|
25
|
-
|
|
26
|
-
**域封闭**:写入值必须 ∈ `mdcos.ALL_ROLES`;域外值一律不写,记 `*_out_of_domain` 且
|
|
27
|
-
**停止降级**(显式声明了域外值 ≠ 无声明,回退到默认档会把待裁决冲突悄悄抹平)。
|
|
28
|
-
`tool-output/command/edit` 属工作角色(`WORK_ROLES`,默认不参与正排,见
|
|
29
|
-
`mdcos._candidates`)。故 ② `map` 档**显式拒绝**工作角色(命中即
|
|
30
|
-
`map_role_is_work_role`,不降级)——映射表是本引擎的推断,把知识节点推成工作角色
|
|
31
|
-
等于亲手把它从默认召回里摘掉;① `tags` 档是**写入者成文声明**,予以尊重(治理层
|
|
32
|
-
不替作者改主意)。
|
|
33
|
-
|
|
34
|
-
**五纪律**(对齐 `backfill`):不猜测(无来源 → `unfillable` + 原因分布,绝不编造)/
|
|
35
|
-
可预演(`role_plan` 只出报表,`role_apply` 写前重查,已有值计 `skipped_drift`)/
|
|
36
|
-
可留痕(`_govern.jsonl`:前后值 / 依据 / 来源档 / 批次 / 操作者 / 规则 id)/
|
|
37
|
-
可回滚(只在当前值 == 写入值时撤销,否则 `conflict`;原本无键 → 删键还原)/
|
|
38
|
-
fail-closed(密文与不可读节点跳过,绝不解密回写;靶规则缺失抛错)。
|
|
39
|
-
§7 负面清单:只写 frontmatter 一个字段,不动正文、永不删除。
|
|
40
|
-
"""
|
|
41
|
-
from __future__ import annotations
|
|
42
|
-
|
|
43
|
-
import argparse
|
|
44
|
-
import json
|
|
45
|
-
import os
|
|
46
|
-
import sys
|
|
47
|
-
import time
|
|
48
|
-
|
|
49
|
-
from .. import crypto
|
|
50
|
-
from ..backfill import _as_cg, _entry_id, _readable_guard, _sha
|
|
51
|
-
from ..fsutil import append_jsonl, read_jsonl
|
|
52
|
-
from ..mdcos import ALL_ROLES, WORK_ROLES
|
|
53
|
-
from .ruleset import _as_rules, _blank
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
#:
|
|
69
|
-
#:
|
|
70
|
-
#:
|
|
71
|
-
#:
|
|
72
|
-
#:
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
#:
|
|
77
|
-
#:
|
|
78
|
-
#:
|
|
79
|
-
#:
|
|
80
|
-
#:
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
"
|
|
84
|
-
"
|
|
85
|
-
"
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
#:
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
`
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
if
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
"
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
if
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
#
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
"
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
rm
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
"
|
|
268
|
-
"
|
|
269
|
-
"
|
|
270
|
-
"
|
|
271
|
-
"
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
item["
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
"
|
|
333
|
-
"
|
|
334
|
-
"
|
|
335
|
-
"
|
|
336
|
-
"
|
|
337
|
-
"
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
fm
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
"
|
|
366
|
-
"
|
|
367
|
-
"
|
|
368
|
-
"
|
|
369
|
-
"
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
rep["
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
"
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
"
|
|
427
|
-
"
|
|
428
|
-
"
|
|
429
|
-
"
|
|
430
|
-
|
|
431
|
-
rep["
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
"
|
|
475
|
-
"
|
|
476
|
-
"
|
|
477
|
-
"
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
#
|
|
498
|
-
#
|
|
499
|
-
#
|
|
500
|
-
#
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
if
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
ap
|
|
520
|
-
ap.add_argument("--
|
|
521
|
-
ap.add_argument("--
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
p.add_argument("--
|
|
529
|
-
p.add_argument("--
|
|
530
|
-
p.add_argument("--
|
|
531
|
-
p.add_argument("--
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
"
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
p
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
p
|
|
569
|
-
p.add_argument("--
|
|
570
|
-
p.add_argument("--
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
p.
|
|
576
|
-
p.add_argument("--
|
|
577
|
-
p.add_argument("--
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
p.
|
|
581
|
-
p.add_argument("--
|
|
582
|
-
p.add_argument("--
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
#
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
print("
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
rep["
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
print("
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
r.get("
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
print("
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
print("
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""记忆评审流水线 · 级 4:M4 落库治理(确定性部分 · rule 驱动 · 永不删除)。
|
|
3
|
+
|
|
4
|
+
真源:`docs/记忆评审系统_立项设计与施工交接_20260915.md` §3 第 4 级 + §6 验收 + §7 负面清单。
|
|
5
|
+
本模块只做**机械可裁决**的治理动作(回填等);LLM 语义类(合并 / 降权)不在其列——
|
|
6
|
+
那属 M2 建议 + designer 终裁,本轮不落地。
|
|
7
|
+
|
|
8
|
+
**规则驱动,引擎不写死**:回填哪个字段、作用于哪些层,不写在本文件里,从 M1 规则库
|
|
9
|
+
(`rules/*.json`,数据真源)读——`R-ROLE-MISSING` 的 `matcher.layer` 定作用域、
|
|
10
|
+
`mechanical[check=field_absent].field` 定字段。改靶子 = 改数据,引擎冻结。
|
|
11
|
+
|
|
12
|
+
**来源链(只搬运已声明证据)**:
|
|
13
|
+
|
|
14
|
+
① `tags` `role:<值>` 标签——显式成文声明;
|
|
15
|
+
② `map` `frontmatter.writer` → role 的**声明映射表**(`WRITER_ROLE_MAP`)。
|
|
16
|
+
当前**故意为空**:库内无任何成文映射能把 writer(`MDCG_ACTOR`:
|
|
17
|
+
dsh-memory / codebuddy / zcode / designer-cli…)落到
|
|
18
|
+
`mdcos.ALL_ROLES`;`scripts/review_cli.py` 的
|
|
19
|
+
`Principal(role="designer")` 属**权限角色**(`tokens.ROLE_SPECS`),
|
|
20
|
+
与节点 role 是两套词汇,混填即污染域。故留空 + 通道保留,待终裁补表;
|
|
21
|
+
③ `layer_default` 层默认投影——真源 = `mdcos.MdCGOS.add` 成文语义「默认 role=None
|
|
22
|
+
(知识)」,即缺省来源 = knowledge。该档是「隐含默认的显式化」,
|
|
23
|
+
断言强度高于 ①②,故单独分档上报(`by_source`);只认显式成文声明时
|
|
24
|
+
用 `sources=("tags","map")` 关掉它。
|
|
25
|
+
|
|
26
|
+
**域封闭**:写入值必须 ∈ `mdcos.ALL_ROLES`;域外值一律不写,记 `*_out_of_domain` 且
|
|
27
|
+
**停止降级**(显式声明了域外值 ≠ 无声明,回退到默认档会把待裁决冲突悄悄抹平)。
|
|
28
|
+
`tool-output/command/edit` 属工作角色(`WORK_ROLES`,默认不参与正排,见
|
|
29
|
+
`mdcos._candidates`)。故 ② `map` 档**显式拒绝**工作角色(命中即
|
|
30
|
+
`map_role_is_work_role`,不降级)——映射表是本引擎的推断,把知识节点推成工作角色
|
|
31
|
+
等于亲手把它从默认召回里摘掉;① `tags` 档是**写入者成文声明**,予以尊重(治理层
|
|
32
|
+
不替作者改主意)。
|
|
33
|
+
|
|
34
|
+
**五纪律**(对齐 `backfill`):不猜测(无来源 → `unfillable` + 原因分布,绝不编造)/
|
|
35
|
+
可预演(`role_plan` 只出报表,`role_apply` 写前重查,已有值计 `skipped_drift`)/
|
|
36
|
+
可留痕(`_govern.jsonl`:前后值 / 依据 / 来源档 / 批次 / 操作者 / 规则 id)/
|
|
37
|
+
可回滚(只在当前值 == 写入值时撤销,否则 `conflict`;原本无键 → 删键还原)/
|
|
38
|
+
fail-closed(密文与不可读节点跳过,绝不解密回写;靶规则缺失抛错)。
|
|
39
|
+
§7 负面清单:只写 frontmatter 一个字段,不动正文、永不删除。
|
|
40
|
+
"""
|
|
41
|
+
from __future__ import annotations
|
|
42
|
+
|
|
43
|
+
import argparse
|
|
44
|
+
import json
|
|
45
|
+
import os
|
|
46
|
+
import sys
|
|
47
|
+
import time
|
|
48
|
+
|
|
49
|
+
from .. import crypto
|
|
50
|
+
from ..backfill import _as_cg, _entry_id, _readable_guard, _sha
|
|
51
|
+
from ..fsutil import append_jsonl, read_jsonl
|
|
52
|
+
from ..mdcos import ALL_ROLES, WORK_ROLES
|
|
53
|
+
from .ruleset import _as_rules, _blank
|
|
54
|
+
from ..readcache import direct_read
|
|
55
|
+
|
|
56
|
+
#: 留痕文件名(与 `backfill._backfill.jsonl` 分立:治理动作须能独立审计)
|
|
57
|
+
GOVERN_LOG = "_govern.jsonl"
|
|
58
|
+
|
|
59
|
+
#: 默认靶规则;`field_absent` 检查名(与 `ruleset.MECH_CHECKS` 同名)
|
|
60
|
+
RULE_ROLE = "R-ROLE-MISSING"
|
|
61
|
+
CHECK_FIELD_ABSENT = "field_absent"
|
|
62
|
+
|
|
63
|
+
#: 来源档
|
|
64
|
+
SOURCE_TAGS = "tags"
|
|
65
|
+
SOURCE_MAP = "map"
|
|
66
|
+
SOURCE_LAYER = "layer_default"
|
|
67
|
+
ALL_SOURCES = (SOURCE_TAGS, SOURCE_MAP, SOURCE_LAYER)
|
|
68
|
+
#: 默认只开「成文声明」两档;③ `layer_default` **必须显式开启**。
|
|
69
|
+
#: 依据(2026-09-16 真实库实测):writer/tags 面无任何成文声明,故默认档在真实库上
|
|
70
|
+
#: 产出 `targeted=0 / unfillable=11120`(原因逐一列出)——这是**诚实结论**,而不是
|
|
71
|
+
#: 用 ③ 把指标刷绿:role"覆盖≥90%"若由常量填满即失去判据意义,且 ③ 会关掉 M1 的
|
|
72
|
+
#: `field_absent`,使 M2 的 `role_inferable` 语义判定**永不发生**——「字段有值」冒充
|
|
73
|
+
#: 「来源已知」。终裁开启前请先读 `LAYER_DEFAULT_WARNING`。
|
|
74
|
+
DEFAULT_SOURCES = (SOURCE_TAGS, SOURCE_MAP)
|
|
75
|
+
|
|
76
|
+
#: 开启 ③ `layer_default` 前**必读**的终裁提示(`role_plan`/CLI 在 `sources` 含 ③ 时透出)。
|
|
77
|
+
#: 它填的是**常量**而非**来源**:`role_ratio_kn` 会立刻跳到 100%(层内几乎全是 knowledge),
|
|
78
|
+
#: 但 ① M1 的 `R-ROLE-MISSING`(`field_absent`)随之永不命中——规则失去发现能力;
|
|
79
|
+
#: ② M2 的 `role_inferable` 语义判定(LLM 从 writer/tags/正文判 role)永无输入——
|
|
80
|
+
#: 即以「字段有值」冒充「来源已知」。③ 若 writer 有声明而库内无法映射(当前真实库
|
|
81
|
+
#: 全部如此),本档会把该冲突抹平成「已治理」,掩盖待裁决项。
|
|
82
|
+
LAYER_DEFAULT_WARNING = (
|
|
83
|
+
"开启 layer_default 是把『来源未知』填成常量 knowledge:role 覆盖率立刻达标,"
|
|
84
|
+
"但 M1 field_absent 与 M2 role_inferable 同时失效(字段有值冒充来源已知),"
|
|
85
|
+
"并以层默认抹平 writer→role 待裁决冲突。须 designer 终裁、显式传 "
|
|
86
|
+
"sources=tags,map,layer_default、留痕署名后方可使用。")
|
|
87
|
+
|
|
88
|
+
#: 标签前缀:`role:<值>` 为成文 role 声明
|
|
89
|
+
ROLE_TAG_PREFIX = "role:"
|
|
90
|
+
|
|
91
|
+
#: `writer` → role 的**声明映射表**(见模块 docstring ②:当前故意为空)
|
|
92
|
+
WRITER_ROLE_MAP: dict = {}
|
|
93
|
+
|
|
94
|
+
#: 层默认投影表:真源 = `mdcos.MdCGOS.add` docstring「默认 role=None(知识)」。
|
|
95
|
+
#: 只有层名本身就在 `ALL_ROLES` 里时才投影(knowledge 是唯一的一个)。
|
|
96
|
+
LAYER_DEFAULT_ROLE = {"knowledge": "knowledge"}
|
|
97
|
+
|
|
98
|
+
BASIS_TAG = "frontmatter.tags(role:<值> 显式声明)"
|
|
99
|
+
BASIS_LAYER = "mdcos.MdCGOS.add 默认语义(role=None ⇒ knowledge)"
|
|
100
|
+
|
|
101
|
+
REASON_NO_SOURCE = "no_source_declared"
|
|
102
|
+
REASON_TAG_OOD = "tag_role_out_of_domain"
|
|
103
|
+
REASON_MAP_OOD = "map_role_out_of_domain"
|
|
104
|
+
REASON_MAP_WORK = "map_role_is_work_role"
|
|
105
|
+
REASON_WRITER_NO_MAP = "writer_no_mapping"
|
|
106
|
+
|
|
107
|
+
ACTION_ROLE = "role"
|
|
108
|
+
ACTION_ROLE_ROLLBACK = "role_rollback"
|
|
109
|
+
BATCH_DEFAULT = "m4-role"
|
|
110
|
+
|
|
111
|
+
ACTIONS = ("role", "role_rollback", "role_history", "role_stats")
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
# ---- 通用工具 ------------------------------------------------------------
|
|
115
|
+
|
|
116
|
+
# 生效条件:传入 cg 即返回 os.path.join(cg.root, GOVERN_LOG),无分支。
|
|
117
|
+
def _log_path(cg) -> str:
|
|
118
|
+
return os.path.join(cg.root, GOVERN_LOG)
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
# 生效条件:fm 为假值(None/{})时按 {} 处理,其 .get("tags") 为假值(None/空串/空列表)时回落 [],否则对该值逐项过滤,只保留 isinstance(t, str) 的元素。
|
|
122
|
+
def _tags(fm: dict) -> list:
|
|
123
|
+
return [t for t in ((fm or {}).get("tags") or []) if isinstance(t, str)]
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
# 生效条件:detail 为假值(None/空串)时返回 kind,否则返回 "%s:%s" % (kind, detail)。
|
|
127
|
+
def _reason_key(kind: str, detail: str = None) -> str:
|
|
128
|
+
return kind if not detail else "%s:%s" % (kind, detail)
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
# 生效条件:box 缺 key 时以 box.get(key, 0) 取 0 再加 1 写回 box[key];key 已存在(含值为非数值)时直接对现值 +1。
|
|
132
|
+
def _bump(box: dict, key: str) -> None:
|
|
133
|
+
box[key] = box.get(key, 0) + 1
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
# ---- 靶子定位(rule 驱动)------------------------------------------------
|
|
137
|
+
|
|
138
|
+
# 生效条件:rules/rules_dir 经 _as_rules 得到的列表中存在 id == rule_id 的项、且该项 matcher.layer 非空、mechanical 中存在 check == CHECK_FIELD_ABSENT 且 field 为真的项时,返回 {'rule_id','title','field','layers','severity','remedy','llm'};该项不存在或 layers/field 为空则抛 ValueError。
|
|
139
|
+
def role_rule(*, rules=None, rules_dir=None, rule_id: str = RULE_ROLE) -> dict:
|
|
140
|
+
"""从 M1 规则库取「role 回填」靶规则 → `{field, layers, remedy, …}`。
|
|
141
|
+
|
|
142
|
+
规则库是靶子的**唯一真源**:缺规则 / 缺 `matcher.layer` / 缺
|
|
143
|
+
`field_absent.field` 一律抛 ValueError(fail-closed)——规则消失不是
|
|
144
|
+
「没有要治理的东西」,而是治理靶子失效,必须显式红灯而非静默空跑。
|
|
145
|
+
"""
|
|
146
|
+
rl = _as_rules(rules, rules_dir)
|
|
147
|
+
hit = next((r for r in rl if r.get("id") == rule_id), None)
|
|
148
|
+
if hit is None:
|
|
149
|
+
raise ValueError("规则库无 %s(可用:%s)"
|
|
150
|
+
% (rule_id, [r.get("id") for r in rl]))
|
|
151
|
+
layers = list((hit.get("matcher") or {}).get("layer") or [])
|
|
152
|
+
field = None
|
|
153
|
+
for spec in hit.get("mechanical") or []:
|
|
154
|
+
if spec.get("check") == CHECK_FIELD_ABSENT and spec.get("field"):
|
|
155
|
+
field = spec["field"]
|
|
156
|
+
break
|
|
157
|
+
if not layers or not field:
|
|
158
|
+
raise ValueError("规则 %s 未声明 matcher.layer / %s.field——无法定位回填靶"
|
|
159
|
+
% (rule_id, CHECK_FIELD_ABSENT))
|
|
160
|
+
return {"rule_id": rule_id, "title": hit.get("title"), "field": field,
|
|
161
|
+
"layers": layers, "severity": hit.get("severity"),
|
|
162
|
+
"remedy": hit.get("remedy"), "llm": list(hit.get("llm") or [])}
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
# ---- 取值推导(唯一入口:只搬运已声明的证据)------------------------------
|
|
166
|
+
|
|
167
|
+
# 生效条件:按 sources 逐档判定——含 SOURCE_TAGS 时取 fm 的 tags 内首个 ROLE_TAG_PREFIX 前缀标签,其值属 ALL_ROLES 则返回 (值, BASIS_TAG, SOURCE_TAGS, None),值域外则返回 (None,None,None, REASON_TAG_OOD 原因);tags 未定出时含 SOURCE_MAP 则以小写 writer 取 role_map 值,属 WORK_ROLES 返回 REASON_MAP_WORK 原因、属 ALL_ROLES 返回 (值,"WRITER_ROLE_MAP[writer]",SOURCE_MAP,None)、否则返回 REASON_MAP_OOD 原因(命中域外值即返回、不降级);再否且含 SOURCE_LAYER 且 LAYER_DEFAULT_ROLE 命中 e["layer"] 时返回 (值, BASIS_LAYER, SOURCE_LAYER, None);全部未命中则 writer 非空返回 REASON_WRITER_NO_MAP:writer,writer 为空返回 REASON_NO_SOURCE。
|
|
168
|
+
def _candidate_role(e, fm, *, sources, role_map) -> tuple:
|
|
169
|
+
"""→ `(role, basis, source, reason)`:有来源则 reason=None;无来源则 role=None。
|
|
170
|
+
|
|
171
|
+
按声明强度优先:`tags`(成文)> `map`(声明表)> `layer_default`(隐含默认显式化)。
|
|
172
|
+
命中**域外值**时立即返回且**不降级**:显式声明了域外值 ≠ 无声明——继续回退到更弱的
|
|
173
|
+
来源会把一个待 designer 裁决的冲突悄悄抹平。
|
|
174
|
+
"""
|
|
175
|
+
if SOURCE_TAGS in sources:
|
|
176
|
+
for t in _tags(fm):
|
|
177
|
+
if t.startswith(ROLE_TAG_PREFIX):
|
|
178
|
+
v = t[len(ROLE_TAG_PREFIX):].strip().lower()
|
|
179
|
+
if v in ALL_ROLES:
|
|
180
|
+
return v, BASIS_TAG, SOURCE_TAGS, None
|
|
181
|
+
return None, None, None, _reason_key(REASON_TAG_OOD, v or "空")
|
|
182
|
+
if SOURCE_MAP in sources:
|
|
183
|
+
w = str(fm.get("writer") or "").strip().lower()
|
|
184
|
+
v = str((role_map or {}).get(w) or "").strip().lower() if w else ""
|
|
185
|
+
if v:
|
|
186
|
+
if v in WORK_ROLES:
|
|
187
|
+
# 映射表是**我们的推断**:把 knowledge 层节点推成工作角色会让它在默认
|
|
188
|
+
# 召回中被剔除(`_candidates` 默认排除 WORK_ROLES)——故意不做,红灯。
|
|
189
|
+
return None, None, None, _reason_key(REASON_MAP_WORK, v)
|
|
190
|
+
if v in ALL_ROLES:
|
|
191
|
+
return v, "WRITER_ROLE_MAP[%s]" % w, SOURCE_MAP, None
|
|
192
|
+
return None, None, None, _reason_key(REASON_MAP_OOD, v)
|
|
193
|
+
if SOURCE_LAYER in sources:
|
|
194
|
+
v = LAYER_DEFAULT_ROLE.get(str(e.get("layer") or ""))
|
|
195
|
+
if v:
|
|
196
|
+
return v, BASIS_LAYER, SOURCE_LAYER, None
|
|
197
|
+
w = str(fm.get("writer") or "").strip()
|
|
198
|
+
return None, None, None, (_reason_key(REASON_WRITER_NO_MAP, w) if w
|
|
199
|
+
else REASON_NO_SOURCE)
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
# 生效条件:按序判定——_readable_guard(cg, e) 为假返回 ("denied", None);direct_read(cg, e) 的 fm 为 None 返回 ("unreadable", None);crypto.is_encrypted(content) 为真返回 ("locked", None);fm.get("role") 非空白返回 ("present", None);否则 _candidate_role(e, fm, sources=sources, role_map=role_map) 的 role 为假返回 ("unfillable", {id,layer,reason,writer}),role 为真返回 ("", {id,layer,role,source,basis,before,had_key,writer})。
|
|
203
|
+
def _classify(cg, e, nid, *, sources, role_map) -> tuple:
|
|
204
|
+
"""单条裁决 → `(skip_reason, item|gap)`;skip_reason 为空串表示可回填。"""
|
|
205
|
+
if not _readable_guard(cg, e):
|
|
206
|
+
return "denied", None
|
|
207
|
+
fm, content = direct_read(cg, e)
|
|
208
|
+
if fm is None:
|
|
209
|
+
return "unreadable", None
|
|
210
|
+
if crypto.is_encrypted(content):
|
|
211
|
+
return "locked", None
|
|
212
|
+
if not _blank(fm.get("role")):
|
|
213
|
+
return "present", None
|
|
214
|
+
role, basis, src, reason = _candidate_role(e, fm, sources=sources,
|
|
215
|
+
role_map=role_map)
|
|
216
|
+
if not role:
|
|
217
|
+
return "unfillable", {"id": nid, "layer": e.get("layer"),
|
|
218
|
+
"reason": reason, "writer": fm.get("writer")}
|
|
219
|
+
return "", {"id": nid, "layer": e.get("layer"), "role": role, "source": src,
|
|
220
|
+
"basis": basis, "before": fm.get("role"),
|
|
221
|
+
"had_key": "role" in fm, "writer": fm.get("writer")}
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
# 生效条件:want 由必填 layers 决定;layer 为真值时须属 layers 否则抛 ValueError,且命中后 want 收窄为 {layer};ids 为真值时只保留白名单内 nid、为假值(None/[])时不过滤;prefix 为真值时只保留 nid 以之开头者;仅 str(e.get("layer")) 属 want 的条目按 nid 升序进入返回列表。
|
|
225
|
+
def _iter_scope(cg, *, layers, layer=None, prefix=None, ids=None) -> list:
|
|
226
|
+
"""按规则作用域遍历索引条目 → `[(nid, entry)]`(只读、nid 稳定序)。
|
|
227
|
+
|
|
228
|
+
`layer` 收窄只能收窄到规则已声明的层内——跨层即抛错(同 `ruleset` 的
|
|
229
|
+
「执行时按同一条件过滤,绝不跨层套用」)。
|
|
230
|
+
"""
|
|
231
|
+
want = set(layers)
|
|
232
|
+
if layer:
|
|
233
|
+
if layer not in want:
|
|
234
|
+
raise ValueError("layer=%s 不在靶规则作用域 %s 内(绝不跨层套用)"
|
|
235
|
+
% (layer, sorted(want)))
|
|
236
|
+
want = {layer}
|
|
237
|
+
idw = set(ids) if ids else None
|
|
238
|
+
out = []
|
|
239
|
+
for nid in sorted((cg.index.get("nodes") or {})):
|
|
240
|
+
if idw is not None and nid not in idw:
|
|
241
|
+
continue
|
|
242
|
+
if prefix and not nid.startswith(prefix):
|
|
243
|
+
continue
|
|
244
|
+
e = cg.index["nodes"][nid]
|
|
245
|
+
if str(e.get("layer")) not in want:
|
|
246
|
+
continue
|
|
247
|
+
out.append((nid, e))
|
|
248
|
+
return out
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
# ---- 预演 -----------------------------------------------------------------
|
|
252
|
+
|
|
253
|
+
# 生效条件:以 x 经 _as_cg 得 cg 并先经 role_rule(rules,rules_dir,rule_id) 取规则(缺规则/缺 layer/缺 field 时该步抛 ValueError),role_map 非空时的条目以小写去空白键合并进 WRITER_ROLE_MAP 副本,再遍历 _iter_scope(cg, layers=rule["layers"], layer=layer, prefix=prefix, ids=ids) 逐条 _classify 后返回不写盘的 rep;每条 item 仅在 limit is None 或 len(rep["items"]) < limit 时追加(limit=0 时 items 为空但 targeted 仍累加),sample 为真且 planned_ids 非空时以 max(1, len(planned_ids)//int(sample)) 为步长取前 int(sample) 项。
|
|
254
|
+
def role_plan(x, layer=None, limit=None, ids=None, prefix=None, *,
|
|
255
|
+
sources=DEFAULT_SOURCES, role_map=None, rule_id=RULE_ROLE,
|
|
256
|
+
rules=None, rules_dir=None, sample=0) -> dict:
|
|
257
|
+
"""预演:产出可回填清单 + 不可回填原因分布,**不写盘**。
|
|
258
|
+
|
|
259
|
+
`sources` 见模块 docstring 的三档来源;`role_map` 覆盖 `WRITER_ROLE_MAP`
|
|
260
|
+
(designer 终裁补声明表的入口,不改库)。
|
|
261
|
+
"""
|
|
262
|
+
cg = _as_cg(x)
|
|
263
|
+
rule = role_rule(rules=rules, rules_dir=rules_dir, rule_id=rule_id)
|
|
264
|
+
rm = dict(WRITER_ROLE_MAP)
|
|
265
|
+
rm.update({str(k).strip().lower(): v for k, v in (role_map or {}).items()})
|
|
266
|
+
rep = {"root": cg.root, "dry_run": True, "action": ACTION_ROLE,
|
|
267
|
+
"rule": rule, "sources": list(sources), "role_map": rm,
|
|
268
|
+
"layer": layer, "prefix": prefix,
|
|
269
|
+
"nodes_scanned": 0, "skipped_out_of_scope": 0,
|
|
270
|
+
"skipped_locked": 0, "skipped_denied": 0, "skipped_unreadable": 0,
|
|
271
|
+
"skipped_present": 0, "unfillable": 0, "unfillable_by_reason": {},
|
|
272
|
+
"targeted": 0, "by_source": {}, "items": []}
|
|
273
|
+
scope_layers = set(rule["layers"])
|
|
274
|
+
idw = set(ids) if ids else None
|
|
275
|
+
for nid in sorted((cg.index.get("nodes") or {})):
|
|
276
|
+
if idw is not None and nid not in idw:
|
|
277
|
+
continue
|
|
278
|
+
if prefix and not str(nid).startswith(prefix):
|
|
279
|
+
continue
|
|
280
|
+
e = cg.index["nodes"][nid]
|
|
281
|
+
if str(e.get("layer")) not in scope_layers:
|
|
282
|
+
rep["skipped_out_of_scope"] += 1
|
|
283
|
+
for nid, e in _iter_scope(cg, layers=rule["layers"], layer=layer,
|
|
284
|
+
prefix=prefix, ids=ids):
|
|
285
|
+
rep["nodes_scanned"] += 1
|
|
286
|
+
skip, item = _classify(cg, e, nid, sources=sources, role_map=rm)
|
|
287
|
+
if skip == "unfillable":
|
|
288
|
+
rep["unfillable"] += 1
|
|
289
|
+
# 原因键带明细(如 writer_no_mapping:dsh-memory)——聚合即得逐来源分布
|
|
290
|
+
_bump(rep["unfillable_by_reason"], str(item["reason"]))
|
|
291
|
+
continue
|
|
292
|
+
if skip:
|
|
293
|
+
rep["skipped_%s" % skip] = rep.get("skipped_%s" % skip, 0) + 1
|
|
294
|
+
continue
|
|
295
|
+
item["entry_id"] = _entry_id(ACTION_ROLE, nid)
|
|
296
|
+
item["ref"] = e.get("ref")
|
|
297
|
+
rep["targeted"] += 1
|
|
298
|
+
_bump(rep["by_source"], item["source"])
|
|
299
|
+
if limit is None or len(rep["items"]) < limit:
|
|
300
|
+
rep["items"].append(item)
|
|
301
|
+
rep["planned_ids"] = [i["id"] for i in rep["items"]]
|
|
302
|
+
if sample and rep["planned_ids"]:
|
|
303
|
+
step = max(1, len(rep["planned_ids"]) // int(sample))
|
|
304
|
+
rep["sample"] = rep["planned_ids"][::step][:int(sample)]
|
|
305
|
+
rep["rule_layers"] = sorted(scope_layers)
|
|
306
|
+
return rep
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
# ---- 执行 / 回滚 / 留痕 / 对照 -------------------------------------------
|
|
310
|
+
|
|
311
|
+
# 生效条件:x 经 _as_cg,batch 为假值时回落 BATCH_DEFAULT;先调 role_plan 取 items,entry_ids 为真值时按 entry_id 收窄;逐项处理:索引无该节点或 _read 得 fm 为 None → skipped_missing,_readable_guard 为假 → skipped_denied,crypto.is_encrypted(content) 为真 → skipped_locked,fm["role"] 非空 → skipped_drift,否则写回 role 并 append_jsonl 留痕、written 递增;written 非零时 cg.rebuild_index(),返回含 plan_remaining 的 rep。
|
|
312
|
+
def role_apply(x, ids=None, entry_ids=None, layer=None, limit=None,
|
|
313
|
+
batch=BATCH_DEFAULT, sources=DEFAULT_SOURCES, role_map=None,
|
|
314
|
+
rule_id=RULE_ROLE, rules=None, rules_dir=None, actor=None,
|
|
315
|
+
prefix=None) -> dict:
|
|
316
|
+
"""执行回填:逐节点改写 md 的 role 字段,写 `_govern.jsonl` 留痕。
|
|
317
|
+
|
|
318
|
+
写前**重查**一次(预演 → 执行之间节点可能被改动):已有 role 值 → `skipped_drift`
|
|
319
|
+
一律不动;密文 / 不可读 → `skipped_locked` / `skipped_missing`,**绝不解密回写**。
|
|
320
|
+
`ids` / `prefix` / `entry_ids` 三重收窄,`entry_ids` 优先(工单式精确执行)。
|
|
321
|
+
"""
|
|
322
|
+
cg = _as_cg(x)
|
|
323
|
+
batch = batch or BATCH_DEFAULT
|
|
324
|
+
p = role_plan(cg, layer=layer, limit=limit, ids=ids, prefix=prefix,
|
|
325
|
+
sources=sources, role_map=role_map, rule_id=rule_id,
|
|
326
|
+
rules=rules, rules_dir=rules_dir)
|
|
327
|
+
items = p["items"]
|
|
328
|
+
if entry_ids:
|
|
329
|
+
want = set(entry_ids)
|
|
330
|
+
items = [i for i in items if i["entry_id"] in want]
|
|
331
|
+
rep = {"root": cg.root, "dry_run": False, "action": ACTION_ROLE,
|
|
332
|
+
"batch": batch, "actor": actor, "rule_id": rule_id,
|
|
333
|
+
"field": p["rule"]["field"], "sources": list(sources),
|
|
334
|
+
"planned": len(items), "written": 0, "by_source": {},
|
|
335
|
+
"skipped_drift": 0, "skipped_locked": 0, "skipped_missing": 0,
|
|
336
|
+
"skipped_denied": 0, "entry_ids": [],
|
|
337
|
+
"unfillable": p["unfillable"],
|
|
338
|
+
"unfillable_by_reason": p["unfillable_by_reason"]}
|
|
339
|
+
for it in items:
|
|
340
|
+
nid = it["id"]
|
|
341
|
+
e = (cg.index.get("nodes") or {}).get(nid)
|
|
342
|
+
if e is None:
|
|
343
|
+
rep["skipped_missing"] += 1
|
|
344
|
+
continue
|
|
345
|
+
if not _readable_guard(cg, e):
|
|
346
|
+
rep["skipped_denied"] += 1
|
|
347
|
+
continue
|
|
348
|
+
fm, content = direct_read(cg, e)
|
|
349
|
+
if fm is None:
|
|
350
|
+
rep["skipped_missing"] += 1
|
|
351
|
+
continue
|
|
352
|
+
if crypto.is_encrypted(content):
|
|
353
|
+
rep["skipped_locked"] += 1
|
|
354
|
+
continue
|
|
355
|
+
if not _blank(fm.get("role")):
|
|
356
|
+
rep["skipped_drift"] += 1
|
|
357
|
+
continue
|
|
358
|
+
fm_before = {"role": fm.get("role"), "had_key": "role" in fm}
|
|
359
|
+
fm["role"] = it["role"]
|
|
360
|
+
ts = time.time()
|
|
361
|
+
cg._write_node(nid, os.path.join(cg.root, e["path"]), fm, content,
|
|
362
|
+
durable=True)
|
|
363
|
+
wid = _sha("%s|%s|%.6f" % (nid, batch, ts))
|
|
364
|
+
append_jsonl(_log_path(cg), {
|
|
365
|
+
"action": ACTION_ROLE, "ts": ts, "batch": batch, "actor": actor,
|
|
366
|
+
"entry_id": it["entry_id"], "write_id": wid, "node": nid,
|
|
367
|
+
"layer": e.get("layer"), "rule_id": rule_id, "field": "role",
|
|
368
|
+
"value": it["role"], "source": it["source"], "basis": it["basis"],
|
|
369
|
+
"writer": it.get("writer"), "fm_before": fm_before,
|
|
370
|
+
"content_hash_after": _sha(content)})
|
|
371
|
+
rep["written"] += 1
|
|
372
|
+
_bump(rep["by_source"], it["source"])
|
|
373
|
+
rep["entry_ids"].append(it["entry_id"])
|
|
374
|
+
if rep["written"]:
|
|
375
|
+
cg.rebuild_index()
|
|
376
|
+
rep["plan_remaining"] = max(0, p["targeted"] - len(items))
|
|
377
|
+
return rep
|
|
378
|
+
|
|
379
|
+
|
|
380
|
+
# 生效条件:只处理 action == ACTION_ROLE 的留痕,且 batch 为真时要求 rec.get("batch") == batch(batch 为 None/假值时不按批次过滤)、entry_ids 为真时要求 rec.get("entry_id") ∈ set(entry_ids);rec 的 write_id 已在既有 ACTION_ROLE_ROLLBACK 记录中 → skipped_done,节点不在索引或 read 不到或加密 → missing,当前 fm.get("role") 为空白或与 rec.get("value") 不等 → conflict 不覆盖,相等时按 fm_before.get("had_key") 恢复 fm_before["role"] 或删除 role 键;reverted 非 0 时 cg.rebuild_index()。
|
|
381
|
+
def role_rollback(x, batch=None, entry_ids=None, actor=None) -> dict:
|
|
382
|
+
"""按留痕反向应用:撤销 role 回填。
|
|
383
|
+
|
|
384
|
+
**只在当前值仍等于当初写入值时**撤销;否则计 `conflict` 不覆盖(此后可能已有
|
|
385
|
+
人工修正 / 后续批次写入)。原本无该键 → 删键还原(不是写空串)。
|
|
386
|
+
"""
|
|
387
|
+
cg = _as_cg(x)
|
|
388
|
+
want = set(entry_ids) if entry_ids else None
|
|
389
|
+
rep = {"root": cg.root, "action": ACTION_ROLE_ROLLBACK, "actor": actor,
|
|
390
|
+
"batch": batch, "reverted": 0, "conflict": 0, "missing": 0,
|
|
391
|
+
"skipped_done": 0, "entry_ids": []}
|
|
392
|
+
log = list(read_jsonl(_log_path(cg)) or [])
|
|
393
|
+
done = {r.get("write_id") for r in log
|
|
394
|
+
if r.get("action") == ACTION_ROLE_ROLLBACK and r.get("write_id")}
|
|
395
|
+
for rec in log:
|
|
396
|
+
if rec.get("action") != ACTION_ROLE:
|
|
397
|
+
continue
|
|
398
|
+
if batch and rec.get("batch") != batch:
|
|
399
|
+
continue
|
|
400
|
+
if want is not None and rec.get("entry_id") not in want:
|
|
401
|
+
continue
|
|
402
|
+
wid = rec.get("write_id")
|
|
403
|
+
if wid and wid in done:
|
|
404
|
+
rep["skipped_done"] += 1
|
|
405
|
+
continue
|
|
406
|
+
nid = rec.get("node")
|
|
407
|
+
e = (cg.index.get("nodes") or {}).get(nid)
|
|
408
|
+
if e is None:
|
|
409
|
+
rep["missing"] += 1
|
|
410
|
+
continue
|
|
411
|
+
fm, content = direct_read(cg, e)
|
|
412
|
+
if fm is None or crypto.is_encrypted(content):
|
|
413
|
+
rep["missing"] += 1
|
|
414
|
+
continue
|
|
415
|
+
if _blank(fm.get("role")) or str(fm.get("role")) != str(rec.get("value")):
|
|
416
|
+
rep["conflict"] += 1
|
|
417
|
+
continue
|
|
418
|
+
before = rec.get("fm_before") or {}
|
|
419
|
+
if before.get("had_key"):
|
|
420
|
+
fm["role"] = before.get("role")
|
|
421
|
+
else:
|
|
422
|
+
fm.pop("role", None)
|
|
423
|
+
cg._write_node(nid, os.path.join(cg.root, e["path"]), fm, content,
|
|
424
|
+
durable=True)
|
|
425
|
+
append_jsonl(_log_path(cg), {
|
|
426
|
+
"action": ACTION_ROLE_ROLLBACK, "ts": time.time(), "actor": actor,
|
|
427
|
+
"batch": rec.get("batch"), "entry_id": rec.get("entry_id"),
|
|
428
|
+
"write_id": wid, "node": nid, "field": "role",
|
|
429
|
+
"restored": before.get("role"),
|
|
430
|
+
"had_key": bool(before.get("had_key")), "reason": "rollback"})
|
|
431
|
+
rep["reverted"] += 1
|
|
432
|
+
rep["entry_ids"].append(rec.get("entry_id"))
|
|
433
|
+
if rep["reverted"]:
|
|
434
|
+
cg.rebuild_index()
|
|
435
|
+
return rep
|
|
436
|
+
|
|
437
|
+
|
|
438
|
+
# 生效条件:action 为真时只保留 r.get("action") == action 的记录、batch 为真时只保留 r.get("batch") == batch 的记录,total/by_action 统计的是过滤后的 recs 全量;records 在 limit 为假值(0/None)时返回全部 recs,否则返回 recs[-int(limit):]。
|
|
439
|
+
def history(x, limit=100, action=None, batch=None) -> dict:
|
|
440
|
+
"""读 `_govern.jsonl` 留痕(治理动作的可审计面)。"""
|
|
441
|
+
cg = _as_cg(x)
|
|
442
|
+
recs = [r for r in (read_jsonl(_log_path(cg)) or [])
|
|
443
|
+
if (not action or r.get("action") == action)
|
|
444
|
+
and (not batch or r.get("batch") == batch)]
|
|
445
|
+
tail = recs[-int(limit):] if limit else recs
|
|
446
|
+
by_action = {}
|
|
447
|
+
for r in recs:
|
|
448
|
+
_bump(by_action, str(r.get("action")))
|
|
449
|
+
return {"root": cg.root, "action": "role_history", "total": len(recs),
|
|
450
|
+
"by_action": by_action, "records": tail}
|
|
451
|
+
|
|
452
|
+
|
|
453
|
+
# 生效条件:x 经 _as_cg 且 role_rule(rules=rules, rules_dir=rules_dir, rule_id=rule_id) 命中并声明了 matcher.layer 与 field_absent.field 时,返回以 conformance.load_index(cg.root) 的节点为口径的统计(by_layer 计全部节点、by_value 只计 role 非空白者、target_met 由 role_ratio_kn >= float(THRESHOLDS["role_coverage_min"]) 决定、gap_to_target 取 max(0.0, thr-kn));规则缺失或声明不全时在 role_rule 处抛 ValueError。
|
|
454
|
+
def role_stats(x, *, rule_id=RULE_ROLE, rules=None, rules_dir=None) -> dict:
|
|
455
|
+
"""只读对照:role 覆盖率 + 取值分布(治理前后量化用)。
|
|
456
|
+
|
|
457
|
+
判据**不复刻**——直接复用 `conformance`(真源:`_coverage_metrics` 的
|
|
458
|
+
knowledge 层口径 `role_ratio_kn` 与 `THRESHOLDS["role_coverage_min"]`),
|
|
459
|
+
本函数只补「逐层 / 逐值」分布,供 M4 复跑报告引用。
|
|
460
|
+
"""
|
|
461
|
+
from .. import conformance as CONF # 延迟导入:读侧判据真源,按需加载
|
|
462
|
+
cg = _as_cg(x)
|
|
463
|
+
rule = role_rule(rules=rules, rules_dir=rules_dir, rule_id=rule_id)
|
|
464
|
+
nodes = CONF.load_index(cg.root)
|
|
465
|
+
cov = CONF._coverage_metrics(nodes)
|
|
466
|
+
thr = float(CONF.THRESHOLDS["role_coverage_min"])
|
|
467
|
+
by_layer, by_value = {}, {}
|
|
468
|
+
for e in nodes.values():
|
|
469
|
+
_bump(by_layer, str(e.get("layer")))
|
|
470
|
+
if not _blank(e.get("role")):
|
|
471
|
+
_bump(by_value, str(e.get("role")))
|
|
472
|
+
kn = float(cov.get("role_ratio_kn") or 0.0)
|
|
473
|
+
return {"root": cg.root, "action": "role_stats", "rule_id": rule_id,
|
|
474
|
+
"layers": rule["layers"], "nodes": cov.get("nodes"),
|
|
475
|
+
"knowledge": cov.get("knowledge"), "role_ratio": cov.get("role_ratio"),
|
|
476
|
+
"role_ratio_kn": cov.get("role_ratio_kn"), "threshold": thr,
|
|
477
|
+
"target_met": kn >= thr, "gap_to_target": max(0.0, thr - kn),
|
|
478
|
+
"by_layer": by_layer, "by_value": by_value}
|
|
479
|
+
|
|
480
|
+
|
|
481
|
+
# ---- 统一入口 ------------------------------------------------------------
|
|
482
|
+
|
|
483
|
+
# 生效条件:action 不在 ACTIONS 内时抛 ValueError;action == ACTION_ROLE 时以 kw.pop("apply", False) 为真调用 role_apply(x, **kw)、为假(含缺该键)调用 role_plan(x, **kw);其余情况以 {ACTION_ROLE_ROLLBACK: role_rollback, "role_history": history, "role_stats": role_stats}[action] 取 fn 并返回 fn(x, **kw)。
|
|
484
|
+
def run(x, action, **kw) -> dict:
|
|
485
|
+
"""`role`(`apply=True` 则执行)/ `role_rollback` / `role_history` / `role_stats`。"""
|
|
486
|
+
if action not in ACTIONS:
|
|
487
|
+
raise ValueError("未知 action=%s(可用:%s)" % (action, list(ACTIONS)))
|
|
488
|
+
if action == ACTION_ROLE:
|
|
489
|
+
if kw.pop("apply", False):
|
|
490
|
+
return role_apply(x, **kw)
|
|
491
|
+
return role_plan(x, **kw)
|
|
492
|
+
fn = {ACTION_ROLE_ROLLBACK: role_rollback, "role_history": history,
|
|
493
|
+
"role_stats": role_stats}[action]
|
|
494
|
+
return fn(x, **kw)
|
|
495
|
+
|
|
496
|
+
|
|
497
|
+
# ---- 命令行 --------------------------------------------------------------
|
|
498
|
+
#
|
|
499
|
+
# 形态约定:`--root` / `--rules-dir` / `--json` 均为**子命令参数**(`parents=[common]`),
|
|
500
|
+
# 须写在子命令**之后**:`govern plan --root <root>`。写在子命令之前会被顶层解析器
|
|
501
|
+
# 当作位置参数吃掉(`scripts/review_cli.py` 的同型坑,2026-09-15 实测更正)。
|
|
502
|
+
|
|
503
|
+
# 生效条件:s 为假值(None/空串)时直接返回 DEFAULT_SOURCES;否则按 "," 切分并 strip 丢弃空段,任一段不在 ALL_SOURCES 内即抛 ValueError,切分后 vals 为空(如 s=",")也抛 ValueError,其余返回该非空元组。
|
|
504
|
+
def _parse_sources(s) -> tuple:
|
|
505
|
+
"""CLI 侧来源档解析:逗号分隔,值域封闭(非法即抛,不静默降级)。"""
|
|
506
|
+
if not s:
|
|
507
|
+
return DEFAULT_SOURCES
|
|
508
|
+
vals = tuple(v.strip() for v in str(s).split(",") if v.strip())
|
|
509
|
+
bad = [v for v in vals if v not in ALL_SOURCES]
|
|
510
|
+
if bad:
|
|
511
|
+
raise ValueError("未知来源档 %s(可用:%s)" % (bad, list(ALL_SOURCES)))
|
|
512
|
+
if not vals:
|
|
513
|
+
raise ValueError("sources 为空(缺省即 %s)" % ",".join(DEFAULT_SOURCES))
|
|
514
|
+
return vals
|
|
515
|
+
|
|
516
|
+
|
|
517
|
+
# 生效条件:无参数,调用即返回 argparse.ArgumentParser(add_help=False),其中已含 --root(default=None)、--rules-dir(default=None)、--json(store_true) 三项,无分支。
|
|
518
|
+
def _common_parser() -> argparse.ArgumentParser:
|
|
519
|
+
ap = argparse.ArgumentParser(add_help=False)
|
|
520
|
+
ap.add_argument("--root", default=None, help="真源根(缺省读环境变量 MDCG_ROOT)")
|
|
521
|
+
ap.add_argument("--rules-dir", default=None, help="规则库目录(缺省 mreview/rules)")
|
|
522
|
+
ap.add_argument("--json", action="store_true", help="输出 JSON(缺省人读摘要)")
|
|
523
|
+
return ap
|
|
524
|
+
|
|
525
|
+
|
|
526
|
+
# 生效条件:传入 p 即向它注册 --layer/--prefix/--ids/--limit(type=int)/--sources 五个参数,无分支且无返回值。
|
|
527
|
+
def _add_scope_args(p) -> None:
|
|
528
|
+
p.add_argument("--layer", help="收窄到该层(须在规则 matcher 层之内)")
|
|
529
|
+
p.add_argument("--prefix", help="只处理该 id 前缀")
|
|
530
|
+
p.add_argument("--ids", help="逗号分隔的节点 id 白名单")
|
|
531
|
+
p.add_argument("--limit", type=int, help="本次最多处理条数")
|
|
532
|
+
p.add_argument("--sources",
|
|
533
|
+
help="来源档逗号分隔:tags,map[,layer_default](缺省 %s)"
|
|
534
|
+
% ",".join(DEFAULT_SOURCES))
|
|
535
|
+
|
|
536
|
+
|
|
537
|
+
# 生效条件:传入 a 后返回 {'layer': a.layer, 'prefix': a.prefix, 'limit': a.limit, 'ids': [...], 'sources': _parse_sources(a.sources), 'rules_dir': a.rules_dir},其中 ids 由 a.ids(为假值即 None/空串)按 "," 切分去空段、结果为空则回落 None,sources 由 a.sources 经 _parse_sources 解析(假值 → DEFAULT_SOURCES,非法值抛 ValueError)。
|
|
538
|
+
def _scope_kw(a) -> dict:
|
|
539
|
+
return {"layer": a.layer, "prefix": a.prefix, "limit": a.limit,
|
|
540
|
+
"ids": [i.strip() for i in (a.ids or "").split(",") if i.strip()] or None,
|
|
541
|
+
"sources": _parse_sources(a.sources), "rules_dir": a.rules_dir}
|
|
542
|
+
|
|
543
|
+
|
|
544
|
+
# 生效条件:SOURCE_LAYER 在 sources 内时向 sys.stderr 打印 "警告:" + LAYER_DEFAULT_WARNING,否则不输出。
|
|
545
|
+
def _warn_layer_default(sources) -> None:
|
|
546
|
+
if SOURCE_LAYER in sources:
|
|
547
|
+
print("警告:" + LAYER_DEFAULT_WARNING, file=sys.stderr)
|
|
548
|
+
|
|
549
|
+
|
|
550
|
+
# 生效条件:传入 msg 即向 sys.stderr 打印该 msg 并返回 2,无分支。
|
|
551
|
+
def _die(msg: str) -> int:
|
|
552
|
+
print(msg, file=sys.stderr)
|
|
553
|
+
return 2
|
|
554
|
+
|
|
555
|
+
|
|
556
|
+
def main(argv=None) -> int:
|
|
557
|
+
ap = argparse.ArgumentParser(
|
|
558
|
+
prog="python -m md_cg.mreview.govern",
|
|
559
|
+
description="记忆评审 M4 · 落库治理(确定性部分:rule 驱动 · 永不删除)")
|
|
560
|
+
sub = ap.add_subparsers(dest="cmd")
|
|
561
|
+
common = _common_parser()
|
|
562
|
+
|
|
563
|
+
p = sub.add_parser("plan", parents=[common], help="预演(只出报表,不写盘)")
|
|
564
|
+
_add_scope_args(p)
|
|
565
|
+
p.add_argument("--sample", type=int, default=0, help="抽样条数(人工核对样板)")
|
|
566
|
+
|
|
567
|
+
p = sub.add_parser("apply", parents=[common], help="执行回填(写盘 + 留痕 + 可回滚)")
|
|
568
|
+
_add_scope_args(p)
|
|
569
|
+
p.add_argument("--batch", default=BATCH_DEFAULT, help="批次名(留痕 / 回滚锚点)")
|
|
570
|
+
p.add_argument("--actor", default=None, help="执行者署名(留痕)")
|
|
571
|
+
p.add_argument("--entry-id", action="append", default=[],
|
|
572
|
+
help="只执行该条目(可多次;工单式精确执行)")
|
|
573
|
+
p.add_argument("--yes", action="store_true", help="显式确认写盘(缺省拒绝)")
|
|
574
|
+
|
|
575
|
+
p = sub.add_parser("rollback", parents=[common], help="按留痕回滚")
|
|
576
|
+
p.add_argument("--batch", default=None, help="只回滚该批次")
|
|
577
|
+
p.add_argument("--entry-id", action="append", default=[], help="只回滚该条目(可多次)")
|
|
578
|
+
p.add_argument("--actor", default=None, help="执行者署名(留痕)")
|
|
579
|
+
|
|
580
|
+
p = sub.add_parser("history", parents=[common], help="读 `_govern.jsonl` 留痕")
|
|
581
|
+
p.add_argument("--limit", type=int, default=100)
|
|
582
|
+
p.add_argument("--batch", default=None)
|
|
583
|
+
p.add_argument("--action", default=None)
|
|
584
|
+
|
|
585
|
+
sub.add_parser("stats", parents=[common],
|
|
586
|
+
help="覆盖率对照(复用 conformance 判据)")
|
|
587
|
+
|
|
588
|
+
a = ap.parse_args(argv)
|
|
589
|
+
if not a.cmd:
|
|
590
|
+
# `--root` 是**子命令**参数(`parents=[common]`):无子命令时顶层 Namespace
|
|
591
|
+
# 根本没有该属性——须先判 cmd 再取 root,否则 `govern` 裸跑即 AttributeError。
|
|
592
|
+
ap.print_help()
|
|
593
|
+
return 2
|
|
594
|
+
root = a.root or os.environ.get("MDCG_ROOT")
|
|
595
|
+
if not root:
|
|
596
|
+
return _die("需要 --root 或环境变量 MDCG_ROOT(fail-closed)")
|
|
597
|
+
|
|
598
|
+
if a.cmd == "plan":
|
|
599
|
+
try:
|
|
600
|
+
kw = _scope_kw(a)
|
|
601
|
+
except ValueError as ex:
|
|
602
|
+
return _die(str(ex))
|
|
603
|
+
_warn_layer_default(kw["sources"])
|
|
604
|
+
try:
|
|
605
|
+
rep = role_plan(root, sample=a.sample, **kw)
|
|
606
|
+
except ValueError as ex:
|
|
607
|
+
return _die("预演失败:%s" % ex)
|
|
608
|
+
if a.json:
|
|
609
|
+
print(json.dumps(rep, ensure_ascii=False, indent=2))
|
|
610
|
+
return 0
|
|
611
|
+
print("[M4 预演·只读] root=%s" % rep["root"])
|
|
612
|
+
print(" 靶规则 %s|层 %s|字段 %s|来源档 %s"
|
|
613
|
+
% (rep["rule"]["rule_id"], ",".join(rep["rule_layers"]),
|
|
614
|
+
rep["rule"]["field"], ",".join(rep["sources"])))
|
|
615
|
+
print(" 扫描 %d|可回填 %d|不可回填 %d|域外 %d|已锁 %d|已有值 %d|无权限 %d"
|
|
616
|
+
% (rep["nodes_scanned"], rep["targeted"], rep["unfillable"],
|
|
617
|
+
rep["skipped_out_of_scope"], rep["skipped_locked"],
|
|
618
|
+
rep["skipped_present"], rep["skipped_denied"]))
|
|
619
|
+
for k, v in sorted(rep["by_source"].items()):
|
|
620
|
+
print(" 来源 %-16s %d" % (k, v))
|
|
621
|
+
for k, v in sorted(rep["unfillable_by_reason"].items(), key=lambda kv: -kv[1]):
|
|
622
|
+
print(" 挡下 %-32s %d" % (k, v))
|
|
623
|
+
if rep.get("sample"):
|
|
624
|
+
print(" 抽样:%s" % ", ".join(rep["sample"]))
|
|
625
|
+
return 0
|
|
626
|
+
|
|
627
|
+
if a.cmd == "apply":
|
|
628
|
+
if not a.yes:
|
|
629
|
+
return _die("apply 会写盘:先跑 plan 核对清单,再以 --yes 显式确认(fail-closed)")
|
|
630
|
+
try:
|
|
631
|
+
kw = _scope_kw(a)
|
|
632
|
+
except ValueError as ex:
|
|
633
|
+
return _die(str(ex))
|
|
634
|
+
_warn_layer_default(kw["sources"])
|
|
635
|
+
try:
|
|
636
|
+
rep = role_apply(root, batch=a.batch, actor=a.actor,
|
|
637
|
+
entry_ids=list(a.entry_id) or None, **kw)
|
|
638
|
+
except ValueError as ex:
|
|
639
|
+
return _die("执行失败:%s" % ex)
|
|
640
|
+
if a.json:
|
|
641
|
+
print(json.dumps(rep, ensure_ascii=False, indent=2))
|
|
642
|
+
return 0
|
|
643
|
+
print("[M4 执行] batch=%s actor=%s" % (rep["batch"], rep["actor"]))
|
|
644
|
+
print(" 写入 %d|漂移跳过 %d|密文跳过 %d|缺失 %d|无权限 %d|余量 %d"
|
|
645
|
+
% (rep["written"], rep["skipped_drift"], rep["skipped_locked"],
|
|
646
|
+
rep["skipped_missing"], rep["skipped_denied"], rep["plan_remaining"]))
|
|
647
|
+
for k, v in sorted(rep["by_source"].items()):
|
|
648
|
+
print(" 来源 %-16s %d" % (k, v))
|
|
649
|
+
return 0
|
|
650
|
+
|
|
651
|
+
if a.cmd == "rollback":
|
|
652
|
+
if not a.batch and not a.entry_id:
|
|
653
|
+
return _die("rollback 需要 --batch 或 --entry-id(防全量误回滚)")
|
|
654
|
+
rep = role_rollback(root, batch=a.batch, actor=a.actor,
|
|
655
|
+
entry_ids=list(a.entry_id) or None)
|
|
656
|
+
if a.json:
|
|
657
|
+
print(json.dumps(rep, ensure_ascii=False, indent=2))
|
|
658
|
+
return 0
|
|
659
|
+
print("[M4 回滚] batch=%s|还原 %d|冲突跳过 %d|缺失 %d|已回滚 %d"
|
|
660
|
+
% (rep["batch"], rep["reverted"], rep["conflict"], rep["missing"],
|
|
661
|
+
rep["skipped_done"]))
|
|
662
|
+
return 0
|
|
663
|
+
|
|
664
|
+
if a.cmd == "history":
|
|
665
|
+
rep = history(root, limit=a.limit, action=a.action, batch=a.batch)
|
|
666
|
+
if a.json:
|
|
667
|
+
print(json.dumps(rep, ensure_ascii=False, indent=2))
|
|
668
|
+
return 0
|
|
669
|
+
print("[M4 留痕] 命中 %d 条|分布 %s" % (rep["total"], rep["by_action"]))
|
|
670
|
+
for r in rep["records"]:
|
|
671
|
+
print(" %-14s %-10s %-22s %s=%s"
|
|
672
|
+
% (time.strftime("%m-%d %H:%M:%S", time.localtime(r.get("ts") or 0)),
|
|
673
|
+
r.get("action"), str(r.get("node")), r.get("field"),
|
|
674
|
+
r.get("value", r.get("restored"))))
|
|
675
|
+
return 0
|
|
676
|
+
|
|
677
|
+
try:
|
|
678
|
+
rep = role_stats(root)
|
|
679
|
+
except ValueError as ex:
|
|
680
|
+
return _die("对照失败:%s" % ex)
|
|
681
|
+
if a.json:
|
|
682
|
+
print(json.dumps(rep, ensure_ascii=False, indent=2))
|
|
683
|
+
return 0
|
|
684
|
+
print("[M4 对照] root=%s 判据源=conformance" % rep["root"])
|
|
685
|
+
print(" 节点 %s|knowledge %s|role 覆盖 %s|knowledge 层覆盖 %s(阈值 %.2f,%s)"
|
|
686
|
+
% (rep["nodes"], rep["knowledge"], rep["role_ratio"],
|
|
687
|
+
rep["role_ratio_kn"], rep["threshold"],
|
|
688
|
+
"达标" if rep["target_met"] else "未达标,缺口 %.4f" % rep["gap_to_target"]))
|
|
689
|
+
print(" 逐层 %s" % rep["by_layer"])
|
|
690
|
+
print(" 逐值 %s" % rep["by_value"])
|
|
691
|
+
return 0
|
|
692
|
+
|
|
693
|
+
|
|
694
|
+
if __name__ == "__main__":
|
|
694
695
|
sys.exit(main())
|