@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
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""M1 机械层入口(零写入):
|
|
3
|
+
|
|
4
|
+
python -m md_cg.mreview [--root R] [--out dump.json] [--full] [--limit N]
|
|
5
|
+
|
|
6
|
+
默认只打印/落盘**摘要 + 包清单 + 机械聚合**;``--full`` 追加条目明细与逐条机械 issue
|
|
7
|
+
(真源全量 dump 会很大,默认裁剪是为可审查性服务,不是隐藏内容)。
|
|
8
|
+
"""
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import argparse
|
|
12
|
+
import collections
|
|
13
|
+
import json
|
|
14
|
+
import os
|
|
15
|
+
|
|
16
|
+
from .. import conformance as CF
|
|
17
|
+
from ..datapath import mdcg_root
|
|
18
|
+
from . import bundle as BD
|
|
19
|
+
from . import candidates as CD
|
|
20
|
+
from . import ruleset as RS
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
# 生效条件:root 为假值(None/空串)时回落 mdcg_root(),sources 缺省取模块常量 CD.SOURCES 并透传给 CD.generate,返回 dict out,且仅当 full 为真值时额外带上 candidates_full、bundles_full、packages_full 三键。
|
|
24
|
+
def run(root=None, *, sources=CD.SOURCES, cold_limit=500, patrol_window=200,
|
|
25
|
+
max_per_bundle=50, limit=None, patrol_offset=None, rules_dir=None,
|
|
26
|
+
full=False) -> dict:
|
|
27
|
+
root = root or mdcg_root()
|
|
28
|
+
nodes = CF.load_index(root) or {} # load_index 直接返回 nodes 字典
|
|
29
|
+
gen = CD.generate(root, sources=sources, nodes=nodes, limit=limit,
|
|
30
|
+
cold_limit=cold_limit, patrol_window=patrol_window,
|
|
31
|
+
patrol_offset=patrol_offset, with_report=True)
|
|
32
|
+
bd = BD.bundle(gen["candidates"], nodes=nodes, root=root,
|
|
33
|
+
max_per_bundle=max_per_bundle)
|
|
34
|
+
asm = RS.assemble_all(bd, rules_dir=rules_dir)
|
|
35
|
+
by_rule = collections.Counter(i["rule_id"] for p in asm["packages"] for i in p["mechanical"])
|
|
36
|
+
out = {
|
|
37
|
+
"root": root,
|
|
38
|
+
"candidates": {"by_source": gen["by_source"], "total_raw": gen["total_raw"],
|
|
39
|
+
"total": gen["total"], "skipped": gen["skipped"]},
|
|
40
|
+
"report": gen.get("report"),
|
|
41
|
+
"assertions": gen.get("assertions"),
|
|
42
|
+
"bundles": [{"bundle_id": b["bundle_id"], "group_kind": b["group_kind"],
|
|
43
|
+
"group_key": b["group_key"], "size": b["size"], "refs": b["refs"]}
|
|
44
|
+
for b in bd["bundles"]],
|
|
45
|
+
"bundle_stats": bd["stats"],
|
|
46
|
+
"content_missing": bd["content_missing"],
|
|
47
|
+
"assemble_stats": asm["stats"],
|
|
48
|
+
"mechanical_by_rule": dict(by_rule),
|
|
49
|
+
"llm_checks": sorted({(c["rule_id"], c["check"]) for p in asm["packages"]
|
|
50
|
+
for c in p["llm"]}),
|
|
51
|
+
}
|
|
52
|
+
if full:
|
|
53
|
+
out["candidates_full"] = gen["candidates"]
|
|
54
|
+
out["bundles_full"] = bd["bundles"]
|
|
55
|
+
out["packages_full"] = asm["packages"]
|
|
56
|
+
return out
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
# 生效条件:d 必须已含 candidates、bundle_stats、assemble_stats、root、content_missing、llm_checks 这些键(直接下标取值,缺键即 KeyError),且仅当 d.get("assertions") 为真值(非空映射)时才追加断言集一行,其 failed 空或缺失时回落显示「无」。
|
|
60
|
+
def _fmt_summary(d: dict) -> str:
|
|
61
|
+
c, b, a = d["candidates"], d["bundle_stats"], d["assemble_stats"]
|
|
62
|
+
lines = ["", "== M1 机械层(候选 → 捆绑 → 规则装配) ==",
|
|
63
|
+
"真源:%s" % d["root"],
|
|
64
|
+
"候选:原始 %d 条 → 去重后 %d 条(按源:%s)"
|
|
65
|
+
% (c["total_raw"], c["total"],
|
|
66
|
+
", ".join("%s=%d" % kv for kv in sorted(c["by_source"].items()))),
|
|
67
|
+
"跳过(未落盘/无节点):%d 条" % len(c["skipped"]),
|
|
68
|
+
"捆绑:%d 包(最大 %d 条/包;分组类型 %s)"
|
|
69
|
+
% (b["bundles"], b["largest"], b["by_group_kind"]),
|
|
70
|
+
"正文缺失:%d 条(读盘失败计入,不冒充内容)" % d["content_missing"],
|
|
71
|
+
"规则:%d 条(%s)" % (len(a["rules"]), ", ".join(a["rules"])),
|
|
72
|
+
"机械检查:%d 条 issue(按类 %s)" % (a["mechanical_total"], a["mechanical_by_kind"]),
|
|
73
|
+
"待 LLM 检查项:%d 条(%s)"
|
|
74
|
+
% (a["llm_checks"], ", ".join("%s:%s" % t for t in d["llm_checks"]))]
|
|
75
|
+
asr = d.get("assertions") or {}
|
|
76
|
+
if asr:
|
|
77
|
+
lines.append("断言集交叉核对:verdict=%s,未达标项 %s"
|
|
78
|
+
% (asr.get("verdict"), ", ".join(asr.get("failed") or []) or "无"))
|
|
79
|
+
return "\n".join(lines)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
# 生效条件:argv 为 None 时 argparse 改从 sys.argv[1:] 取参,--sources 按逗号切分并丢弃空串后组成元组传入 run,仅当 --out 为非空串时才以 UTF-8 落盘 JSON dump,各分支之后统一 return 0。
|
|
83
|
+
def main(argv=None) -> int:
|
|
84
|
+
ap = argparse.ArgumentParser(prog="python -m md_cg.mreview",
|
|
85
|
+
description="M1 机械层:候选生成→捆绑→规则装配(只读零写入)")
|
|
86
|
+
ap.add_argument("--root", default=None, help="认知图根(缺省 MDCG_ROOT/默认路径)")
|
|
87
|
+
ap.add_argument("--sources", default=",".join(CD.SOURCES))
|
|
88
|
+
ap.add_argument("--limit", type=int, default=None)
|
|
89
|
+
ap.add_argument("--cold-limit", type=int, default=500)
|
|
90
|
+
ap.add_argument("--patrol-window", type=int, default=200)
|
|
91
|
+
ap.add_argument("--patrol-offset", type=int, default=None)
|
|
92
|
+
ap.add_argument("--max-per-bundle", type=int, default=50)
|
|
93
|
+
ap.add_argument("--rules-dir", default=None)
|
|
94
|
+
ap.add_argument("--out", default=None, help="落盘 dump(JSON)")
|
|
95
|
+
ap.add_argument("--full", action="store_true", help="含条目明细与逐条 issue")
|
|
96
|
+
a = ap.parse_args(argv)
|
|
97
|
+
d = run(a.root, sources=tuple(s for s in a.sources.split(",") if s),
|
|
98
|
+
cold_limit=a.cold_limit, patrol_window=a.patrol_window, limit=a.limit,
|
|
99
|
+
patrol_offset=a.patrol_offset, max_per_bundle=a.max_per_bundle,
|
|
100
|
+
rules_dir=a.rules_dir, full=a.full)
|
|
101
|
+
print(_fmt_summary(d))
|
|
102
|
+
if a.out:
|
|
103
|
+
with open(os.path.abspath(a.out), "w", encoding="utf-8") as f:
|
|
104
|
+
json.dump(d, f, ensure_ascii=False, indent=1)
|
|
105
|
+
print("已落盘:%s" % os.path.abspath(a.out))
|
|
106
|
+
return 0
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
if __name__ == "__main__":
|
|
110
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""记忆评审流水线 · 级 2:捆绑(确定性,零写入)。
|
|
3
|
+
|
|
4
|
+
真源:docs/记忆评审系统_立项设计与施工交接_20260915.md §3 第 2 级。
|
|
5
|
+
|
|
6
|
+
**同模板组一包一评**:批次报告类节点(「批次N收官记忆」)正文各不相同但同模板,
|
|
7
|
+
逐条评审=重复劳动且立场漂移;成组评审=一次裁决整组。分组键优先级:
|
|
8
|
+
|
|
9
|
+
1. ``template`` 模板签名——复用 :func:`writelimit.template_signature`(强信号 ``t:``);
|
|
10
|
+
长文(≥ MAX_CONTENT)无标题签名时回退**首行骨架**(弱信号 ``h:``)——真源知识节点
|
|
11
|
+
正文多为长文四要素,标题/首行才是同模板流水的载体(该回退只在评审分组内生效,
|
|
12
|
+
不改写侧聚合口径)。
|
|
13
|
+
2. ``lineage`` 血缘——branch_id / branched_from / derived_from[0](同源迁移一起看)。
|
|
14
|
+
3. ``topic`` 标签前缀集合(无标签回退层)。
|
|
15
|
+
4. ``batch`` 无任何分组特征的孤立候选,按确定性顺序成批(不误聚、不丢弃)。
|
|
16
|
+
|
|
17
|
+
组内条数 < ``min_group`` 的组降级并入 batch;组内超 ``max_per_bundle`` 切分多包。
|
|
18
|
+
包号确定性:``b1_<kind>_<sha1(key)[:8]>_c<idx>``;同一输入两次运行逐字节一致。
|
|
19
|
+
"""
|
|
20
|
+
from __future__ import annotations
|
|
21
|
+
|
|
22
|
+
import collections
|
|
23
|
+
import hashlib
|
|
24
|
+
import os
|
|
25
|
+
|
|
26
|
+
from .. import conformance as CF
|
|
27
|
+
from .. import writelimit as WL
|
|
28
|
+
|
|
29
|
+
GROUP_TEMPLATE = "template"
|
|
30
|
+
GROUP_LINEAGE = "lineage"
|
|
31
|
+
GROUP_TOPIC = "topic"
|
|
32
|
+
GROUP_BATCH = "batch"
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
# 生效条件:content 为假值(如空串)时返回 (None, None);content 为真且 WL.template_signature(content) 返回真值时返回 (s, 'strong');否则遍历 content.splitlines() 中 strip 后非空的行,遇首个非空行时其 WL._skeleton 长度 ≥ WL.MIN_SKELETON 则返回 ('h:' + sk, 'head'),否则立即返回 (None, None)(不再看后续行),无任何非空行时也返回 (None, None);
|
|
36
|
+
def _sig_of(content: str):
|
|
37
|
+
"""模板签名(强/弱)。返回 (sig, kind) 或 (None, None)。"""
|
|
38
|
+
if not content:
|
|
39
|
+
return None, None
|
|
40
|
+
s = WL.template_signature(content)
|
|
41
|
+
if s:
|
|
42
|
+
return s, "strong"
|
|
43
|
+
for line in content.splitlines():
|
|
44
|
+
t = line.strip()
|
|
45
|
+
if not t:
|
|
46
|
+
continue
|
|
47
|
+
sk = WL._skeleton(t)
|
|
48
|
+
if len(sk) >= WL.MIN_SKELETON:
|
|
49
|
+
return "h:" + sk, "head"
|
|
50
|
+
return None, None
|
|
51
|
+
return None, None
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
# 生效条件:meta(dict)中按 ("branched_from", "branch_id") 顺序取到的首个 v 为真且 str(v) != 'None' 时返回 str(v);否则 meta.get('derived_from') 为 list/tuple 且非空时返回 str(d[0]);否则其为非空 str 且不等于 '[]' 与 'None' 时返回该 str 本身;以上皆不成立时返回 None;
|
|
55
|
+
def _lineage_key(meta: dict):
|
|
56
|
+
for f in ("branched_from", "branch_id"):
|
|
57
|
+
v = meta.get(f)
|
|
58
|
+
if v and str(v) != "None":
|
|
59
|
+
return str(v)
|
|
60
|
+
d = meta.get("derived_from")
|
|
61
|
+
if isinstance(d, (list, tuple)) and d:
|
|
62
|
+
return str(d[0])
|
|
63
|
+
if isinstance(d, str) and d and d not in ("[]", "None"):
|
|
64
|
+
return d
|
|
65
|
+
return None
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
# 生效条件:meta 为真时先取 sorted(CF._tag_prefixes(meta)) 作为 pref,pref 为空且 tags 有真值元素时改用各 tag 的 str(t).split(':', 1)[0](跳过假值 tag)集合排序;pref 非空返回 '+'.join(pref);pref 为空时取 str((meta or {}).get('layer') or ''),该层名非空返回 'layer:' + layer,层名为假值(None/空串/0)则返回 None;
|
|
69
|
+
def _topic_key(meta: dict, tags: list):
|
|
70
|
+
pref = sorted(CF._tag_prefixes(meta)) if meta else []
|
|
71
|
+
if not pref and tags:
|
|
72
|
+
pref = sorted({str(t).split(":", 1)[0] for t in tags if t})
|
|
73
|
+
if pref:
|
|
74
|
+
return "+".join(pref)
|
|
75
|
+
layer = str((meta or {}).get("layer") or "")
|
|
76
|
+
return ("layer:" + layer) if layer else None
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
# 生效条件:content 经 _sig_of 得到的 sig 为真时返回 (GROUP_TEMPLATE, sig, kind);否则 meta 经 _lineage_key 得到的值非空时返回 (GROUP_LINEAGE, lin, None);否则 (meta, tags) 经 _topic_key 得到的值非空时返回 (GROUP_TOPIC, top, None);三者皆不成立时返回 (GROUP_BATCH, 'misc', None);
|
|
80
|
+
def _group_of(meta: dict, tags: list, content: str):
|
|
81
|
+
sig, kind = _sig_of(content)
|
|
82
|
+
if sig:
|
|
83
|
+
return GROUP_TEMPLATE, sig, kind
|
|
84
|
+
lin = _lineage_key(meta)
|
|
85
|
+
if lin:
|
|
86
|
+
return GROUP_LINEAGE, lin, None
|
|
87
|
+
top = _topic_key(meta, tags)
|
|
88
|
+
if top:
|
|
89
|
+
return GROUP_TOPIC, top, None
|
|
90
|
+
return GROUP_BATCH, "misc", None
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
# 生效条件:root 与 meta 均为真、且 meta.get('path') 经 or '' 再 str 后为非空路径时,把 rel 中的 '\\' 与 '/' 替换为 os.sep 并与 root 拼接,以 encoding='utf-8'、errors='replace' 打开并返回 f.read(int(limit))(limit 为 0 时读 0 字节);root 或 meta 为假值、rel 为空、或该打开/读取抛 OSError 时返回 None;
|
|
94
|
+
def _load_content(root: str, meta: dict, limit: int):
|
|
95
|
+
if not root or not meta:
|
|
96
|
+
return None
|
|
97
|
+
rel = str(meta.get("path") or "")
|
|
98
|
+
if not rel:
|
|
99
|
+
return None
|
|
100
|
+
p = os.path.join(root, rel.replace("\\", os.sep).replace("/", os.sep))
|
|
101
|
+
try:
|
|
102
|
+
with open(p, encoding="utf-8", errors="replace") as f:
|
|
103
|
+
return f.read(int(limit))
|
|
104
|
+
except OSError:
|
|
105
|
+
return None
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
# 生效条件:恒返回构造 dict——ref/node_id/proposal_id/origin/layer/role/importance/evidence_count/verification_basis/lifecycle_state 均以 .get 取值(缺键得 None),content_hash 先取 cand.get('content_hash') 为空再取 (meta or {}).get('content_hash'),tags/issue_kinds/evidence 经 or [] 后 list 化,excerpt 取 (content or '')[:int(limit)] 为空时(含 content 为空串或 limit 为 0)为 None;
|
|
109
|
+
def _entry(cand: dict, meta: dict, content: str, limit: int) -> dict:
|
|
110
|
+
return {"ref": cand.get("key"), "node_id": cand.get("node_id"),
|
|
111
|
+
"proposal_id": cand.get("proposal_id"), "origin": cand.get("origin"),
|
|
112
|
+
"layer": cand.get("layer"), "tags": list(cand.get("tags") or []),
|
|
113
|
+
"role": (meta or {}).get("role"),
|
|
114
|
+
"importance": (meta or {}).get("importance"),
|
|
115
|
+
"evidence_count": (meta or {}).get("evidence_count"),
|
|
116
|
+
"verification_basis": (meta or {}).get("verification_basis"),
|
|
117
|
+
"lifecycle_state": (meta or {}).get("lifecycle_state"),
|
|
118
|
+
"content_hash": cand.get("content_hash") or (meta or {}).get("content_hash"),
|
|
119
|
+
"issue_kinds": list(cand.get("issue_kinds") or []),
|
|
120
|
+
"evidence": list(cand.get("evidence") or []),
|
|
121
|
+
"excerpt": (content or "")[:int(limit)] or None}
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
# 生效条件:ids 与 size 为必需形参,size 先经 max(1, int(size))(0、负值或 1 均按 1)作为步长,返回 [ids[i:i + size] for i in range(0, len(ids), size)];ids 为空(如 [] 或空串)时返回 [];
|
|
125
|
+
def _chunks(ids: list, size: int) -> list:
|
|
126
|
+
size = max(1, int(size))
|
|
127
|
+
return [ids[i:i + size] for i in range(0, len(ids), size)]
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
# 生效条件:candidates(list)为必需输入,nodes 为假值时按 {} 取节点表;逐候选以 c.get('node_id') 查 meta(无 node_id 则 meta 为 {})、以 c.get('content') or '' 取内容,内容为空且 read_content 为真且 nid 为真时改用 _load_content(root, meta, content_limit) 的结果(再 or ''),并按 int(content_limit) 截断;随后按 _group_of(meta, c.get('tags') or [], content) 分组,非 GROUP_BATCH 且组内 id 数 < int(min_group)(min_group 为 0 时该比较恒不成立,即无降级)的组与既有 GROUP_BATCH 组一并降级,降级集合非空时并入 (GROUP_BATCH, 'misc')、为空则不建该组;再对每组按 int(max_per_bundle) 经 _chunks 切块生成带 entries 的 bundles 与 stats,返回 content_missing 为 contents 中该候选键对应值为假值的候选数;
|
|
131
|
+
def bundle(candidates: list, nodes=None, root=None, *, max_per_bundle=50,
|
|
132
|
+
min_group=2, read_content=True, content_limit=3000) -> dict:
|
|
133
|
+
"""候选清单 → 待评包(含条目上下文与机械字段,供级 3 装配与级 4 spec)。"""
|
|
134
|
+
nodes = nodes or {}
|
|
135
|
+
bykey = {c.get("key"): c for c in candidates}
|
|
136
|
+
groups, contents = {}, {}
|
|
137
|
+
for c in candidates:
|
|
138
|
+
nid = c.get("node_id")
|
|
139
|
+
meta = (nodes.get(nid) or {}) if nid else {}
|
|
140
|
+
content = c.get("content") or ""
|
|
141
|
+
if not content and read_content and nid:
|
|
142
|
+
content = _load_content(root, meta, content_limit) or ""
|
|
143
|
+
content = (content or "")[:int(content_limit)]
|
|
144
|
+
kind, key, sig_kind = _group_of(meta, c.get("tags") or [], content)
|
|
145
|
+
g = groups.get((kind, key))
|
|
146
|
+
if g is None:
|
|
147
|
+
g = groups[(kind, key)] = {"ids": [], "sig_kind": sig_kind}
|
|
148
|
+
g["ids"].append(c.get("key"))
|
|
149
|
+
contents[c.get("key")] = content
|
|
150
|
+
|
|
151
|
+
# 小组降级并入 batch(不误聚、不丢弃)
|
|
152
|
+
demoted = []
|
|
153
|
+
for k in [k for k, g in groups.items()
|
|
154
|
+
if k[0] != GROUP_BATCH and len(g["ids"]) < int(min_group)]:
|
|
155
|
+
demoted += groups.pop(k)["ids"]
|
|
156
|
+
for k in [k for k in groups if k[0] == GROUP_BATCH]:
|
|
157
|
+
demoted += groups.pop(k)["ids"]
|
|
158
|
+
demoted = sorted(demoted)
|
|
159
|
+
if demoted:
|
|
160
|
+
groups[(GROUP_BATCH, "misc")] = {"ids": demoted, "sig_kind": None}
|
|
161
|
+
|
|
162
|
+
bundles = []
|
|
163
|
+
for (kind, key) in sorted(groups):
|
|
164
|
+
g = groups[(kind, key)]
|
|
165
|
+
for i, chunk in enumerate(_chunks(sorted(g["ids"]), int(max_per_bundle))):
|
|
166
|
+
bid = "b1_%s_%s_c%d" % (kind, hashlib.sha1(str(key).encode("utf-8")).hexdigest()[:8], i)
|
|
167
|
+
bundles.append({
|
|
168
|
+
"bundle_id": bid, "group_kind": kind, "group_key": str(key),
|
|
169
|
+
"group_sig_kind": g["sig_kind"], "size": len(chunk), "refs": chunk,
|
|
170
|
+
"entries": [_entry(bykey[r], nodes.get(bykey[r].get("node_id") or "") or {},
|
|
171
|
+
contents.get(r) or "", content_limit)
|
|
172
|
+
for r in chunk]})
|
|
173
|
+
stats = {"candidates": len(candidates), "bundles": len(bundles),
|
|
174
|
+
"largest": max([b["size"] for b in bundles] or [0]),
|
|
175
|
+
"by_group_kind": dict(collections.Counter(b["group_kind"] for b in bundles))}
|
|
176
|
+
return {"bundles": bundles, "stats": stats,
|
|
177
|
+
"content_missing": sum(1 for c in candidates
|
|
178
|
+
if not contents.get(c.get("key")))}
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""记忆评审流水线 · 级 1:候选生成(确定性,零写入)。
|
|
3
|
+
|
|
4
|
+
真源:docs/记忆评审系统_立项设计与施工交接_20260915.md §3 第 1 级。
|
|
5
|
+
|
|
6
|
+
四源(可单独关闭):
|
|
7
|
+
|
|
8
|
+
* ``assertion`` 断言集超限项的**条目级展开**——复用 conformance 的**同一判据函数**
|
|
9
|
+
(`_coverage_metrics` / `unanalyzed` / `_dup_metrics` / `_edge_metrics`),
|
|
10
|
+
保证「报告聚合数」与「条目清单」两个入口同口径(见 test_mr_m1 T4)。
|
|
11
|
+
* ``defer_queue`` 闸门待裁决提案(inbox 未被 decisions 覆盖)+遗忘日志 DEFER 留痕;
|
|
12
|
+
未落盘的 node_id 计入 ``skipped``,**如实上报不冒充候选**。
|
|
13
|
+
* ``cold`` access log 触达集合之外、且重要度不低的节点(久未触达者更值得评审)。
|
|
14
|
+
* ``patrol`` sha1(node_id) 排序切片轮巡(offset 决定窗口位置),多轮不重不漏。
|
|
15
|
+
|
|
16
|
+
纪律:零写入;输出顺序由 (优先级, node_id) 决定;每条候选必带证据。
|
|
17
|
+
"""
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
|
|
20
|
+
import collections
|
|
21
|
+
import hashlib
|
|
22
|
+
import json
|
|
23
|
+
import os
|
|
24
|
+
import time
|
|
25
|
+
|
|
26
|
+
from .. import conformance as CF
|
|
27
|
+
from ..datapath import mdcg_root
|
|
28
|
+
|
|
29
|
+
SOURCES = ("assertion", "defer_queue", "cold", "patrol")
|
|
30
|
+
_PRIORITY = {"assertion": 0, "defer_queue": 1, "cold": 2, "patrol": 3}
|
|
31
|
+
FORGET_LOG = "_forgetting.jsonl"
|
|
32
|
+
PATROL_EPOCH = 3600.0 # 轮巡窗口推进周期(秒):默认每窗口周期轮转一格
|
|
33
|
+
COLD_IMPORTANCE_MIN = 0.6 # 冷节点抽样门槛(importance 解析失败按 0 计)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
# 生效条件:path 不满足 os.path.exists(path) 时返回 [];否则逐行 strip 后跳过空行与 json.JSONDecodeError 行,返回成功 json.loads 的每行结果列表。
|
|
37
|
+
def _read_jsonl(path: str) -> list:
|
|
38
|
+
if not os.path.exists(path):
|
|
39
|
+
return []
|
|
40
|
+
out = []
|
|
41
|
+
with open(path, encoding="utf-8", errors="replace") as f:
|
|
42
|
+
for line in f:
|
|
43
|
+
line = line.strip()
|
|
44
|
+
if not line:
|
|
45
|
+
continue
|
|
46
|
+
try:
|
|
47
|
+
out.append(json.loads(line))
|
|
48
|
+
except json.JSONDecodeError:
|
|
49
|
+
continue
|
|
50
|
+
return out
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
# 生效条件:os.path.join(root, CF.ACCESS_LOG) 不存在时返回空集;否则逐行解析,rec.get("ids") 为 list/tuple 时仅加入其中真值元素的 str 形式,nid 取 rec.get("id") or rec.get("node_id") or rec.get("nid") 的真值并加入 str(nid),最终返回 seen & set(nodes)(仅保留 nodes 键内的 id)。
|
|
54
|
+
def _touched(root: str, nodes: dict) -> set:
|
|
55
|
+
"""access log 触达集(与 conformance._reach_metrics 同一解析口径:ids / id / node_id / nid)。"""
|
|
56
|
+
p = os.path.join(root, CF.ACCESS_LOG)
|
|
57
|
+
seen = set()
|
|
58
|
+
if not os.path.exists(p):
|
|
59
|
+
return seen
|
|
60
|
+
with open(p, encoding="utf-8", errors="replace") as f:
|
|
61
|
+
for line in f:
|
|
62
|
+
line = line.strip()
|
|
63
|
+
if not line:
|
|
64
|
+
continue
|
|
65
|
+
try:
|
|
66
|
+
rec = json.loads(line)
|
|
67
|
+
except json.JSONDecodeError:
|
|
68
|
+
continue
|
|
69
|
+
got = rec.get("ids")
|
|
70
|
+
if isinstance(got, (list, tuple)):
|
|
71
|
+
seen.update(str(x) for x in got if x)
|
|
72
|
+
nid = rec.get("id") or rec.get("node_id") or rec.get("nid")
|
|
73
|
+
if nid:
|
|
74
|
+
seen.add(str(nid))
|
|
75
|
+
return seen & set(nodes)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
# 生效条件:str(layer)=="knowledge" 的节点在 role 为假值、CF._as_int(evidence_count)==0、或 {"doc","code"} 命中 CF._tag_prefixes(r) 时分别产出 missing_field/mixed_layer;任意节点在 CF._as_int(evidence_count)==0 且无 CF._basis_of(r) 且 lifecycle_state 为假值时产出 unanalyzed;str(content_hash) 既非 "None" 也非 "" 且同指纹计数 >1 时该组内每个节点产出 dup_content;edges 为 list/tuple 时逐条处理其中的 dict 边,当该边所有真值键(CF.CANONICAL_EDGE_KEYS 与 "type")中不含任一规范键时产出 edge_non_canonical 并停止该节点后续边。
|
|
79
|
+
def _assertion_items(nodes: dict) -> list:
|
|
80
|
+
"""断言集超限项 → 条目级(判据函数与 conformance 同源,不另立口径)。"""
|
|
81
|
+
out = []
|
|
82
|
+
kn = {nid: r for nid, r in nodes.items() if str(r.get("layer")) == "knowledge"}
|
|
83
|
+
for nid, r in kn.items():
|
|
84
|
+
if not r.get("role"):
|
|
85
|
+
out.append({"node_id": nid, "issue_kind": "missing_field",
|
|
86
|
+
"evidence": "role 为空(口径=conformance.stratum.role_coverage,knowledge 层分母)"})
|
|
87
|
+
if CF._as_int(r.get("evidence_count")) == 0:
|
|
88
|
+
out.append({"node_id": nid, "issue_kind": "missing_field",
|
|
89
|
+
"evidence": "evidence_count=0(口径=conformance.stratum.evidence_coverage)"})
|
|
90
|
+
if {"doc", "code"} & CF._tag_prefixes(r):
|
|
91
|
+
out.append({"node_id": nid, "issue_kind": "mixed_layer",
|
|
92
|
+
"evidence": "knowledge 层带 doc/code 标签(口径=conformance.stratum.mixed_ratio)"})
|
|
93
|
+
for nid, r in nodes.items():
|
|
94
|
+
if (CF._as_int(r.get("evidence_count")) == 0 and not CF._basis_of(r)
|
|
95
|
+
and not r.get("lifecycle_state")):
|
|
96
|
+
out.append({"node_id": nid, "issue_kind": "unanalyzed",
|
|
97
|
+
"evidence": "无一维分析痕迹(口径=conformance.unanalyzed 派生判据)"})
|
|
98
|
+
ch = collections.Counter(str(r.get("content_hash")) for r in nodes.values())
|
|
99
|
+
dup = {h for h, c in ch.items() if c > 1 and h not in ("None", "")}
|
|
100
|
+
if dup:
|
|
101
|
+
for nid, r in nodes.items():
|
|
102
|
+
h = str(r.get("content_hash"))
|
|
103
|
+
if h in dup:
|
|
104
|
+
out.append({"node_id": nid, "issue_kind": "dup_content",
|
|
105
|
+
"evidence": "content_hash 重复组 %s(%d 条同指纹;口径=conformance.dup.content)" % (h, ch[h])})
|
|
106
|
+
for nid, r in nodes.items():
|
|
107
|
+
es = r.get("edges") or []
|
|
108
|
+
if not isinstance(es, (list, tuple)):
|
|
109
|
+
continue
|
|
110
|
+
for e in es:
|
|
111
|
+
if not isinstance(e, dict):
|
|
112
|
+
continue
|
|
113
|
+
keys = [k for k in CF.CANONICAL_EDGE_KEYS + ("type",) if e.get(k)]
|
|
114
|
+
if not any(k in CF.CANONICAL_EDGE_KEYS for k in keys):
|
|
115
|
+
out.append({"node_id": nid, "issue_kind": "edge_non_canonical",
|
|
116
|
+
"evidence": "边键非规范:%s(口径=conformance.edge.key.canonical)"
|
|
117
|
+
% ("+".join(keys) or "<无关系键>")})
|
|
118
|
+
break
|
|
119
|
+
return out
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
# 生效条件:当 root、nodes 给定,对 _read_jsonl(root/CF.INBOX_LOG) 解析出的 inbox 记录中 pid(str(r.get("pid") or ""))非空且不在 decisions 中 x.get("pid") 真值 str 集合的记录,生成 origin=proposal、issue_kind=gate_defer 条目,其中 layer 直接取 r.get("layer") 不回落、content 取 r.get("content") or ""、tags 取 r.get("tags") or []、content_hash 取 r.get("payload_hash") or ""、node_id 取 str(r.get("id") or "") or None;再对 _read_jsonl(root/FORGET_LOG) 中 str(r.get("verdict"))=="DEFER" 的记录,若 str(r.get("node_id") or "") 非空且该 nid 是 nodes 的键则加入 out,否则加入 skipped(source=defer_queue、reason=node_not_landed、detail 为 nid 或 str(r.get("reason")));返回 (out, skipped);
|
|
123
|
+
def _defer_items(root: str, nodes: dict):
|
|
124
|
+
"""闸门待裁决提案 + 遗忘日志 DEFER 留痕。返回 (items, skipped)。"""
|
|
125
|
+
ib = _read_jsonl(os.path.join(root, CF.INBOX_LOG))
|
|
126
|
+
dc = _read_jsonl(os.path.join(root, CF.DECISION_LOG))
|
|
127
|
+
done = {str(x.get("pid")) for x in dc if x.get("pid")}
|
|
128
|
+
out, skipped = [], []
|
|
129
|
+
for r in ib:
|
|
130
|
+
pid = str(r.get("pid") or "")
|
|
131
|
+
if not pid or pid in done:
|
|
132
|
+
continue
|
|
133
|
+
out.append({"node_id": str(r.get("id") or "") or None, "origin": "proposal",
|
|
134
|
+
"proposal_id": pid, "issue_kind": "gate_defer",
|
|
135
|
+
"layer": r.get("layer"), "content": r.get("content") or "",
|
|
136
|
+
"tags": r.get("tags") or [], "content_hash": r.get("payload_hash") or "",
|
|
137
|
+
"evidence": "闸门待裁决提案(inbox 中 %s 未被 decisions 覆盖)" % pid})
|
|
138
|
+
for r in _read_jsonl(os.path.join(root, FORGET_LOG)):
|
|
139
|
+
if str(r.get("verdict")) != "DEFER":
|
|
140
|
+
continue
|
|
141
|
+
nid = str(r.get("node_id") or "")
|
|
142
|
+
if nid and nid in nodes:
|
|
143
|
+
out.append({"node_id": nid, "issue_kind": "gate_defer",
|
|
144
|
+
"evidence": "遗忘闸门 DEFER 留痕:%s" % str(r.get("reason"))})
|
|
145
|
+
else:
|
|
146
|
+
skipped.append({"source": "defer_queue", "reason": "node_not_landed",
|
|
147
|
+
"detail": nid or str(r.get("reason"))})
|
|
148
|
+
return out, skipped
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
# 生效条件:取 nodes 中不在 _touched(root, nodes) 触达集、且 importance 经 _imp 归一后 >= COLD_IMPORTANCE_MIN 的节点,按归一 importance 降序(同值按 node_id 升序)后取 [:int(limit)];limit=0 得空列表,limit 为负则排除末尾 |limit| 条而非空返回(源码无 int(limit)<=0 早退)。
|
|
152
|
+
def _cold_items(root: str, nodes: dict, limit: int) -> list:
|
|
153
|
+
seen = _touched(root, nodes)
|
|
154
|
+
|
|
155
|
+
# 生效条件:r 的 importance 为真值且 float() 可转换时返回其 float 值;importance 为假值(None/0/"")时回落 0.0;转换抛 TypeError 或 ValueError 时返回 0.0。
|
|
156
|
+
def _imp(r):
|
|
157
|
+
try:
|
|
158
|
+
return float(r.get("importance") or 0.0)
|
|
159
|
+
except (TypeError, ValueError):
|
|
160
|
+
return 0.0
|
|
161
|
+
|
|
162
|
+
rows = [(nid, r) for nid, r in nodes.items() if nid not in seen]
|
|
163
|
+
rows.sort(key=lambda kv: (-_imp(kv[1]), kv[0]))
|
|
164
|
+
picked = [(nid, r) for nid, r in rows if _imp(r) >= COLD_IMPORTANCE_MIN][:int(limit)]
|
|
165
|
+
return [{"node_id": nid, "issue_kind": "cold",
|
|
166
|
+
"evidence": "access log 未触达(importance=%.2f,排名第 %d)" % (_imp(r), i + 1)}
|
|
167
|
+
for i, (nid, r) in enumerate(picked)]
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
# 生效条件:int(window) <= 0 或 nodes 为假值时返回 [];否则按 sha1(nid.encode("utf-8")).hexdigest() 排序,从 (int(offset)*int(window)) % len(nodes) 起取 int(window) 条,不足时用头部元素环绕补足。
|
|
171
|
+
def _patrol_items(nodes: dict, window: int, offset: int) -> list:
|
|
172
|
+
if int(window) <= 0 or not nodes:
|
|
173
|
+
return []
|
|
174
|
+
ordered = sorted(nodes, key=lambda nid: hashlib.sha1(nid.encode("utf-8")).hexdigest())
|
|
175
|
+
n = len(ordered)
|
|
176
|
+
start = (int(offset) * int(window)) % n
|
|
177
|
+
picked = ordered[start:start + int(window)]
|
|
178
|
+
if len(picked) < int(window): # 环绕补足:多轮不重不漏
|
|
179
|
+
picked = picked + ordered[:int(window) - len(picked)]
|
|
180
|
+
return [{"node_id": nid, "issue_kind": "patrol",
|
|
181
|
+
"evidence": "轮巡窗口 #%d(窗口 %d 条,按 sha1(node_id) 排序切片)" % (int(offset), int(window))}
|
|
182
|
+
for nid in picked]
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
# 生效条件:items 逐条以 node_id 为真时 key=str(node_id)、否则 key="pid:"+str(proposal_id) 聚合;首次建房时 node_id 为真则取 nodes.get(node_id) or {} 作 meta,tags 取 it.get("tags") or meta.get("tags") or [],evidence 每聚合记录最多 8 条;结果 sources 去重后为空则置 ["-"],priority 取 _PRIORITY.get(s, 9) 的最小值,按 (priority, str(key)) 排序返回。
|
|
186
|
+
def _merge(items: list, nodes: dict) -> list:
|
|
187
|
+
"""按条目(节点/提案)聚合:一条候选 = 一个待评条目,多个问题挂在 issue_kinds 上。"""
|
|
188
|
+
acc = {}
|
|
189
|
+
for it in items:
|
|
190
|
+
nid = it.get("node_id")
|
|
191
|
+
key = str(nid) if nid else "pid:" + str(it.get("proposal_id"))
|
|
192
|
+
rec = acc.get(key)
|
|
193
|
+
if rec is None:
|
|
194
|
+
meta = nodes.get(nid) or {} if nid else {}
|
|
195
|
+
rec = acc[key] = {
|
|
196
|
+
"key": key, "node_id": nid or None, "proposal_id": it.get("proposal_id") or None,
|
|
197
|
+
"origin": it.get("origin") or "node",
|
|
198
|
+
"layer": it.get("layer") or meta.get("layer"),
|
|
199
|
+
"sources": [], "issue_kinds": [], "evidence": [],
|
|
200
|
+
"content": it.get("content") or "",
|
|
201
|
+
"tags": list(it.get("tags") or meta.get("tags") or []),
|
|
202
|
+
"content_hash": it.get("content_hash") or str(meta.get("content_hash") or ""),
|
|
203
|
+
}
|
|
204
|
+
src = it.get("source")
|
|
205
|
+
if src and src not in rec["sources"]:
|
|
206
|
+
rec["sources"].append(src)
|
|
207
|
+
ik = it.get("issue_kind")
|
|
208
|
+
if ik and ik not in rec["issue_kinds"]:
|
|
209
|
+
rec["issue_kinds"].append(ik)
|
|
210
|
+
ev = it.get("evidence")
|
|
211
|
+
if ev and len(rec["evidence"]) < 8:
|
|
212
|
+
rec["evidence"].append(ev)
|
|
213
|
+
out = list(acc.values())
|
|
214
|
+
for r in out:
|
|
215
|
+
r["sources"] = sorted(set(r["sources"])) or ["-"]
|
|
216
|
+
r["priority"] = min(_PRIORITY.get(s, 9) for s in r["sources"])
|
|
217
|
+
r["issue_kinds"] = sorted(r["issue_kinds"])
|
|
218
|
+
out.sort(key=lambda r: (r["priority"], str(r["key"])))
|
|
219
|
+
return out
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
# 生效条件:root 为假值(None/"")时回落 mdcg_root();nodes 为 None 时用 CF.load_index(root) or {};wanted 为 (sources or SOURCES) 与 SOURCES 的交集,按其中是否含 assertion/defer_queue/cold/patrol 分派取候选(cold 传 cold_limit,patrol 传 patrol_window 与偏移,patrol_offset 为 None 时偏移取 int((now if now is not None else time.time())//PATROL_EPOCH));limit 为真值时才把 merged 截到 int(limit);with_report 为真值时补 CF.check(root, check_paths=False) 的指标与失败断言 id。
|
|
223
|
+
def generate(root=None, *, sources=SOURCES, nodes=None, limit=None, cold_limit=500,
|
|
224
|
+
patrol_window=200, patrol_offset=None, with_report=False, now=None) -> dict:
|
|
225
|
+
"""四源候选生成。nodes 可注入(测试用合成库,免读真源)。"""
|
|
226
|
+
root = root or mdcg_root()
|
|
227
|
+
if nodes is None:
|
|
228
|
+
nodes = CF.load_index(root) or {} # load_index 直接返回 nodes 字典
|
|
229
|
+
wanted = [s for s in SOURCES if s in (sources or SOURCES)]
|
|
230
|
+
items, skipped, by_source = [], [], {}
|
|
231
|
+
if "assertion" in wanted:
|
|
232
|
+
got = _assertion_items(nodes)
|
|
233
|
+
by_source["assertion"] = len(got)
|
|
234
|
+
items += [dict(x, source="assertion") for x in got]
|
|
235
|
+
if "defer_queue" in wanted:
|
|
236
|
+
got, sk = _defer_items(root, nodes)
|
|
237
|
+
by_source["defer_queue"] = len(got)
|
|
238
|
+
items += [dict(x, source="defer_queue") for x in got]
|
|
239
|
+
skipped += sk
|
|
240
|
+
if "cold" in wanted:
|
|
241
|
+
got = _cold_items(root, nodes, cold_limit)
|
|
242
|
+
by_source["cold"] = len(got)
|
|
243
|
+
items += [dict(x, source="cold") for x in got]
|
|
244
|
+
if "patrol" in wanted:
|
|
245
|
+
off = int(patrol_offset) if patrol_offset is not None \
|
|
246
|
+
else int((now if now is not None else time.time()) // PATROL_EPOCH)
|
|
247
|
+
got = _patrol_items(nodes, patrol_window, off)
|
|
248
|
+
by_source["patrol"] = len(got)
|
|
249
|
+
items += [dict(x, source="patrol") for x in got]
|
|
250
|
+
merged = _merge(items, nodes)
|
|
251
|
+
if limit:
|
|
252
|
+
merged = merged[:int(limit)]
|
|
253
|
+
rep = {"root": root, "generated_at": float(now if now is not None else time.time()),
|
|
254
|
+
"sources": wanted, "by_source": by_source, "total_raw": len(items),
|
|
255
|
+
"total": len(merged), "candidates": merged, "skipped": skipped}
|
|
256
|
+
if with_report:
|
|
257
|
+
rep0 = CF.check(root, check_paths=False)
|
|
258
|
+
rep["report"] = CF.metrics_of(rep0)
|
|
259
|
+
rep["assertions"] = {"verdict": rep0.get("verdict"), "counts": rep0.get("counts"),
|
|
260
|
+
"failed": [c.get("id") for c in rep0.get("checks") or []
|
|
261
|
+
if not c.get("ok")]}
|
|
262
|
+
return rep
|