@furongjun1999/dsh-memory 0.4.7 → 0.4.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +130 -47
- package/codebuddy/CODEBUDDY.md +21 -10
- package/codebuddy/README.md +13 -1
- package/codebuddy/mcp.json +9 -0
- package/docs/GBrain/345/217/257/345/200/237/351/211/264/347/202/271_/347/201/265/346/236/242/350/220/275/347/202/271/344/272/244/346/216/245_20260919.md +169 -0
- package/docs/Pi/345/217/257/345/255/246/344/271/240/344/274/230/347/202/271_/347/201/265/346/236/242/345/244/247/350/204/221/346/224/271/350/277/233/344/272/244/346/216/245_20260915.md +144 -0
- package/docs/README.md +111 -0
- package/docs/discipline/harnesses.yaml +226 -152
- package/docs/discipline/templates/full.md.tmpl +61 -58
- package/docs/eval//347/254/254/344/270/211/346/226/271/351/252/214/350/257/201/346/212/245/345/221/212_LoCoMo_/347/201/265/346/236/242_.md +127 -0
- package/docs/eval//347/254/254/344/270/211/346/226/271/351/252/214/350/257/201/346/212/245/345/221/212_LoCoMo_/347/201/265/346/236/242_.png +0 -0
- package/docs/eval//347/254/254/344/270/211/346/226/271/351/252/214/350/257/201/346/212/245/345/221/212_/347/201/265/346/236/242_vs_dejavu_/347/273/237/344/270/200/350/257/204/345/210/206_v7.md +202 -0
- package/docs/experiments/linkref_backfill/candidates_20260917.json +726 -0
- package/docs/experiments/linkref_backfill/candidates_internal_20260917.json +602 -0
- package/docs/experiments/linkref_backfill/candidates_internal_v2.json +603 -0
- package/docs/experiments/linkref_backfill/candidates_secret_20260917.json +884 -0
- package/docs/experiments/linkref_backfill/candidates_secret_v2.json +789 -0
- package/docs/experiments/m4-role-probe/census.py +86 -0
- package/docs/experiments/m4-role-probe/probe.py +89 -0
- package/docs/experiments/mapped_confidence/bench_mapped_conf.py +216 -0
- package/docs/experiments/mapped_confidence/post_check.py +75 -0
- package/docs/experiments/mapped_confidence/repro_thirdparty_tol.py +83 -0
- package/docs/experiments/mapped_confidence/result_mapped_conf.json +215 -0
- package/docs/hive//345/244/232/347/253/257harness/351/200/232/344/277/241/345/245/221/347/272/246_v0.1.md +42 -0
- package/docs/hive//346/243/200/347/264/242/346/224/266/346/225/233/345/256/236/346/265/213/344/270/216S1b/350/256/276/350/256/241_v0.1.md +43 -0
- package/docs/hive//346/243/200/347/264/242/350/267/257/345/276/204/344/270/216/350/256/244/347/237/245/347/273/223/346/236/204/345/245/221/347/272/246_v0.1.md +102 -0
- package/docs/hive//347/234/237/345/256/236/345/272/223/347/253/257/345/210/260/347/253/257/345/256/236/346/265/213_S1b/344/270/216/345/217/254/345/233/236/346/235/203/350/241/241_v0.1.md +57 -0
- package/docs/hive//347/234/237/345/256/236/345/272/223/347/253/257/345/210/260/347/253/257/345/256/236/346/265/213_S7/345/200/222/346/216/222/345/200/231/351/200/211/345/261/202_v0.1.md +126 -0
- package/docs/hive//350/234/202/345/267/242/345/217/214/345/256/236/344/276/213/344/272/222/351/252/214_/350/256/276/350/256/241/345/256/232/347/250/277.md +503 -0
- package/docs/{ → hive/}/350/234/202/345/267/242/345/267/245/344/275/234/350/256/260/345/277/206_/351/241/271/347/233/256/350/256/241/345/210/222.md +14 -2
- package/docs/mdcg/D_meta_/345/267/245/347/250/213/345/214/226/346/226/271/346/241/210_v0.2.md +216 -0
- package/docs/{README → mdcg/README}/350/257/246/347/273/206/347/211/210_v0.4.5.md +631 -625
- package/docs/{ → mdcg/}/344/270/273/344/273/243/347/220/206/345/255/220/344/273/243/347/220/206/350/256/260/345/277/206/346/236/266/346/236/204/350/256/276/350/256/241.md +1 -1
- package/docs/mdcg//344/273/243/347/240/201/350/257/204/345/256/241/344/270/216/346/235/241/344/273/266/345/214/226/346/263/250/351/207/212_/345/245/221/347/272/246_v0.1.md +82 -0
- package/docs/mdcg//345/205/250/345/272/223/344/273/243/347/240/201/350/257/204/345/256/241/344/270/216/346/235/241/344/273/266/345/214/226/346/263/250/351/207/212_/350/256/241/345/210/222_v0.1.md +600 -0
- package/docs/mdcg//345/212/237/350/203/275/350/260/203/347/224/250/346/230/240/345/260/204/350/241/250_v0.1.md +263 -0
- package/docs/{ → mdcg/}/345/215/225/345/205/203/350/207/252/346/210/221/351/224/232/347/202/271_/347/263/273/347/273/237/346/217/220/347/244/272/350/257/215/346/240/207/345/207/206_v0.3.md +1 -1
- package/docs/mdcg//345/255/220/344/273/243/347/220/206/351/205/215/347/275/256/346/240/207/345/207/206_v0.5.md +236 -0
- package/docs/{ → mdcg/}/346/272/220/347/240/201/347/272/247/346/236/266/346/236/204/345/256/241/350/256/241_GPT/346/211/271/350/257/204/345/257/271/347/205/247_v1.0.md +2 -2
- package/docs/{ → mdcg/}/347/201/265/346/236/242/344/270/211/345/261/202/346/213/206/345/210/206/350/247/204/345/210/222_v0.1.md +181 -181
- package/docs/mdcg//347/201/265/346/236/242/350/256/260/345/277/206/345/212/250/350/257/215/345/215/217/350/256/256_v1.0-draft.md +172 -0
- package/docs/mdcg//347/216/257/344/272/214_/347/231/275/347/256/261/345/241/253/345/205/205/346/265/201/346/260/264/347/272/277_v0.1.md +31 -0
- package/docs/{ → mdcg/}/347/274/272/345/217/243/345/215/225_P0/346/224/266/345/217/243_v0.1.md +2 -2
- package/docs/{ → mdcg/}/350/256/244/347/237/245/345/233/276_G4-G8/347/274/272/345/217/243/350/243/201/345/256/232/345/215/225_v0.1.md +1 -1
- package/docs/{ → mdcg/}/350/256/244/347/237/245/345/233/276_/347/264/242/345/274/225/344/270/216/345/267/245/347/250/213/350/247/204/350/214/203/345/214/226_/350/256/241/345/210/222_v0.1.md +4 -4
- package/docs/{ → mdcg/}/350/256/260/345/277/206/346/223/215/344/275/234/347/263/273/347/273/237_MdCGOS/344/270/216MCP/346/216/245/345/205/245_v0.1.md +2 -2
- package/docs/mdcg//350/267/250/347/253/257/351/252/214/350/257/201/344/270/216/345/220/214/346/255/245/345/215/217/350/256/256_v0.1.md +93 -0
- package/docs/plans/GridWorld/346/234/200/345/260/217/351/227/255/347/216/257/350/247/204/346/240/274_v0.1.md +176 -0
- package/docs/{ → swarm/}/350/234/202/347/276/244/344/272/222/350/201/224_v0.1.md +3 -3
- package/docs/swarm//350/234/202/347/276/244/345/220/214/351/224/231/346/243/200/346/265/213/345/256/236/351/252/214/345/215/217/350/256/256_v0.1.md +242 -0
- package/docs/theory//345/215/225/347/272/277/347/250/213/344/270/216/346/263/250/346/204/217/345/212/233/351/233/206/344/270/255_/346/227/240/344/272/211/350/256/256/347/220/206/350/256/272/346/226/207/346/241/243_v1.0.md +129 -0
- package/docs/theory//345/271/266/345/217/221/345/277/205/347/204/266/346/200/247/347/220/206/350/256/272_v0.2.md +134 -0
- package/docs/theory//346/246/202/345/277/265/345/210/206/345/261/202/345/257/271/351/275/220/350/241/250_v0.1.md +145 -0
- package/docs/{ → theory/}/347/220/206/350/256/272_/346/234/272/345/210/266_/344/273/243/347/240/201_/345/256/236/351/252/214_/347/274/272/345/217/243/347/237/251/351/230/265_v0.1.md +6 -6
- package/docs/{ → theory/}/347/220/206/350/256/272/344/273/223/346/213/206/345/210/206/344/270/216/347/231/275/347/256/261/347/237/245/350/257/206/345/272/223/345/206/205/350/277/201_v0.1.md +1 -1
- package/docs/theory//350/256/244/347/237/245/344/273/243/347/220/206/344/270/216/346/224/266/346/225/233/347/273/223/346/236/204_/346/235/241/344/273/266/350/256/272/351/207/215/346/236/204_v0.1.md +221 -0
- package/docs/world_model//344/270/226/347/225/214/346/250/241/345/236/213_/347/245/236/347/273/217/347/275/221/347/273/234/345/272/225/345/261/202/346/236/266/346/236/204_v1.0.md +237 -0
- package/docs//345/255/230/347/256/227/344/270/200/344/275/223/346/236/266/346/236/204/345/255/246/344/271/240_/345/244/226/351/203/250/347/220/206/350/256/272/345/257/271/347/205/247/344/270/216/350/267/257/347/272/277/344/272/244/346/216/245_20260916.md +98 -0
- package/docs//345/255/230/347/256/227/344/270/200/344/275/223/347/245/236/347/273/217/347/275/221/347/273/234/346/236/266/346/236/204_/346/200/273/347/272/262/344/270/216/344/272/244/346/216/245_20260916.md +109 -0
- package/docs//345/267/245/344/275/234/347/272/252/345/276/213_/350/256/244/347/237/245/345/233/276/346/235/241/347/233/256_v1.1.json +434 -405
- package/docs//347/201/265/346/236/242/350/207/252/346/210/221/346/224/271/350/277/233/345/267/245/344/275/234/350/256/241/345/210/222_/345/244/226/351/203/250/347/240/224/347/251/266/347/263/273/345/210/227/345/220/270/346/224/266_v1_20260919.md +286 -0
- package/docs//347/231/275/347/256/261/345/214/226/347/272/262/351/242/206_/344/270/211/351/241/271/347/233/256/345/255/246/344/271/240/346/200/273/346/236/266/346/236/204/344/270/216Ghidra/344/272/244/346/216/245_20260916.md +104 -0
- package/docs//350/256/260/345/277/206/350/257/204/345/256/241/347/263/273/347/273/237_/347/253/213/351/241/271/350/256/276/350/256/241/344/270/216/346/226/275/345/267/245/344/272/244/346/216/245_20260915.md +183 -0
- package/dsh/README.md +34 -1
- package/dsh/cordis.yml.example +13 -7
- package/dsh/hive-mcp-probe.mjs +94 -0
- package/dsh/hive-mcp.example.yml +62 -0
- package/dsh/update-lingshu.bat +11 -0
- package/dsh/update-lingshu.ps1 +337 -0
- package/lib/hooks.d.ts +8 -0
- package/lib/hooks.js +27 -24
- package/lib/index.d.ts +4 -2
- package/lib/index.js +29 -6
- package/lib/lib/datapath.d.ts +76 -1
- package/lib/lib/datapath.js +199 -13
- package/lib/lib/mdcg_client.d.ts +6 -3
- package/lib/lib/mdcg_client.js +6 -5
- package/lib/lib/mutual.js +4 -4
- package/lib/lib/prompt_safety.d.ts +48 -0
- package/lib/lib/prompt_safety.js +62 -0
- package/lib/lib/token_store.js +4 -5
- package/md_cg/audit.py +91 -2
- package/md_cg/autonomy.py +86 -15
- package/md_cg/backfill.py +36 -1
- package/md_cg/backfill_bigdomain.py +34 -0
- package/md_cg/bench6_arms.py +28 -1
- package/md_cg/bench6_common.py +10 -1
- package/md_cg/bench6_competitors.py +6 -1
- package/md_cg/bench_axis_domain.py +9 -1
- package/md_cg/bench_blind_comp.py +7 -1
- package/md_cg/bench_en_atoms_public.py +9 -0
- package/md_cg/bench_governance.py +348 -0
- package/md_cg/bench_lme_zh.py +16 -1
- package/md_cg/bench_locomo.py +2 -1
- package/md_cg/bench_locomo_zh.py +16 -1
- package/md_cg/bench_locomo_zh_public.py +4 -1
- package/md_cg/bench_longmem.py +2 -1
- package/md_cg/bench_membench.py +37 -6
- package/md_cg/bench_p0.py +4 -1
- package/md_cg/bench_progressive.py +61 -10
- package/md_cg/bench_role_views.py +238 -0
- package/md_cg/bench_task_ab.py +8 -1
- package/md_cg/bench_task_ab_llm.py +13 -1
- package/md_cg/bench_unified_en.py +6 -1
- package/md_cg/bench_zh_mad.py +20 -1
- package/md_cg/blindspot_tickets.py +123 -0
- package/md_cg/branches.py +286 -0
- package/md_cg/build_postings.py +73 -0
- package/md_cg/ccgc.py +949 -0
- package/md_cg/census.py +5 -1
- package/md_cg/chain.py +24 -3
- package/md_cg/codeindex.py +134 -17
- package/md_cg/coldverify.py +265 -0
- package/md_cg/comment_gate.py +338 -0
- package/md_cg/cond_compose.py +190 -0
- package/md_cg/cond_facts.py +155 -0
- package/md_cg/cond_template.json +107 -0
- package/md_cg/condition_anchor.py +143 -0
- package/md_cg/conformance.py +727 -0
- package/md_cg/consistency.py +25 -2
- package/md_cg/consolidate.py +53 -2
- package/md_cg/corpus.py +5 -1
- package/md_cg/crosscheck.py +42 -2
- package/md_cg/crypto.py +35 -1
- package/md_cg/d_meta.py +310 -0
- package/md_cg/datapath.py +201 -26
- package/md_cg/docindex.py +122 -1
- package/md_cg/eval_common.py +35 -6
- package/md_cg/evidence.py +27 -1
- package/md_cg/evolution.py +21 -1
- package/md_cg/export.py +11 -1
- package/md_cg/forgetting.py +34 -4
- package/md_cg/fsutil.py +81 -2
- package/md_cg/hotcache.py +214 -0
- package/md_cg/hyperedge.py +251 -0
- package/md_cg/identity.py +20 -3
- package/md_cg/insight.py +19 -3
- package/md_cg/lexicon/build_cedict_en_zh.py +9 -0
- package/md_cg/lexicon/build_standard_en.py +171 -168
- package/md_cg/lexicon/expand_en_zh.py +6 -0
- package/md_cg/lifecycle.py +273 -0
- package/md_cg/linkref.py +281 -0
- package/md_cg/links.py +29 -1
- package/md_cg/mcp_server.py +894 -168
- package/md_cg/md_whitebox.py +53 -1
- package/md_cg/mdcg.py +1205 -41
- package/md_cg/mdcos.py +1060 -76
- package/md_cg/metacognition.py +39 -4
- package/md_cg/migrate.py +4 -0
- package/md_cg/migrate_aeis.py +221 -213
- package/md_cg/migrate_roleplay.py +8 -0
- package/md_cg/migrate_wisdom_graph.py +14 -1
- package/md_cg/mreview/__init__.py +25 -0
- package/md_cg/mreview/__main__.py +110 -0
- package/md_cg/mreview/bundle.py +178 -0
- package/md_cg/mreview/candidates.py +262 -0
- package/md_cg/mreview/govern.py +694 -0
- package/md_cg/mreview/locate.py +939 -0
- package/md_cg/mreview/pipeline.py +729 -0
- package/md_cg/mreview/rules/duplication.json +21 -0
- package/md_cg/mreview/rules/field_coverage.json +54 -0
- package/md_cg/mreview/rules/source_license.json +21 -0
- package/md_cg/mreview/rules/template_flow.json +21 -0
- package/md_cg/mreview/ruleset.py +253 -0
- package/md_cg/nodefile.py +269 -1
- package/md_cg/pooling.py +23 -1
- package/md_cg/postings.py +298 -0
- package/md_cg/predict.py +113 -15
- package/md_cg/progressive.py +3 -0
- package/md_cg/protect.py +18 -5
- package/md_cg/protocol.py +372 -0
- package/md_cg/provenance.py +262 -0
- package/md_cg/reach.py +453 -0
- package/md_cg/refindex.py +88 -4
- package/md_cg/refine.py +20 -1
- package/md_cg/roleviews.py +89 -0
- package/md_cg/routing.py +76 -0
- package/md_cg/scrub.py +68 -3
- package/md_cg/security.py +26 -1
- package/md_cg/self_state.py +65 -2
- package/md_cg/selfreport.py +151 -0
- package/md_cg/semantic/canonical.py +5 -0
- package/md_cg/semantic/en_normalizer.py +364 -295
- package/md_cg/semantic/zh_en_atoms.py +139 -136
- package/md_cg/signer.py +41 -1
- package/md_cg/sources.py +583 -547
- package/md_cg/statushdr.py +179 -0
- package/md_cg/stg.py +59 -18
- package/md_cg/subgraph.py +23 -0
- package/md_cg/sustain.py +74 -1
- package/md_cg/tasks.py +471 -0
- package/md_cg/test_action_derive.py +203 -0
- package/md_cg/test_audit_rotate.py +270 -0
- package/md_cg/test_autonomy.py +26 -0
- package/md_cg/test_bench_governance.py +102 -0
- package/md_cg/test_blindspot_tickets.py +166 -0
- package/md_cg/test_branches.py +249 -0
- package/md_cg/test_ccg_perturb.py +1 -1
- package/md_cg/test_ccgc.py +433 -0
- package/md_cg/test_codeindex.py +338 -0
- package/md_cg/test_comment_gate.py +187 -0
- package/md_cg/test_cond_compose_anchors.py +76 -0
- package/md_cg/test_condition_anchor.py +82 -0
- package/md_cg/test_conformance.py +343 -0
- package/md_cg/test_d_meta.py +412 -0
- package/md_cg/test_datapath_root.py +188 -0
- package/md_cg/test_en_pipeline.py +24 -0
- package/md_cg/test_gain_gate.py +47 -1
- package/md_cg/test_health_scale.py +173 -0
- package/md_cg/test_hot_cold.py +187 -0
- package/md_cg/test_hyperedge.py +245 -0
- package/md_cg/test_identity_attribution.py +6 -0
- package/md_cg/test_index_durability.py +224 -0
- package/md_cg/test_lifecycle.py +309 -0
- package/md_cg/test_linkref.py +306 -0
- package/md_cg/test_md_access_parity.py +15 -3
- package/md_cg/test_mr_m1.py +769 -0
- package/md_cg/test_mr_m2.py +587 -0
- package/md_cg/test_mr_m3.py +710 -0
- package/md_cg/test_mr_m4.py +485 -0
- package/md_cg/test_p1.py +1 -1
- package/md_cg/test_p11_consistency.py +1 -1
- package/md_cg/test_p12_metacognition.py +2 -2
- package/md_cg/test_p16_self_state.py +1 -1
- package/md_cg/test_p26_refindex.py +50 -21
- package/md_cg/test_p27_docindex.py +258 -4
- package/md_cg/test_p28_refcheck.py +1 -1
- package/md_cg/test_p29_session_ingest_export.py +1 -1
- package/md_cg/test_p2_mcp.py +7 -1
- package/md_cg/test_p31_insight.py +24 -0
- package/md_cg/test_p38_contextualize.py +1 -1
- package/md_cg/test_p39_vision_evidence.py +1 -1
- package/md_cg/test_p40_refine_worklist.py +1 -1
- package/md_cg/test_p41_evolve_patrol.py +1 -1
- package/md_cg/test_p42_provenance.py +1 -1
- package/md_cg/test_p43_pooling.py +41 -13
- package/md_cg/test_p44_md_whitebox.py +14 -1
- package/md_cg/test_protocol.py +243 -0
- package/md_cg/test_reach.py +378 -0
- package/md_cg/test_reach_keys.py +201 -0
- package/md_cg/test_reach_meta_exits.py +145 -0
- package/md_cg/test_read_clip.py +141 -0
- package/md_cg/test_retr_s1.py +340 -0
- package/md_cg/test_retr_s1b.py +209 -0
- package/md_cg/test_retr_s3.py +194 -0
- package/md_cg/test_retr_s4.py +163 -0
- package/md_cg/test_retr_s5.py +200 -0
- package/md_cg/test_retr_s6.py +157 -0
- package/md_cg/test_retr_s7.py +385 -0
- package/md_cg/test_retr_s8_time.py +316 -0
- package/md_cg/test_retr_s9_edges.py +286 -0
- package/md_cg/test_retr_s9_entity_ctx.py +175 -0
- package/md_cg/test_review_conformance.py +367 -0
- package/md_cg/test_role_views.py +354 -0
- package/md_cg/test_tasks.py +409 -0
- package/md_cg/test_tool_face.py +189 -0
- package/md_cg/test_trust.py +361 -0
- package/md_cg/test_twophase.py +286 -0
- package/md_cg/test_units_poll.py +71 -0
- package/md_cg/test_v14_fixes.py +397 -0
- package/md_cg/test_validity_filter.py +280 -0
- package/md_cg/test_wisdom_md_store.py +7 -3
- package/md_cg/test_writepipe.py +214 -0
- package/md_cg/theory.py +17 -2
- package/md_cg/tokens.py +134 -12
- package/md_cg/tool_face.py +261 -0
- package/md_cg/trust.py +943 -0
- package/md_cg/twophase.py +232 -0
- package/md_cg/units.py +665 -0
- package/md_cg/vision_evidence.py +25 -2
- package/md_cg/weights.py +25 -2
- package/md_cg/whitebox.py +33 -2
- package/md_cg/whitebox_kb/data/verify_cache.json +21233 -360
- package/md_cg/whitebox_kb/data/verify_savings.jsonl +5124 -0
- package/md_cg/whitebox_kb/wisdom/audit_log/chain_heat.json +10 -10
- package/md_cg/whitebox_kb/wisdom/code_compose.py +113 -6
- package/md_cg/whitebox_kb/wisdom/code_solidified.json +1 -1
- package/md_cg/whitebox_kb/wisdom/verifier.py +340 -55
- package/md_cg/whitebox_kb/wisdom/wisdom-book-cloud.db +0 -0
- package/md_cg/writelimit.py +37 -7
- package/md_cg/writepipe.py +543 -0
- package/package.json +2 -2
- package/skills/skills/designer-perspective/scripts/__pycache__/designer.cpython-310.pyc +0 -0
- package/skills/skills/designer-perspective/scripts/designer.py +17 -1
- package/skills/skills/designer-perspective/tests/selftest.py +3 -1
- package/src/hooks.ts +27 -22
- package/src/index.ts +33 -6
- package/src/lib/datapath.ts +211 -13
- package/src/lib/mdcg_client.ts +12 -8
- package/src/lib/mutual.ts +411 -411
- package/src/lib/prompt_safety.ts +62 -0
- package/src/lib/token_store.ts +4 -5
- package/zcode/AGENTS.md +21 -10
- package/zcode/README.md +4 -0
- package/docs//345/212/237/350/203/275/350/260/203/347/224/250/346/230/240/345/260/204/350/241/250_v0.1.md +0 -221
- /package/docs/{AGI → eval/AGI}/344/270/203/347/273/264/350/257/204/345/210/206/346/212/245/345/221/212_md_cg_v1.0.md" +0 -0
- /package/docs/{AGI → eval/AGI}/344/270/203/347/273/264/350/257/204/345/210/206/346/212/245/345/221/212_md_cg_v2.0.md" +0 -0
- /package/docs/{ → eval/}/345/256/236/351/252/214/346/226/271/346/241/210_/345/255/246/344/271/240/351/227/255/347/216/257AB/344/270/216/346/250/252/350/257/204_v1.0.md" +0 -0
- /package/docs/{ → eval/}/346/250/252/350/257/204_/345/205/255/345/256/266100/351/242/230/344/270/255/350/213/261/345/217/214/346/237/245_v1.0.md" +0 -0
- /package/docs/{guardrail-charter.md → mdcg/guardrail-charter.md} +0 -0
- /package/docs/{lingshu_tutorial.html → mdcg/lingshu_tutorial.html} +0 -0
- /package/docs/{memory-assessment.html → mdcg/memory-assessment.html} +0 -0
- /package/docs/{memory_score.html → mdcg/memory_score.html} +0 -0
- /package/docs/{memory_score.png → mdcg/memory_score.png} +0 -0
- /package/docs/{release_v0.3.0.md → mdcg/release_v0.3.0.md} +0 -0
- /package/docs/{release_v0.4.5.md → mdcg/release_v0.4.5.md} +0 -0
- /package/docs/{tool_table_v0.3.0.md → mdcg/tool_table_v0.3.0.md} +0 -0
- /package/docs/{ → mdcg/}/344/273/244/347/211/214/344/270/216/350/247/222/350/211/262/346/235/203/350/201/214/345/210/206/347/246/273_v0.1.md" +0 -0
- /package/docs/{ → mdcg/}/347/201/265/346/236/24282/345/267/245/345/205/267_/345/212/237/350/203/275/346/225/264/347/220/206/344/270/216/350/277/201/347/247/273/346/230/240/345/260/204_v0.1.md" +0 -0
- /package/docs/{ → mdcg/}/347/201/265/346/236/242MCP/345/267/245/345/205/267/346/200/273/350/241/250_v3.4.md" +0 -0
- /package/docs/{ → mdcg/}/347/201/265/346/236/242_/350/207/252/346/210/221/345/261/202/345/256/232/344/271/211.md" +0 -0
- /package/docs/{ → mdcg/}/350/256/244/347/237/245/345/233/276_MD/347/233/256/345/275/225/346/226/271/346/241/210_v0.1.md" +0 -0
- /package/docs/{ → mdcg/}/350/256/244/347/237/245/345/233/276_/346/235/241/344/273/266/347/251/272/351/227/264/345/220/210/346/210/220/344/270/216/347/224/237/346/225/210/346/235/241/344/273/266/345/217/243/345/276/204_v0.1.md" +0 -0
- /package/docs/{ → mdcg/}/350/256/244/347/237/245/345/233/276/344/275/234/344/270/272/350/256/260/345/277/206/346/223/215/344/275/234/347/263/273/347/273/237_/350/257/204/344/274/260/344/270/216/350/267/257/347/272/277_v0.1.md" +0 -0
- /package/docs/{ → plans/}/345/221/275/345/220/215/346/262/273/347/220/206_/351/241/271/347/233/256/350/256/241/345/210/222.md" +0 -0
- /package/docs/{ → plans/}/347/237/245/350/257/206/345/233/276/350/260/261/351/241/271/347/233/256_/344/272/244/346/216/245/346/226/207/346/241/243_20260914.md" +0 -0
- /package/docs/{ → swarm/}/350/234/202/347/276/244/345/244/232/346/231/272/350/203/275/344/275/223_/344/275/277/347/224/250/346/212/245/345/221/212_20260913.md" +0 -0
- /package/docs/{ → swarm/}/350/234/202/347/276/244/345/244/232/346/231/272/350/203/275/344/275/223_/345/212/237/350/203/275/350/257/264/346/230/216_v0.6.md" +0 -0
- /package/docs/{ → swarm/}/350/234/202/347/276/244/347/233/262/345/214/272/346/240/207/350/256/260_v1.0.md" +0 -0
- /package/docs/{ → theory/}/344/277/241/346/201/257/345/267/256/344/270/272/344/273/200/344/271/210/345/277/205/347/204/266/345/255/230/345/234/250/344/270/224/350/207/252/347/204/266/346/211/251/345/244/247.md" +0 -0
- /package/docs/{ → theory/}/346/231/272/350/203/275/347/232/204/345/205/254/347/220/206/345/214/226/345/237/272/347/237/263.md" +0 -0
- /package/docs/{ → theory/}/346/231/272/350/203/275/347/232/204/350/256/244/347/237/245/350/277/207/347/250/213.md" +0 -0
- /package/docs/{ → theory/}/346/231/272/350/203/275/350/256/2723.4.md" +0 -0
- /package/docs/{ → theory/}/347/231/275/347/256/261/346/231/272/350/203/275/346/230/257/344/273/200/344/271/210/357/274/237.md" +0 -0
- /package/docs/{ → theory/}/347/231/275/347/256/261/346/231/272/350/203/275/347/263/273/345/210/227/302/267/347/254/254/344/272/224/347/257/207/357/274/232/350/256/251AI/347/234/237/346/255/243/350/243/205/344/270/212/350/256/260/345/277/206.md" +0 -0
package/md_cg/refindex.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
2
|
"""md_cg · 统一 ref 协议 + 索引水位(增量)+ 漂移/悬空巡检
|
|
3
3
|
|
|
4
|
-
对照 `docs/认知图_索引与工程规范化_计划_v0.1.md` 的 R3(修 D + 修 F):
|
|
4
|
+
对照 `docs/mdcg/认知图_索引与工程规范化_计划_v0.1.md` 的 R3(修 D + 修 F):
|
|
5
5
|
|
|
6
6
|
**D 漂移 / 悬空检测(本模块 `check_refs`)**
|
|
7
7
|
扫描带 `code_ref` / `doc_ref` 的节点,回两类问题:
|
|
@@ -42,6 +42,7 @@ MAX_CHECK = 2000 # 巡检节点上限(超出报 truncated,不静默
|
|
|
42
42
|
STATUSES = ("ok", "stale", "dangling", "unresolved", "error")
|
|
43
43
|
|
|
44
44
|
|
|
45
|
+
# 生效条件:给定 fp 时返回 os.path.abspath(fp or '')(fp 为空/None 则返回当前目录的绝对路径),作为水位键以绝对路径保证不同 root 下同名文件不互相覆盖。
|
|
45
46
|
def _src_key(fp: str) -> str:
|
|
46
47
|
"""水位的键 = 源文件绝对路径。
|
|
47
48
|
|
|
@@ -51,6 +52,7 @@ def _src_key(fp: str) -> str:
|
|
|
51
52
|
return os.path.abspath(fp or "")
|
|
52
53
|
|
|
53
54
|
|
|
55
|
+
# 生效条件:无 required 形参,任何调用都返回 round(time.time(), 1),把时间戳压到 1 位小数以稳定 `_refindex.json` 字节数。
|
|
54
56
|
def _now() -> float:
|
|
55
57
|
"""时间戳压到 1 位小数:让 `_refindex.json` 字节数稳定(重跑不涨),
|
|
56
58
|
同时保留足够的「多久以前」信息(float 的最短 repr 保证小数位固定为 1)。"""
|
|
@@ -61,6 +63,7 @@ def _now() -> float:
|
|
|
61
63
|
# 提取器注册表(统一调度:调用方只说 kind,不说「用哪个模块」)
|
|
62
64
|
# --------------------------------------------------------------------------
|
|
63
65
|
|
|
66
|
+
# 生效条件:kind == 'code_ref' 返回 codeindex、kind == 'doc_ref' 返回 docindex,其他 kind 抛 ValueError(提示支持 REF_KEYS)。
|
|
64
67
|
def _mod(kind: str):
|
|
65
68
|
from . import codeindex, docindex
|
|
66
69
|
if kind == "code_ref":
|
|
@@ -70,6 +73,7 @@ def _mod(kind: str):
|
|
|
70
73
|
raise ValueError(f"未知 ref kind:{kind!r}(支持 {REF_KEYS})")
|
|
71
74
|
|
|
72
75
|
|
|
76
|
+
# 生效条件:无 required 形参,调用即返回 {'code_ref': {'suffixes': tuple(codeindex.SUFFIX)}, 'doc_ref': {'suffixes': tuple(docindex.SUFFIX)}}。
|
|
73
77
|
def registry() -> dict:
|
|
74
78
|
"""后缀 → kind 的注册表(code / doc 各一份提取器)。"""
|
|
75
79
|
from . import codeindex, docindex
|
|
@@ -79,6 +83,7 @@ def registry() -> dict:
|
|
|
79
83
|
}
|
|
80
84
|
|
|
81
85
|
|
|
86
|
+
# 生效条件:path 的小写后缀在 codeindex.EXTRACTORS 中返回 'code_ref',在 docindex.SUFFIX 中返回 'doc_ref',无后缀或均不匹配返回 ''。
|
|
82
87
|
def kind_of_path(path: str) -> str:
|
|
83
88
|
"""按后缀判 kind;无提取器返回 ''(由调用方决定是报错还是跳过)。"""
|
|
84
89
|
from . import codeindex, docindex
|
|
@@ -92,6 +97,7 @@ def kind_of_path(path: str) -> str:
|
|
|
92
97
|
return ""
|
|
93
98
|
|
|
94
99
|
|
|
100
|
+
# 生效条件:source 为待提取文本,kind 非空或 path 后缀能推出 kind 时返回 _mod(k).extract(source, path),推不出 kind 时抛 ValueError。
|
|
95
101
|
def extract(source: str, path: str = "", kind: str = ""):
|
|
96
102
|
"""统一提取入口:按 kind(或从 path 推断)分发到对应 extractor。"""
|
|
97
103
|
k = kind or kind_of_path(path)
|
|
@@ -101,20 +107,24 @@ def extract(source: str, path: str = "", kind: str = ""):
|
|
|
101
107
|
return _mod(k).extract(source, path)
|
|
102
108
|
|
|
103
109
|
|
|
110
|
+
# 生效条件:kind 为 'code_ref'/'doc_ref' 时返回 _mod(kind).node_id(item),其他 kind 由 _mod 抛 ValueError。
|
|
104
111
|
def node_id_of(item: dict, kind: str) -> str:
|
|
105
112
|
return _mod(kind).node_id(item)
|
|
106
113
|
|
|
107
114
|
|
|
115
|
+
# 生效条件:kind 为 'code_ref'/'doc_ref' 时返回 _mod(kind).render(item),其他 kind 由 _mod 抛 ValueError。
|
|
108
116
|
def render_of(item: dict, kind: str) -> str:
|
|
109
117
|
return _mod(kind).render(item)
|
|
110
118
|
|
|
111
119
|
|
|
120
|
+
# 生效条件:node 的 frontmatter 中 REF_KEYS 命中且值为非空 dict 时返回 {'ref': ref, 'ref_kind': kind},否则返回 {'ref': None, 'ref_kind': ''}。
|
|
112
121
|
def ref_fields(node) -> dict:
|
|
113
122
|
"""节点 → 检索结果要带的两字段(读侧只加字段,不改召回逻辑)。"""
|
|
114
123
|
kind, ref = ref_of(node)
|
|
115
124
|
return {"ref": ref, "ref_kind": kind} if ref else {"ref": None, "ref_kind": ""}
|
|
116
125
|
|
|
117
126
|
|
|
127
|
+
# 生效条件:node 的 frontmatter 按 REF_KEYS 顺序取到第一个非空 dict 时返回 (k, r),否则返回 ('', None)。
|
|
118
128
|
def ref_of(node) -> tuple:
|
|
119
129
|
"""从节点 frontmatter 取 ref:返回 (kind, ref) 或 ('', None)。"""
|
|
120
130
|
fm = (node or {}).get("frontmatter") or {}
|
|
@@ -129,14 +139,17 @@ def ref_of(node) -> tuple:
|
|
|
129
139
|
# 索引水位(_refindex.json):增量 + 截断留痕
|
|
130
140
|
# --------------------------------------------------------------------------
|
|
131
141
|
|
|
142
|
+
# 生效条件:以 root 为必填实参构造,实例化即置 self.root=root、self.path=os.path.join(root, LEDGER_FILE)、self._d=None;
|
|
132
143
|
class Ledger:
|
|
133
144
|
"""`<root>/_refindex.json`:每个源文件的 (size, mtime) 水位 + 节点区间。"""
|
|
134
145
|
|
|
146
|
+
# 生效条件:当传入 root 时,self.root 取该 root,self.path 为 os.path.join(root, LEDGER_FILE),self._d 置为 None;
|
|
135
147
|
def __init__(self, root: str):
|
|
136
148
|
self.root = root
|
|
137
149
|
self.path = os.path.join(root, LEDGER_FILE)
|
|
138
150
|
self._d = None
|
|
139
151
|
|
|
152
|
+
# 生效条件:当 self._d is not None 时直接返回 self._d;否则读取 self.path 的 JSON,仅当 obj 是 dict 且 obj.get("schema") == SCHEMA 且 obj.get("files") 是 dict 时用 obj,否则(含 OSError/ValueError、结构不符)回落为 {"schema": SCHEMA, "updated_at": 0.0, "files": {}} 并缓存返回;
|
|
140
153
|
def load(self) -> dict:
|
|
141
154
|
if self._d is not None:
|
|
142
155
|
return self._d
|
|
@@ -152,6 +165,7 @@ class Ledger:
|
|
|
152
165
|
self._d = d or {"schema": SCHEMA, "updated_at": 0.0, "files": {}}
|
|
153
166
|
return self._d
|
|
154
167
|
|
|
168
|
+
# 生效条件:传入 rel、fp 时,若 self.load()["files"].get(_src_key(fp)) 缺失或为假值、或 os.stat(fp) 抛 OSError、或条目 e.get("size") != st.st_size,则返回 False;否则返回 abs(float(e.get("mtime") or 0.0) - st.st_mtime) < 1e-6(mtime 缺失或假值时按 0.0);
|
|
155
169
|
def is_fresh(self, rel: str, fp: str) -> bool:
|
|
156
170
|
"""源文件自上次索引后未变(size + mtime 双等)→ 可跳过不重切。"""
|
|
157
171
|
e = self.load()["files"].get(_src_key(fp))
|
|
@@ -165,6 +179,7 @@ class Ledger:
|
|
|
165
179
|
return False
|
|
166
180
|
return abs(float(e.get("mtime") or 0.0) - st.st_mtime) < 1e-6
|
|
167
181
|
|
|
182
|
+
# 生效条件:当 rel、fp、kind、nodes 传入且 os.stat(fp) 成功时,向 self.load()["files"][_src_key(fp)] 写条目,其中 root 为 root if root else os.path.dirname(key)、path 为 rel、kind 为 kind、size/mtime 取 st、nodes 为每项 n.get("id")/n.get("lineno")/n.get("end")/n.get("hash");os.stat(fp) 抛 OSError 时不写入;
|
|
168
183
|
def record(self, rel: str, fp: str, kind: str, nodes,
|
|
169
184
|
root: str = None) -> None:
|
|
170
185
|
"""记一个源文件的水位(节点区间用于判 stale)。
|
|
@@ -190,9 +205,11 @@ class Ledger:
|
|
|
190
205
|
],
|
|
191
206
|
}
|
|
192
207
|
|
|
208
|
+
# 生效条件:当传入 fp 时,self.load()["files"].pop(_src_key(fp), None),即删除对应键(不存在也静默);
|
|
193
209
|
def drop(self, fp: str) -> None:
|
|
194
210
|
self.load()["files"].pop(_src_key(fp), None)
|
|
195
211
|
|
|
212
|
+
# 生效条件:当传入 root、kind、seen 时,对 self.load()["files"] 中满足 os.path.abspath(e.get("root") or "") == os.path.abspath(root) 且 e.get("kind") == kind 且键 k 不在 seen 的条目删除,返回删除数量;
|
|
196
213
|
def reconcile(self, root: str, kind: str, seen) -> int:
|
|
197
214
|
"""一次**完整**索引后对账:本 (root, kind) 下没被扫到的旧条目剪掉。
|
|
198
215
|
|
|
@@ -209,6 +226,7 @@ class Ledger:
|
|
|
209
226
|
files.pop(k, None)
|
|
210
227
|
return len(dead)
|
|
211
228
|
|
|
229
|
+
# 生效条件:对 load()["files"] 中「条目 root(为假值时用 os.path.dirname(键) 兜底)不是目录」的条目逐一 pop 并返回删除条数,无匹配时返回 0。
|
|
212
230
|
def prune(self) -> int:
|
|
213
231
|
"""剪掉「源大域已不存在」的条目(整个目录被搬走/删除)。
|
|
214
232
|
|
|
@@ -222,6 +240,7 @@ class Ledger:
|
|
|
222
240
|
files.pop(k, None)
|
|
223
241
|
return len(dead)
|
|
224
242
|
|
|
243
|
+
# 生效条件:当 kind、root、files、indexed、truncated 传入时,self.load()["last_index"] 被设为含 ts=_now()、kind、root、files、indexed、truncated=bool(truncated)、truncated_reason=reason or "" 的字典;reason 为假值(默认 ""/None)时 truncated_reason 回落 "";
|
|
225
244
|
def note_index(self, *, kind: str, root: str, files: int, indexed: int,
|
|
226
245
|
truncated: bool, reason: str = "") -> None:
|
|
227
246
|
"""记「最近一次索引」结果——截断在这里留痕,供 diagnose 看见。"""
|
|
@@ -231,12 +250,14 @@ class Ledger:
|
|
|
231
250
|
"truncated_reason": reason or "",
|
|
232
251
|
}
|
|
233
252
|
|
|
253
|
+
# 生效条件:无参数调用即生效,取 self.load() 结果把 updated_at 置为 _now(),再以 atomic_write 把 json.dumps(..., ensure_ascii=False, indent=1, sort_keys=True) 写入 self.path,无返回值。
|
|
234
254
|
def save(self) -> None:
|
|
235
255
|
d = self.load()
|
|
236
256
|
d["updated_at"] = _now()
|
|
237
257
|
atomic_write(self.path, json.dumps(d, ensure_ascii=False,
|
|
238
258
|
indent=1, sort_keys=True))
|
|
239
259
|
|
|
260
|
+
# 生效条件:无参数调用即生效,返回含 path、schema、load()["files"] 条目数、nodes 总数(各条目 nodes 列表长度之和)、updated_at、exists=os.path.isfile(self.path) 的 out;age_s 在 updated_at 为假值(0.0)时为 None,否则为 max(0.0, time.time()-up);仅当 load() 的 last_index 为 dict 时才并入 out["last_index"]。
|
|
240
261
|
def summary(self) -> dict:
|
|
241
262
|
d = self.load()
|
|
242
263
|
files = d.get("files") or {}
|
|
@@ -260,6 +281,7 @@ class Ledger:
|
|
|
260
281
|
# 统一 index_dir:调度 + 水位 + 落盘(供 op=index_code / op=index_doc / heal 共用)
|
|
261
282
|
# --------------------------------------------------------------------------
|
|
262
283
|
|
|
284
|
+
# 生效条件:root 为源大域根、kind 为 'code_ref'/'doc_ref' 时经 _mod(kind) 调度底层 index_dir 并返回 (items, errors, stats);ledger 非空时逐文件 record,incremental 为真时跳过 ledger.is_fresh 为真的文件,且 stats 未截断时执行 reconcile。
|
|
263
285
|
def index_dir(root: str, *, kind: str, patterns=None, max_files: int = 500,
|
|
264
286
|
max_items: int = 2000, incremental: bool = False,
|
|
265
287
|
ledger: "Ledger" = None, skip_dirs=None):
|
|
@@ -279,12 +301,14 @@ def index_dir(root: str, *, kind: str, patterns=None, max_files: int = 500,
|
|
|
279
301
|
seen = set() # 本次真正走过的源文件(用于对账)
|
|
280
302
|
if ledger is not None:
|
|
281
303
|
if incremental:
|
|
304
|
+
# 生效条件:当 rel、fp 传入时,ok = ledger.is_fresh(rel, fp);若 ok 为真则将 _src_key(fp) 加入 seen 并返回 ok,若 ok 为假则直接返回 False;
|
|
282
305
|
def fresh(rel, fp): # noqa: E306
|
|
283
306
|
ok = ledger.is_fresh(rel, fp)
|
|
284
307
|
if ok:
|
|
285
308
|
seen.add(_src_key(fp))
|
|
286
309
|
return ok
|
|
287
310
|
|
|
311
|
+
# 生效条件:当 rel、fp、got 传入时,将 _src_key(fp) 加入 seen,并以 root=root 调用 ledger.record(rel, fp, kind, [{"id": node_id_of(it, kind), "lineno": it.get("lineno"), "end": it.get("end"), "hash": it.get("hash")} for it in got]);
|
|
288
312
|
def on_file(rel, fp, got): # noqa: E306
|
|
289
313
|
seen.add(_src_key(fp))
|
|
290
314
|
ledger.record(rel, fp, kind,
|
|
@@ -308,6 +332,7 @@ def index_dir(root: str, *, kind: str, patterns=None, max_files: int = 500,
|
|
|
308
332
|
return items, errors, stats
|
|
309
333
|
|
|
310
334
|
|
|
335
|
+
# 生效条件:it['path'] 非空时返回其首段 path.split('/')[0] 作为 domain 键,path 为空返回 'orphan'。
|
|
311
336
|
def _domain_of(it: dict) -> str:
|
|
312
337
|
"""条目 → 路由域键(供 `tags` 的 `domain:` 显式声明)。
|
|
313
338
|
|
|
@@ -323,6 +348,7 @@ def _domain_of(it: dict) -> str:
|
|
|
323
348
|
return path.split("/")[0] or "orphan"
|
|
324
349
|
|
|
325
350
|
|
|
351
|
+
# 生效条件:kind == 'code_ref' 时按 codeindex.node_id/render 写入 cg(tags 含 'code'、code_ref=_code_ref(it, root)),kind == 'doc_ref' 时按 docindex 写入(tags 含 'doc'、doc_ref=_doc_ref(it, root)、密级取自 docindex.sensitivity_for(it['path'], sensitivity)),其他 kind 抛 ValueError,返回 (ids, sens)。
|
|
326
352
|
def add_items(cg, items, *, kind: str, root: str, layer=None, sensitivity=None,
|
|
327
353
|
layer_of=None):
|
|
328
354
|
"""把索引条目写进认知图(code / doc 的落盘细节收在这里,唯一实现)。
|
|
@@ -370,15 +396,21 @@ def add_items(cg, items, *, kind: str, root: str, layer=None, sensitivity=None,
|
|
|
370
396
|
return ids, sens
|
|
371
397
|
|
|
372
398
|
|
|
373
|
-
|
|
399
|
+
# 生效条件:把入参 root 原样写入返回 dict 的 'root',path/name/kind/lineno/end/lang/hash 按 it.get 取值(缺省 None),precise 取 bool(it.get('precise', True)),render_version 取传入值(传入 None 时延迟 import codeindex 取 codeindex.RENDER_VERSION,保证与 render 契约**同源**、无第二处硬编码)。
|
|
400
|
+
def _code_ref(it: dict, root: str, render_version=None) -> dict:
|
|
401
|
+
if render_version is None: # 直接调用点的兜底:与 render 产物同源
|
|
402
|
+
from . import codeindex
|
|
403
|
+
render_version = codeindex.RENDER_VERSION
|
|
374
404
|
return {
|
|
375
405
|
"path": it.get("path"), "name": it.get("name"),
|
|
376
406
|
"kind": it.get("kind"), "lineno": it.get("lineno"), "end": it.get("end"),
|
|
377
407
|
"lang": it.get("lang"), "precise": bool(it.get("precise", True)),
|
|
378
408
|
"hash": it.get("hash"), "root": root,
|
|
409
|
+
"render_version": render_version,
|
|
379
410
|
}
|
|
380
411
|
|
|
381
412
|
|
|
413
|
+
# 生效条件:把入参 root 原样写入返回 dict 的 'root',path/heading/heading_path/level/lineno/end/anchor/hash/lang 按 it.get 取值(缺省 None),precise 取 bool(it.get('precise', True))。
|
|
382
414
|
def _doc_ref(it: dict, root: str) -> dict:
|
|
383
415
|
return {
|
|
384
416
|
"path": it.get("path"), "heading": it.get("heading"),
|
|
@@ -393,6 +425,7 @@ def _doc_ref(it: dict, root: str) -> dict:
|
|
|
393
425
|
# 回读(唯一实现:op=ref 与 check_refs 共用)
|
|
394
426
|
# --------------------------------------------------------------------------
|
|
395
427
|
|
|
428
|
+
# 生效条件:传入 ref 为假值(如 None/{})时按 {} 处理,rel 取 ref.get("path") or "";root 与 ref.get("root") 均为假值时返回含 ref/path/status:"unresolved"/ok:False/error:"ref 未记录 root..." 的 out;否则用 root or ref.get("root") 与 rel 拼 fp,os.path.isfile(fp) 为假时返回 status:"dangling"、stale:True,读取抛 OSError/UnicodeDecodeError 时返回 status:"error";读取成功时 lineno 取 int(ref.get("lineno") or 1)(假值回落 1)、end 取 int(ref.get("end") or lineno)(假值回落 lineno),ref.get("hash") 为 None 时 match=None、ok=True、status:"ok",ref.get("hash") 为真值且等于 region_hash 时 ok=True/status:"ok"、不等时 ok=False/status:"stale",ref.get("hash") 为假值但非 None(如 ""/0/False)时 ok=False/status:"stale";with_text 为真时 out["text"] 取 lines[max(0,lineno-1):max(max(0,lineno-1),end)] 的 join;
|
|
396
429
|
def probe_ref(ref: dict, *, root: str = None, with_text: bool = False) -> dict:
|
|
397
430
|
"""只读探测单个 ref 的状态(不回读整篇,除非 with_text)。"""
|
|
398
431
|
from . import codeindex
|
|
@@ -433,6 +466,7 @@ def probe_ref(ref: dict, *, root: str = None, with_text: bool = False) -> dict:
|
|
|
433
466
|
return out
|
|
434
467
|
|
|
435
468
|
|
|
469
|
+
# 生效条件:ref 经 probe_ref(root=root, with_text=True) 后 status 为 'ok'/'stale' 时返回 ok=True 及 text/total_lines/hash/hash_match/stale/precise,status 为 'unresolved'/'error'/'dangling' 时返回 ok=False 与 error。
|
|
436
470
|
def read_ref(ref: dict, *, root: str = None, ref_kind: str = "ref") -> dict:
|
|
437
471
|
"""按 ref 回读源区间——`op=ref` 与 `check_refs` 的唯一实现。"""
|
|
438
472
|
p = probe_ref(ref, root=root, with_text=True)
|
|
@@ -452,6 +486,7 @@ def read_ref(ref: dict, *, root: str = None, ref_kind: str = "ref") -> dict:
|
|
|
452
486
|
}
|
|
453
487
|
|
|
454
488
|
|
|
489
|
+
# 生效条件:cg 的 index['nodes'] 非空时汇总 stale/dangling/unresolved/errors 并返回 ok =(无 stale 且无 dangling);only_tagged 为真时只探测 tags 含 'code'/'doc' 的节点,ledger 非空时先走 (size, mtime) 快路径。
|
|
455
490
|
def check_refs(cg, *, ledger: "Ledger" = None, max_nodes: int = MAX_CHECK,
|
|
456
491
|
only_tagged: bool = True) -> dict:
|
|
457
492
|
"""漂移 / 悬空巡检(只读、不抛)。
|
|
@@ -468,6 +503,7 @@ def check_refs(cg, *, ledger: "Ledger" = None, max_nodes: int = MAX_CHECK,
|
|
|
468
503
|
stale, dangling, unresolved, errors = [], [], [], []
|
|
469
504
|
covered = set()
|
|
470
505
|
|
|
506
|
+
# 生效条件:当 nid、ref、kind、rel 传入时,p = probe_ref(ref) 后按 p["status"] 分派:为 "dangling" 时把含 node_id/ref_kind/path/lineno/end/error 的 row 加入 dangling,为 "stale" 时补 hash_expected/hash 加入 stale,为 "unresolved" 时加入 unresolved,为 "error" 时加入 errors;其他状态不加入;
|
|
471
507
|
def _probe_one(nid, ref, kind, rel):
|
|
472
508
|
p = probe_ref(ref)
|
|
473
509
|
row = {"node_id": nid, "ref_kind": kind, "path": rel,
|
|
@@ -509,6 +545,7 @@ def check_refs(cg, *, ledger: "Ledger" = None, max_nodes: int = MAX_CHECK,
|
|
|
509
545
|
kind, rel)
|
|
510
546
|
|
|
511
547
|
# 回退:ledger 未覆盖的索引节点
|
|
548
|
+
# 生效条件:当 nid 传入时,若 only_tagged 为假值立即返回 True;否则取 (nodes.get(nid) or {}).get("tags") or [],仅当其中存在 "code" 或 "doc" 返回 True,否则返回 False;
|
|
512
549
|
def _candidate(nid):
|
|
513
550
|
if not only_tagged:
|
|
514
551
|
return True
|
|
@@ -556,11 +593,13 @@ def check_refs(cg, *, ledger: "Ledger" = None, max_nodes: int = MAX_CHECK,
|
|
|
556
593
|
# `op=ref action=prune`(悬空)使用,避免两处各写一套口径。
|
|
557
594
|
# --------------------------------------------------------------------------
|
|
558
595
|
|
|
596
|
+
# 生效条件:返回 os.path.normcase(os.path.abspath(str(p or ''))),即 p 为 None/空串时返回当前目录的归一绝对路径。
|
|
559
597
|
def _norm_root(p) -> str:
|
|
560
598
|
"""root 归一:同一目录的大小写/分隔符差异不得影响「同一大域」判定。"""
|
|
561
599
|
return os.path.normcase(os.path.abspath(str(p or "")))
|
|
562
600
|
|
|
563
601
|
|
|
602
|
+
# 生效条件:a 与 b 都非空且 _norm_root(a) == _norm_root(b) 时返回 True,否则(含 TypeError/ValueError)返回 False。
|
|
564
603
|
def _same_root(a, b) -> bool:
|
|
565
604
|
try:
|
|
566
605
|
return bool(a) and bool(b) and _norm_root(a) == _norm_root(b)
|
|
@@ -568,10 +607,12 @@ def _same_root(a, b) -> bool:
|
|
|
568
607
|
return False
|
|
569
608
|
|
|
570
609
|
|
|
610
|
+
# 生效条件:返回 str(p or '').replace('\\', '/').lstrip('./'),即 p 为 None/空串时返回 ''。
|
|
571
611
|
def _norm_rel(p) -> str:
|
|
572
612
|
return str(p or "").replace("\\", "/").lstrip("./")
|
|
573
613
|
|
|
574
614
|
|
|
615
|
+
# 生效条件:cg 具备可调用的 forget 方法时对 plan 中每个 nid 调 cg.forget(nid, why),返回 (成功 id 列表, 被拦下/失败的 {node_id, error} 列表);cg 无 forget 时返回 ([], plan 中每 nid 一条错误)。
|
|
575
616
|
def _forget_many(cg, plan, why: str) -> tuple:
|
|
576
617
|
"""逐条软删(进 trash/、写删除清单、可 restore);受保护节点拦下不删。
|
|
577
618
|
|
|
@@ -598,6 +639,27 @@ def _forget_many(cg, plan, why: str) -> tuple:
|
|
|
598
639
|
return done, blocked
|
|
599
640
|
|
|
600
641
|
|
|
642
|
+
# 生效条件:cg 具备可调用的 _unstage 时对 ghosts 逐个调用并收集成功 id(单条异常跳过),cg 无该能力时返回 [](不假装成功)。
|
|
643
|
+
def _drop_ghosts(cg, ghosts) -> list:
|
|
644
|
+
"""摘除幽灵条目的索引记录(节点文件已不存在,没有可软删的实体)。
|
|
645
|
+
|
|
646
|
+
走 `MdCG._unstage`:它顺带落删除记录,保证幽灵不会再次从分片日志里复活。
|
|
647
|
+
基础层没有该能力时如实返回空列表(不假装成功)。
|
|
648
|
+
"""
|
|
649
|
+
drop = getattr(cg, "_unstage", None)
|
|
650
|
+
if not callable(drop):
|
|
651
|
+
return []
|
|
652
|
+
out = []
|
|
653
|
+
for nid in ghosts:
|
|
654
|
+
try:
|
|
655
|
+
drop(nid)
|
|
656
|
+
out.append(nid)
|
|
657
|
+
except Exception: # 单条失败不拖垮整批
|
|
658
|
+
continue
|
|
659
|
+
return out
|
|
660
|
+
|
|
661
|
+
|
|
662
|
+
# 生效条件:items 中同 kind 的节点若其 ref['path'] 命中本次 items 的文件、id 不在本次产出内且 ref['root'] 与入参 root 同一(_same_root),则列入清退计划;dry_run 为真只返回计划,否则经 _forget_many 软删;items 为空时返回 count 0。
|
|
601
663
|
def prune_orphans(cg, *, kind: str, root: str, items, dry_run: bool = False,
|
|
602
664
|
reason: str = "") -> dict:
|
|
603
665
|
"""清退「同 root + 同 path,但已不在本次产出里」的**过期代**节点。
|
|
@@ -656,6 +718,7 @@ def prune_orphans(cg, *, kind: str, root: str, items, dry_run: bool = False,
|
|
|
656
718
|
"skipped_protected": blocked[:20], "reason": why}
|
|
657
719
|
|
|
658
720
|
|
|
721
|
+
# 生效条件:cg 中带 'code'/'doc' 标签且 ref 带 root 的节点经 probe_ref 判为 'dangling' 时列入清退计划;only_roots 为空时另收集 cg.root 下取不到对应节点 .md 的幽灵条目经 _drop_ghosts 摘除;dry_run 为真只返回计划。
|
|
659
722
|
def prune_dangling(cg, *, only_roots=None, dry_run: bool = False,
|
|
660
723
|
max_nodes: int = MAX_CHECK, reason: str = "") -> dict:
|
|
661
724
|
"""清退**悬空**节点:ref 指向的源文件已删除,回读必然失败。
|
|
@@ -663,8 +726,23 @@ def prune_dangling(cg, *, only_roots=None, dry_run: bool = False,
|
|
|
663
726
|
`check_refs` 只报告不处置(其原话是「悬空需人工处置」),本函数就是那个
|
|
664
727
|
出口——已删脚本、被搬走的文档留下的残留节点一次清掉,而不是逐条手工
|
|
665
728
|
`forget`。判定与巡检共用 `probe_ref` 的唯一实现,口径不会打架。
|
|
729
|
+
|
|
730
|
+
另清**幽灵条目**(ghosts):索引有条目、节点文件却不存在。它们是历史
|
|
731
|
+
「删除只摘内存索引、不落盘」的遗留——`cg.get` 取不回 → 悬空清退够不着它,
|
|
732
|
+
而 `check_refs` 走 ledger 会一直报 → dangling 永不归零。判据只用唯一真源
|
|
733
|
+
(节点 .md 不存在即脏索引),与「索引是派生物」的宣言一致;`only_roots`
|
|
734
|
+
非空时跳过(幽灵条目无 ref,无法归因到某个源大域)。
|
|
666
735
|
"""
|
|
667
736
|
nodes = (getattr(cg, "index", {}) or {}).get("nodes") or {}
|
|
737
|
+
ghosts = []
|
|
738
|
+
if not only_roots:
|
|
739
|
+
for nid, e in list(nodes.items()):
|
|
740
|
+
tags = (e or {}).get("tags") or []
|
|
741
|
+
if not any(t in ("code", "doc") for t in tags):
|
|
742
|
+
continue
|
|
743
|
+
path = (e or {}).get("path") or ""
|
|
744
|
+
if path and not os.path.exists(os.path.join(cg.root, path)):
|
|
745
|
+
ghosts.append(nid)
|
|
668
746
|
# 同 prune_orphans:ref 只在节点 frontmatter 里,索引条目里没有,
|
|
669
747
|
# 必须 cg.get 取回节点再 ref_of(否则恒空、静默不删)。
|
|
670
748
|
todo = []
|
|
@@ -684,7 +762,7 @@ def prune_dangling(cg, *, only_roots=None, dry_run: bool = False,
|
|
|
684
762
|
if only_roots and not any(_same_root(ref.get("root"), r) for r in only_roots):
|
|
685
763
|
continue
|
|
686
764
|
todo.append((nid, ref))
|
|
687
|
-
truncated = len(todo) > max_nodes
|
|
765
|
+
truncated = len(todo) > max_nodes or len(ghosts) > max_nodes
|
|
688
766
|
plan = []
|
|
689
767
|
for nid, ref in todo[:max_nodes]:
|
|
690
768
|
try:
|
|
@@ -695,17 +773,23 @@ def prune_dangling(cg, *, only_roots=None, dry_run: bool = False,
|
|
|
695
773
|
|
|
696
774
|
why = reason or ("源文件已删除,索引节点悬空(回读必然失败),"
|
|
697
775
|
"清退以消除永不消失的 dangling")
|
|
776
|
+
ghost_plan = sorted(ghosts)[:max_nodes]
|
|
698
777
|
base = {"scanned": len(nodes), "candidates": len(plan),
|
|
778
|
+
"ghosts": len(ghost_plan),
|
|
699
779
|
"dry_run": bool(dry_run), "truncated": truncated,
|
|
700
780
|
"max_nodes": max_nodes}
|
|
701
781
|
if dry_run:
|
|
702
782
|
return {**base, "ok": True, "count": len(plan), "pruned": sorted(plan)[:50],
|
|
783
|
+
"ghost_pruned": ghost_plan[:50],
|
|
703
784
|
"skipped_protected": [], "reason": why}
|
|
704
785
|
done, blocked = _forget_many(cg, plan, why)
|
|
786
|
+
dropped = _drop_ghosts(cg, ghost_plan)
|
|
705
787
|
return {**base, "ok": True, "count": len(done), "pruned": done[:50],
|
|
788
|
+
"ghost_pruned": dropped[:50],
|
|
706
789
|
"skipped_protected": blocked[:20], "reason": why}
|
|
707
790
|
|
|
708
791
|
|
|
792
|
+
# 生效条件:cg 节点按 ref['root'] 与 kind 分组后逐组以 index_dir(incremental=False, ledger=ledger) 重切、再以 add_items(layer_of=原 layer) 重建,返回 {'ok','roots','groups','indexed','errors','truncated'};only_roots 非 None 时只处理其中列出的 root。
|
|
709
793
|
def rebuild(cg, *, ledger: "Ledger" = None, only_roots=None, max_files: int = 500,
|
|
710
794
|
max_items: int = 2000) -> dict:
|
|
711
795
|
"""按 ref 记录的 root 重建索引(sustain.heal 的修复动作)。
|
|
@@ -747,4 +831,4 @@ def rebuild(cg, *, ledger: "Ledger" = None, only_roots=None, max_files: int = 50
|
|
|
747
831
|
out["errors"].append(f"{root} [{kind}]:{exc}")
|
|
748
832
|
if ledger is not None:
|
|
749
833
|
ledger.save()
|
|
750
|
-
return out
|
|
834
|
+
return out
|
package/md_cg/refine.py
CHANGED
|
@@ -94,25 +94,30 @@ SPEC = {
|
|
|
94
94
|
|
|
95
95
|
# ---- 通用工具 -------------------------------------------------------------
|
|
96
96
|
|
|
97
|
+
# 生效条件:x 为 str 时返回 MdCGOS(x),否则原样返回 x;
|
|
97
98
|
def _as_cg(x):
|
|
98
99
|
return MdCGOS(x) if isinstance(x, str) else x
|
|
99
100
|
|
|
100
101
|
|
|
102
|
+
# 生效条件:cg 具 "root" 属性时以该值为根、否则以 str(cg) 为根,与模块常量 REFINE_LOG 拼接返回;
|
|
101
103
|
def _log_path(cg) -> str:
|
|
102
104
|
root = cg.root if hasattr(cg, "root") else str(cg)
|
|
103
105
|
return os.path.join(root, REFINE_LOG)
|
|
104
106
|
|
|
105
107
|
|
|
108
|
+
# 生效条件:read_jsonl(_log_path(cg)) 返回假值时按空列表处理,仅保留 action 字段等于 "refine" 的记录;
|
|
106
109
|
def _read_log(cg) -> list:
|
|
107
110
|
return [r for r in (read_jsonl(_log_path(cg)) or [])
|
|
108
111
|
if r.get("action") == "refine"]
|
|
109
112
|
|
|
110
113
|
|
|
114
|
+
# 生效条件:cg 无 index 或 index["nodes"] 为假值时以空字典查找;nodes.get(nid) 为假值时返回空字典 {};
|
|
111
115
|
def _entry(cg, nid):
|
|
112
116
|
nodes = (getattr(cg, "index", None) or {}).get("nodes") or {}
|
|
113
117
|
return nodes.get(nid) or {}
|
|
114
118
|
|
|
115
119
|
|
|
120
|
+
# 生效条件:取 _entry(cg, nid) 的 path(假值回落空串)的父目录 basename;该 basename 为假值时返回 "(root)";
|
|
116
121
|
def _family(cg, nid):
|
|
117
122
|
"""家族 = 节点所在 `cond_*` 目录名(无则 `(root)`)。"""
|
|
118
123
|
p = str(_entry(cg, nid).get("path") or "").replace("\\", "/")
|
|
@@ -120,11 +125,13 @@ def _family(cg, nid):
|
|
|
120
125
|
return d or "(root)"
|
|
121
126
|
|
|
122
127
|
|
|
128
|
+
# 生效条件:对传入的 parts 逐项 str 后以 "|" 连接并 UTF-8 编码,返回 sha1 的 hexdigest;
|
|
123
129
|
def _sha(*parts) -> str:
|
|
124
130
|
h = hashlib.sha1("|".join(str(p) for p in parts).encode("utf-8"))
|
|
125
131
|
return h.hexdigest()
|
|
126
132
|
|
|
127
133
|
|
|
134
|
+
# 生效条件:遍历 cg.index 的 nodes,仅收 str(nid) 以 prefix 开头且条目 protected 为假值的 nid 进 ids(排序后返回),protected 为真值的计入 protected 计数;
|
|
128
135
|
def _pool(cg, prefix) -> tuple:
|
|
129
136
|
"""抽检池:id 以 prefix 开头、非受保护节点(与 induce 的池口径一致)。"""
|
|
130
137
|
nodes = (getattr(cg, "index", None) or {}).get("nodes") or {}
|
|
@@ -140,6 +147,7 @@ def _pool(cg, prefix) -> tuple:
|
|
|
140
147
|
return ids, protected
|
|
141
148
|
|
|
142
149
|
|
|
150
|
+
# 生效条件:cg 的 id 池经 prefix(假值回落 PREFIX_DEFAULT)过滤后,n 为 None 用 SAMPLE_N、否则 int(n),seed 假值回落 SAMPLE_SEED;pool 为空或 n<=0 时返回 ([], meta),否则按家族分层最大余数分配并在层内按 _sha(seed, id) 升序取前 k 个返回 (picked, meta);
|
|
143
151
|
def sample_ids(cg, n=None, seed=None, prefix=None) -> tuple:
|
|
144
152
|
"""确定性分层抽样(家族=层,家族内按 sha1(seed|id) 排序)。
|
|
145
153
|
|
|
@@ -180,6 +188,7 @@ def sample_ids(cg, n=None, seed=None, prefix=None) -> tuple:
|
|
|
180
188
|
return picked, meta
|
|
181
189
|
|
|
182
190
|
|
|
191
|
+
# 生效条件:对 cg 中 nid 对应节点(cg.get(nid) 为假值时用空字典)生成字段;content 长度大于 EXCERPT_MAX 时 body 截断且 body_truncated 为 True,否则 body 为全 content;
|
|
183
192
|
def _item(cg, nid) -> dict:
|
|
184
193
|
node = cg.get(nid) or {}
|
|
185
194
|
fm = node.get("frontmatter") or {}
|
|
@@ -203,6 +212,7 @@ def _item(cg, nid) -> dict:
|
|
|
203
212
|
|
|
204
213
|
# ---- 预演:与 induce 同源的聚类口径 ---------------------------------------
|
|
205
214
|
|
|
215
|
+
# 生效条件:pool 中能取到 grams 的 nid 进入 cache;按 keys 顺序贪心,与当前 a 的 _jaccard >= float(min_jaccard) 且未分配的后续 b 并入组,组大小达到 int(min_cluster) 才成组,返回 (cache, keys, groups);
|
|
206
216
|
def _cluster(cg, pool, min_jaccard, min_cluster) -> tuple:
|
|
207
217
|
"""贪心聚类(与 `consolidate.induce_memories` 同源)。返回 (cache, keys, groups)。"""
|
|
208
218
|
from . import subgraph
|
|
@@ -228,6 +238,7 @@ def _cluster(cg, pool, min_jaccard, min_cluster) -> tuple:
|
|
|
228
238
|
return cache, keys, groups
|
|
229
239
|
|
|
230
240
|
|
|
241
|
+
# 生效条件:terms 为空时对每个 t 返回 df[t]=0;否则对 keys 中每个 k 的 cache[k]["pos"] 统计各 term 出现次数,返回 df;
|
|
231
242
|
def _term_df(cache, keys, terms) -> dict:
|
|
232
243
|
"""词面在抽检样本内的文档频率(不含语义推断)。"""
|
|
233
244
|
df = {t: 0 for t in terms}
|
|
@@ -241,6 +252,7 @@ def _term_df(cache, keys, terms) -> dict:
|
|
|
241
252
|
return df
|
|
242
253
|
|
|
243
254
|
|
|
255
|
+
# 生效条件:x 转为 cg;min_jaccard 为 None 取 MIN_JACCARD、否则 float(min_jaccard),min_cluster 为 None 取 MIN_CLUSTER、否则 int(min_cluster);ids 为真值时 pool 为显式 ids 去重排序且 meta 标记 explicit_ids=True,否则 pool/meta 来自 sample_ids(cg, n=n, seed=seed, prefix=prefix);对 pool 聚类后,require_conditions 为真且某组 common 为空时跳过该组并累计 skipped_no_cond;返回含 candidates 与 sample_adequacy 的只读预演字典;
|
|
244
256
|
def preview(x, ids=None, n=None, seed=None, prefix=None,
|
|
245
257
|
min_jaccard=None, min_cluster=None, require_conditions=True) -> dict:
|
|
246
258
|
"""提炼预演(只读):抽样 → 同源聚类 → 共性条件 + 来源 + 泛化度标记。"""
|
|
@@ -316,6 +328,7 @@ def preview(x, ids=None, n=None, seed=None, prefix=None,
|
|
|
316
328
|
|
|
317
329
|
# ---- 工单 ---------------------------------------------------------------
|
|
318
330
|
|
|
331
|
+
# 生效条件:x 转为 cg;prefix 假值回落 PREFIX_DEFAULT,seed 假值回落 SAMPLE_SEED;ids 为真值时 sample 为显式 ids 去重排序且 real_pool=len(sample)、skipped=0、families=0,否则 sample/meta 来自 sample_ids(cg, n=n, seed=seed, prefix=prefix) 并取 meta["pool"]/meta["skipped_protected"]/meta["families"];items 为 sample 的 _item,preview 以 sample 为显式 ids 调用,返回只读工单;
|
|
319
332
|
def plan(x, ids=None, n=None, seed=None, prefix=None,
|
|
320
333
|
min_jaccard=None, min_cluster=None) -> dict:
|
|
321
334
|
"""抽检工单(只读):确定性样本 + 原文摘录 + 字段缺口 + 口径声明 + 预演。"""
|
|
@@ -366,6 +379,7 @@ def plan(x, ids=None, n=None, seed=None, prefix=None,
|
|
|
366
379
|
}
|
|
367
380
|
|
|
368
381
|
|
|
382
|
+
# 生效条件:x 转为 cg;target 为 None 取 CALIBRATE_TARGET、否则 int(target),cap 为 None 取 CALIBRATE_CAP、否则 int(cap),初始 n=min(SAMPLE_N if n0 is None else int(n0), cap);循环以 n 调 preview(cg, n=n, seed=seed, prefix=prefix, min_jaccard=min_jaccard, min_cluster=min_cluster) 并记录 step,直到 pv["clusters"] >= target 则 chosen=step 并 break,或 n >= min(cap, pool or cap) 则 break;chosen 为 None 时 blocked=True 并按 steps 汇总 recommend,否则 recommend 基于 chosen;返回只读校准报告;
|
|
369
383
|
def calibrate(x, target=None, cap=None, n0=None, seed=None, prefix=None,
|
|
370
384
|
min_jaccard=None, min_cluster=None) -> dict:
|
|
371
385
|
"""样本量校准(只读):20 条起步有界爬坡,直到产出 `target` 个候选或触顶。
|
|
@@ -425,6 +439,7 @@ def calibrate(x, target=None, cap=None, n0=None, seed=None, prefix=None,
|
|
|
425
439
|
|
|
426
440
|
# ---- 留痕与扩批闸门 ------------------------------------------------------
|
|
427
441
|
|
|
442
|
+
# 生效条件:candidates 中取 concept_id 为真值的 id 列表;verdicts 中为 dict 且 concept_id 非空且首次出现的条目计入 reviewed 并累加其中 faithful/added_info 的真值计数;ids 非空时按 reviewed < len(ids) → awaiting_human_review、added 非零 → added_info_detected、faithful_rate < GATE_MIN_PASS_RATE → faithful_rate_below_gate、否则 ok 且 expand_allowed=True;ids 为空时 reason="no_candidates" 且 expand_allowed=False;
|
|
428
443
|
def _verdict_stats(verdicts, candidates) -> dict:
|
|
429
444
|
ids = [c.get("concept_id") for c in candidates if c.get("concept_id")]
|
|
430
445
|
seen, faithful, added = set(), 0, 0
|
|
@@ -457,6 +472,7 @@ def _verdict_stats(verdicts, candidates) -> dict:
|
|
|
457
472
|
"expand_allowed": expand, "reason": reason}
|
|
458
473
|
|
|
459
474
|
|
|
475
|
+
# 生效条件:x 转为 cg;以 ids/n/seed/prefix/min_jaccard/min_cluster 调 plan 得到 p,batch 假值(含 None/空串)回落当前时间字符串;从 p["preview"]["candidates"] 与 verdicts 经 _verdict_stats 得 stats;把含 batch/actor/prefix/seed/sample/verdicts/gate/note 的 record 追加写入 _log_path(cg),返回 rep;
|
|
460
476
|
def apply(x, batch=None, actor=None, verdicts=None, note=None,
|
|
461
477
|
ids=None, n=None, seed=None, prefix=None,
|
|
462
478
|
min_jaccard=None, min_cluster=None) -> dict:
|
|
@@ -493,6 +509,7 @@ def apply(x, batch=None, actor=None, verdicts=None, note=None,
|
|
|
493
509
|
return rep
|
|
494
510
|
|
|
495
511
|
|
|
512
|
+
# 生效条件:x 转为 cg;batch 为真值时只保留 _read_log(cg) 中 batch 字段相等的记录,过滤后为空时返回 batches=0、expand_allowed=False、reason="no_batch";否则取最后一条记录,以其 verdicts 与 candidates(假值转空列表)经 _verdict_stats 复算并返回 expand_allowed/reason 等;
|
|
496
513
|
def gate(x, batch=None) -> dict:
|
|
497
514
|
"""扩批闸门:读留痕复算通过率(不写盘)。"""
|
|
498
515
|
cg = _as_cg(x)
|
|
@@ -517,6 +534,7 @@ def gate(x, batch=None) -> dict:
|
|
|
517
534
|
"闸门未放行,禁止扩大批次;不得盲跑全量。")}
|
|
518
535
|
|
|
519
536
|
|
|
537
|
+
# 生效条件:batch 为真值时只保留该批次记录,total 先记为过滤后条数;limit 非 None 且 >=0 时以 recs[-int(limit):] 截尾(limit=0 因 [-0:] 等价 [0:] 仍返回全部记录),limit 为 None 或负数时不截断;
|
|
520
538
|
def history(x, limit=100, batch=None) -> dict:
|
|
521
539
|
cg = _as_cg(x)
|
|
522
540
|
recs = _read_log(cg)
|
|
@@ -531,6 +549,7 @@ def history(x, limit=100, batch=None) -> dict:
|
|
|
531
549
|
|
|
532
550
|
# ---- CLI(真实库抽检/预演用;MCP 侧走 maintain action) -------------------
|
|
533
551
|
|
|
552
|
+
# 生效条件:解析 argv(None 时由 argparse 取 sys.argv)得到 root/n/seed/prefix/min-jaccard/min-cluster/report 等参数;a.calibrate 为真时调 calibrate 并打印 target/cap/pool/blocked/sample_adequacy/recommend 及每步摘要;否则 a.preview 为真选 preview、为假选 plan,调用后打印 thin 摘要与候选并返回 0;
|
|
534
553
|
def _main(argv=None) -> int:
|
|
535
554
|
import argparse
|
|
536
555
|
import json
|
|
@@ -583,4 +602,4 @@ def _main(argv=None) -> int:
|
|
|
583
602
|
|
|
584
603
|
if __name__ == "__main__":
|
|
585
604
|
import sys
|
|
586
|
-
sys.exit(_main())
|
|
605
|
+
sys.exit(_main())
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""md_cg · 角色化读取视图(第四阶段 6.1,计划文档 §4)
|
|
3
|
+
|
|
4
|
+
【为什么】同一认知图对不同消费角色应呈现不同侧面:主代理要「结论与
|
|
5
|
+
修正历史」,验证端要「判据面与环境陷阱」,回执审计要「命令与预期输出」。
|
|
6
|
+
三者的候选资格不同,但都建立在同一节点集上——视图是读取面的资格过滤,
|
|
7
|
+
不是数据的第二副本(与超边的派生视图正交:超边是图结构增补,视图是
|
|
8
|
+
候选资格过滤;计划 §3)。
|
|
9
|
+
|
|
10
|
+
【设计】
|
|
11
|
+
- ROLE_VIEWS:声明式规则表(唯一真源),每个 view 声明四维资格:
|
|
12
|
+
content_kinds —— content_kind 必须落在集合内(None=不限)
|
|
13
|
+
layers —— layer 必须落在集合内(None=不限)
|
|
14
|
+
roles —— 非 None 时作为正向白名单(role 必须命中,
|
|
15
|
+
短路返回;receipt 用)
|
|
16
|
+
include_work —— False 时 role 落在工作角色(tool-output/
|
|
17
|
+
command/edit)即一票否决(与候选池默认
|
|
18
|
+
include_work=False 同口径)
|
|
19
|
+
- matches(fm, view):单点谓词。fm 接受扁平 entry 或 frontmatter
|
|
20
|
+
字典(两者对 role/layer/content_kind 三键同名)。
|
|
21
|
+
- 非法 view 一律 ValueError(fail-closed,不静默回落)——合法性
|
|
22
|
+
判定单点在本模块;与节点缺字段 fail-open(缺 content_kind 按
|
|
23
|
+
"text" 对待)是两回事,后者是读取面惯例。
|
|
24
|
+
|
|
25
|
+
【边界】
|
|
26
|
+
- 零内部依赖(不 import 本包其它模块,防循环导入)。规则表内的
|
|
27
|
+
工作角色白名单与 mdcos.WORK_ROLES 字面一致,一致性由
|
|
28
|
+
test_role_views 守卫断言钉住(防两处漂移)。
|
|
29
|
+
- 初版规则是类型启发,bench_role_views.py 的对照校准是法定修正
|
|
30
|
+
通道(改规则必须复跑对照,计划 §7.3)。
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
# 规则表内的工作角色白名单,字面与 mdcos.WORK_ROLES 保持一致
|
|
34
|
+
# (一致性由 test_role_views 守卫断言钉住,防两处漂移)。
|
|
35
|
+
_WORK_ROLES = ("tool-output", "command", "edit")
|
|
36
|
+
|
|
37
|
+
ROLE_VIEWS = {
|
|
38
|
+
# 主代理视图:结论与修正历史(正文/工作结论落在 knowledge 层)
|
|
39
|
+
"main": {
|
|
40
|
+
"content_kinds": ("text", "work_done"),
|
|
41
|
+
"layers": ("knowledge",),
|
|
42
|
+
"roles": None,
|
|
43
|
+
"include_work": False,
|
|
44
|
+
},
|
|
45
|
+
# 验证端视图:判据面与环境陷阱(marks 条目 + 知识正文,含 contextual)
|
|
46
|
+
"verifier": {
|
|
47
|
+
"content_kinds": ("ccg_marks", "text"),
|
|
48
|
+
"layers": ("knowledge", "contextual"),
|
|
49
|
+
"roles": None,
|
|
50
|
+
"include_work": False,
|
|
51
|
+
},
|
|
52
|
+
# 回执审计视图:恰是默认候选池剔除的工作角色节点(include_work
|
|
53
|
+
# 默认 False 的补集),roles 白名单短路命中即保留
|
|
54
|
+
"receipt": {
|
|
55
|
+
"content_kinds": None,
|
|
56
|
+
"layers": None,
|
|
57
|
+
"roles": _WORK_ROLES,
|
|
58
|
+
"include_work": True,
|
|
59
|
+
},
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def views():
|
|
64
|
+
"""合法视图名(排序稳定,供错误信息与文档引用)。"""
|
|
65
|
+
return tuple(sorted(ROLE_VIEWS))
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def matches(fm, view):
|
|
69
|
+
"""节点元数据是否满足视图资格(单点谓词)。
|
|
70
|
+
|
|
71
|
+
fm:扁平 entry 或 frontmatter 字典(role/layer/content_kind 同名键)。
|
|
72
|
+
view:ROLE_VIEWS 键之一;非法值 ValueError(fail-closed)。
|
|
73
|
+
"""
|
|
74
|
+
spec = ROLE_VIEWS.get(view)
|
|
75
|
+
if spec is None:
|
|
76
|
+
raise ValueError(
|
|
77
|
+
"非法 view:%r(合法三值:%s)" % (view, ", ".join(views())))
|
|
78
|
+
if spec["roles"] is not None: # 正向白名单视图(receipt)
|
|
79
|
+
return fm.get("role") in spec["roles"]
|
|
80
|
+
ck = fm.get("content_kind")
|
|
81
|
+
if spec["content_kinds"] is not None \
|
|
82
|
+
and (ck or "text") not in spec["content_kinds"]:
|
|
83
|
+
return False
|
|
84
|
+
if spec["layers"] is not None \
|
|
85
|
+
and fm.get("layer") not in spec["layers"]:
|
|
86
|
+
return False
|
|
87
|
+
if not spec["include_work"] and fm.get("role") in _WORK_ROLES:
|
|
88
|
+
return False
|
|
89
|
+
return True
|