@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,280 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""查询侧时效过滤(validity)验收 · GBrain 可借鉴点交接 §3②
|
|
3
|
+
|
|
4
|
+
【裁决】使用者三处拍板,是本文件全部断言的判据来源:
|
|
5
|
+
① 候选面**只排已过期**(valid_until 已过);**未生效(valid_from 未到)保留**。
|
|
6
|
+
依据仓内既有纪律:`scrub` 模块「valid_from 绝不并入 _EXPIRY_KEYS」——
|
|
7
|
+
`not_yet`(尚未开始)与 `expired`(已经失效)语义相反;预约/计划类记忆
|
|
8
|
+
在生效前仍应可召回,故不得用「时间轴上不活跃」一刀切。
|
|
9
|
+
② 启用默认值 = **opt-in 默认关**(缺省不过滤):零 rust 镜像义务(保 py↔rust
|
|
10
|
+
零漂移硬承诺)、零 bench 漂移、完全可逆。
|
|
11
|
+
③ 过滤落在**候选层**(一处过滤全链路生效:search / search_rrf / recall),
|
|
12
|
+
与 session / branch 过滤同构;MCP 面三入口透传。
|
|
13
|
+
|
|
14
|
+
【覆盖】
|
|
15
|
+
① `trust.is_expired` 布尔语义(四态→布尔;端点不可解析不猜测)+ scrub 纪律交叉守卫
|
|
16
|
+
② 候选面:MdCG.search(裸核心)/ MdCGSecure.search(角色面)
|
|
17
|
+
③ 默认关:缺省口径与启用口径逐条对照(opt-in 可逆性)
|
|
18
|
+
④ search_rrf:融合面 + query 热缓存分键(validity 不入键会串口径)
|
|
19
|
+
⑤ recall:RRF 分支 + 非 RRF 分支
|
|
20
|
+
⑥ 图扩展:零词面交集节点经 edges 扩散可达时同样受 validity 约束
|
|
21
|
+
⑦ MCP 面三入口透传(cg op=read / mdcg_search / mdcg_recall)
|
|
22
|
+
|
|
23
|
+
【不适用】写入侧(add 落 valid_from/valid_until 是本项前提,非本项范围);
|
|
24
|
+
库内真实数据面(当前 13514 节点零有效期,故本项为 opt-in 零漂移)。
|
|
25
|
+
"""
|
|
26
|
+
import os
|
|
27
|
+
import shutil
|
|
28
|
+
import sys
|
|
29
|
+
import tempfile
|
|
30
|
+
import traceback
|
|
31
|
+
|
|
32
|
+
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
33
|
+
|
|
34
|
+
# Windows cmd 默认 GBK:测试自带 UTF-8 兜底,不依赖调用方记得加 -X utf8。
|
|
35
|
+
if hasattr(sys.stdout, "reconfigure"):
|
|
36
|
+
sys.stdout.reconfigure(encoding="utf-8", errors="replace")
|
|
37
|
+
|
|
38
|
+
from md_cg import hotcache, mcp_server as MS, scrub, trust
|
|
39
|
+
from md_cg.mdcg import MdCG
|
|
40
|
+
from md_cg.mdcos import MdCGSecure
|
|
41
|
+
from md_cg.security import DEFAULT_SENSITIVITY, Principal
|
|
42
|
+
|
|
43
|
+
_ok = 0
|
|
44
|
+
_bad = []
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def check(name, cond, detail=""):
|
|
48
|
+
global _ok
|
|
49
|
+
if cond:
|
|
50
|
+
_ok += 1
|
|
51
|
+
print("[PASS] " + name)
|
|
52
|
+
else:
|
|
53
|
+
_bad.append(name)
|
|
54
|
+
print("[FAIL] %s · %s" % (name, str(detail)[:220]))
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
Q = "量子咖啡机萃取压力曲线" # 罕见词:测试库内确定性命中
|
|
58
|
+
PAST, FUT = "2001-01-01", "2099-01-01"
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _princ():
|
|
62
|
+
return Principal(actor="t_validity", clearance=DEFAULT_SENSITIVITY,
|
|
63
|
+
can_write=True, can_admin=True, role="designer")
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def _ids(res):
|
|
67
|
+
"""命中集 id 清单。
|
|
68
|
+
|
|
69
|
+
口径=**nid 是否在命中集**,而非「结果为空」——全扫兜底返回低分节点
|
|
70
|
+
是检索器既有行为,不属过滤失败。
|
|
71
|
+
"""
|
|
72
|
+
out = []
|
|
73
|
+
for r in res or []:
|
|
74
|
+
n = r[0] if isinstance(r, tuple) else r
|
|
75
|
+
if isinstance(n, dict) and isinstance(n.get("node"), dict):
|
|
76
|
+
n = n["node"] # _cg_call(op=read) 的 {"node": view} 包装
|
|
77
|
+
out.append((n or {}).get("id"))
|
|
78
|
+
return out
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def _seed(cg):
|
|
82
|
+
"""三态语料:expired / not_yet / unknown + 一条图扩展专用的 expired。"""
|
|
83
|
+
cg.add("mem_now", Q + " 现行规格 9 巴", layer="knowledge",
|
|
84
|
+
verification_basis="test", consistency=False)
|
|
85
|
+
cg.add("mem_old", Q + " 旧版规格已被取代", layer="knowledge",
|
|
86
|
+
verification_basis="test", consistency=False, valid_until=PAST)
|
|
87
|
+
cg.add("mem_future", Q + " 下一代规格预告", layer="knowledge",
|
|
88
|
+
verification_basis="test", consistency=False, valid_from=FUT)
|
|
89
|
+
cg.add("mem_hub", Q + " 主档索引", layer="knowledge",
|
|
90
|
+
verification_basis="test", consistency=False,
|
|
91
|
+
edges=[{"target": "mem_ghost", "relation_type": "causal"}])
|
|
92
|
+
# 词面与 Q 零交集:只能经图扩展(edges)抵达 —— 图扩展绕过候选层时的靶子
|
|
93
|
+
cg.add("mem_ghost", "旁系条目与提问没有任何词面重合", layer="knowledge",
|
|
94
|
+
verification_basis="test", consistency=False, valid_until=PAST)
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
# ---------------------------------------------------------------- ① 纯函数
|
|
98
|
+
def _t1():
|
|
99
|
+
check("①expired → True", trust.is_expired({"valid_until": PAST}) is True)
|
|
100
|
+
check("①not_yet 不算过期(语义相反纪律)",
|
|
101
|
+
trust.is_expired({"valid_from": FUT}) is False)
|
|
102
|
+
check("①active → False",
|
|
103
|
+
trust.is_expired({"valid_from": "2020-01-01",
|
|
104
|
+
"valid_until": "2030-01-01"}) is False)
|
|
105
|
+
check("①unknown(无时间轴)→ False", trust.is_expired({}) is False)
|
|
106
|
+
check("①None 入参 → False", trust.is_expired(None) is False)
|
|
107
|
+
check("①端点不可解析 → 不猜测(False,不误判为过期)",
|
|
108
|
+
trust.is_expired({"valid_until": "不是时间"}) is False)
|
|
109
|
+
check("①now 显式传入生效",
|
|
110
|
+
trust.is_expired({"valid_until": "2030-01-01"},
|
|
111
|
+
now=trust.parse_time("2035-01-01")) is True)
|
|
112
|
+
check("①判定序:未来起点优先(not_yet 而非 expired)",
|
|
113
|
+
trust.validity({"valid_from": FUT, "valid_until": PAST})[0] == "not_yet")
|
|
114
|
+
check("①scrub 纪律交叉守卫:valid_from 绝不并入 _EXPIRY_KEYS",
|
|
115
|
+
"valid_from" not in scrub._EXPIRY_KEYS
|
|
116
|
+
and "valid_until" in scrub._EXPIRY_KEYS
|
|
117
|
+
and "valid_from" in scrub._NOT_YET_KEYS,
|
|
118
|
+
str(scrub._EXPIRY_KEYS))
|
|
119
|
+
# ---- 2026-09-19 阶段一:规范键族 + 第三类语义(信念时间)交叉守卫 ----
|
|
120
|
+
check("①阶段一:effective_from 属未生效族、绝不入已结束族",
|
|
121
|
+
"effective_from" in scrub._NOT_YET_KEYS
|
|
122
|
+
and "effective_from" not in scrub._EXPIRY_KEYS,
|
|
123
|
+
str(scrub._EXPIRY_KEYS))
|
|
124
|
+
check("①阶段一:effective_until/expired_at 属已结束族、不入未生效族",
|
|
125
|
+
"effective_until" in scrub._EXPIRY_KEYS
|
|
126
|
+
and "expired_at" in scrub._EXPIRY_KEYS
|
|
127
|
+
and "effective_until" not in scrub._NOT_YET_KEYS
|
|
128
|
+
and "expired_at" not in scrub._NOT_YET_KEYS,
|
|
129
|
+
str(scrub._EXPIRY_KEYS))
|
|
130
|
+
check("①阶段一:believed_at 两族都不入(第三类语义·物理隔离)",
|
|
131
|
+
trust.BELIEVED_FIELD not in scrub._EXPIRY_KEYS
|
|
132
|
+
and trust.BELIEVED_FIELD not in scrub._NOT_YET_KEYS,
|
|
133
|
+
str(scrub._EXPIRY_KEYS))
|
|
134
|
+
check("①键族同步守卫:trust 别名元组 ⊆ scrub 对应键族(两侧禁各自漂移)",
|
|
135
|
+
set(trust.FROM_ALIASES) <= set(scrub._NOT_YET_KEYS)
|
|
136
|
+
and set(trust.UNTIL_ALIASES) <= set(scrub._EXPIRY_KEYS),
|
|
137
|
+
str(trust.FROM_ALIASES) + " / " + str(scrub._NOT_YET_KEYS))
|
|
138
|
+
# ---- 规范键优先、别名回落(双轨读取)----
|
|
139
|
+
check("①规范键优先:effective_from 压过 valid_from",
|
|
140
|
+
trust.validity({"effective_from": FUT,
|
|
141
|
+
"valid_from": "2020-01-01"})[0] == "not_yet")
|
|
142
|
+
check("①别名回落:仅历史键时照常判定(存量兼容)",
|
|
143
|
+
trust.validity({"valid_from": FUT})[0] == "not_yet")
|
|
144
|
+
check("①规范键不可解析 → 继续回落别名(不因写坏一键而漏判)",
|
|
145
|
+
trust.validity({"effective_until": "坏值",
|
|
146
|
+
"valid_until": PAST})[0] == "expired")
|
|
147
|
+
check("①believed_at 不参与时效判定(物理隔离·绝不误判)",
|
|
148
|
+
trust.validity({"believed_at": PAST})[0] == "unknown"
|
|
149
|
+
and trust.is_expired({"believed_at": PAST}) is False)
|
|
150
|
+
check("①believed_at 读取器:epoch 值 / 缺字段 None",
|
|
151
|
+
trust.believed_at({"believed_at": PAST}) is not None
|
|
152
|
+
and trust.believed_at({}) is None)
|
|
153
|
+
check("①time_window_msg 点名实际命中键(规范名/历史名各归其位)",
|
|
154
|
+
"effective_from" in trust.time_window_msg({"effective_from": FUT})
|
|
155
|
+
and "valid_from" in trust.time_window_msg({"valid_from": FUT}))
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
# ------------------------------------------------- ②③ 候选面 + 默认关
|
|
159
|
+
def _t2(root_core, root_sec):
|
|
160
|
+
core = MdCG(root_core)
|
|
161
|
+
_seed(core)
|
|
162
|
+
|
|
163
|
+
off = _ids(core.search(Q, k=20)[0])
|
|
164
|
+
check("③MdCG.search 默认关:expired 可见(opt-in 可逆)",
|
|
165
|
+
"mem_old" in off, off)
|
|
166
|
+
check("③MdCG.search 默认关:not_yet 可见", "mem_future" in off, off)
|
|
167
|
+
on = _ids(core.search(Q, k=20, validity=True)[0])
|
|
168
|
+
check("②MdCG.search 启用后 expired 剔除", "mem_old" not in on, on)
|
|
169
|
+
check("②MdCG.search 启用后 not_yet 保留", "mem_future" in on, on)
|
|
170
|
+
check("②MdCG.search 启用后 unknown 保留", "mem_now" in on, on)
|
|
171
|
+
|
|
172
|
+
sec = MdCGSecure(root_sec, principal=_princ())
|
|
173
|
+
_seed(sec)
|
|
174
|
+
sec_off = _ids(sec.search(Q, k=20)[0])
|
|
175
|
+
check("③Secure.search 默认关:expired 可见", "mem_old" in sec_off, sec_off)
|
|
176
|
+
sec_on = _ids(sec.search(Q, k=20, validity=True)[0])
|
|
177
|
+
check("②Secure.search 启用后 expired 剔除", "mem_old" not in sec_on, sec_on)
|
|
178
|
+
check("②Secure.search 启用后 not_yet 保留",
|
|
179
|
+
"mem_future" in sec_on, sec_on)
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
# --------------------------- ④ RRF + 缓存分键 ⑤ recall ⑥ 图扩展
|
|
183
|
+
def _t3(root):
|
|
184
|
+
cg = MdCGSecure(root, principal=_princ())
|
|
185
|
+
_seed(cg)
|
|
186
|
+
hotcache.attach(cg)
|
|
187
|
+
|
|
188
|
+
off, _ = cg.search_rrf(Q, k=20)
|
|
189
|
+
on, _ = cg.search_rrf(Q, k=20, validity=True)
|
|
190
|
+
check("④search_rrf 默认关:expired 可见", "mem_old" in _ids(off), _ids(off))
|
|
191
|
+
check("④search_rrf 启用后 expired 剔除", "mem_old" not in _ids(on), _ids(on))
|
|
192
|
+
check("④search_rrf 启用后 not_yet 保留", "mem_future" in _ids(on), _ids(on))
|
|
193
|
+
|
|
194
|
+
# 缓存分键:两口径必须各自成键,否则第二次调用命中第一次的缓存即串口径
|
|
195
|
+
hc = hotcache.get(cg)
|
|
196
|
+
on2, meta2 = cg.search_rrf(Q, k=20, validity=True)
|
|
197
|
+
off2, meta_off = cg.search_rrf(Q, k=20)
|
|
198
|
+
check("④缓存命中路径仍零 expired(meta.cached 可证命中)",
|
|
199
|
+
"mem_old" not in _ids(on2) and meta2.get("cached") is True,
|
|
200
|
+
"cached=%s ids=%s" % (meta2.get("cached"), _ids(on2)))
|
|
201
|
+
check("④两口径各自成键且互不顶替(默认口径仍见 expired 且命中自身缓存)",
|
|
202
|
+
"mem_old" in _ids(off2) and meta_off.get("cached") is True,
|
|
203
|
+
"off_cached=%s ids=%s" % (meta_off.get("cached"), _ids(off2)))
|
|
204
|
+
check("④缓存中两口径键并存(>=2 条)",
|
|
205
|
+
hc.stats()["query_cache_size"] >= 2, hc.stats())
|
|
206
|
+
|
|
207
|
+
# ⑥ 图扩展:零词面交集节点只能经 edges 抵达 —— 前置证据 + 过滤断言
|
|
208
|
+
check("⑥前置证据:不过滤时 ghost 经图扩展可达(零词面交集)",
|
|
209
|
+
"mem_ghost" in _ids(off), _ids(off))
|
|
210
|
+
check("⑥启用后 ghost 被剔除(图扩展不绕过时效过滤)",
|
|
211
|
+
"mem_ghost" not in _ids(on), _ids(on))
|
|
212
|
+
|
|
213
|
+
# ⑤ recall 两分支
|
|
214
|
+
p_off = [p.get("id") for p in cg.recall(Q, budget_tokens=6000, k=20)["pack"]]
|
|
215
|
+
check("⑤recall(RRF 分支)默认关:expired 可见", "mem_old" in p_off, p_off)
|
|
216
|
+
p_rrf = [p.get("id") for p in cg.recall(Q, budget_tokens=6000, k=20,
|
|
217
|
+
validity=True)["pack"]]
|
|
218
|
+
check("⑤recall(RRF 分支)启用后 expired 剔除", "mem_old" not in p_rrf, p_rrf)
|
|
219
|
+
check("⑤recall(RRF 分支)not_yet 保留", "mem_future" in p_rrf, p_rrf)
|
|
220
|
+
p_lin = [p.get("id") for p in cg.recall(Q, budget_tokens=6000, k=20,
|
|
221
|
+
use_rrf=False, validity=True)["pack"]]
|
|
222
|
+
check("⑤recall(非 RRF 分支)启用后 expired 剔除",
|
|
223
|
+
"mem_old" not in p_lin, p_lin)
|
|
224
|
+
check("⑤recall(非 RRF 分支)not_yet 保留", "mem_future" in p_lin, p_lin)
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
# ------------------------------------------------------------- ⑦ MCP 面
|
|
228
|
+
def _t4(root):
|
|
229
|
+
cg = MdCGSecure(root, principal=_princ())
|
|
230
|
+
_seed(cg)
|
|
231
|
+
|
|
232
|
+
s_on = MS.call_tool(cg, "mdcg_search",
|
|
233
|
+
{"query": Q, "k": 20, "validity": True})
|
|
234
|
+
ids_on = [r["node"]["id"] for r in s_on["results"]]
|
|
235
|
+
check("⑦mdcg_search 透传 validity(expired 剔除)",
|
|
236
|
+
"mem_old" not in ids_on, ids_on)
|
|
237
|
+
check("⑦mdcg_search 透传后 not_yet 保留", "mem_future" in ids_on, ids_on)
|
|
238
|
+
s_off = MS.call_tool(cg, "mdcg_search", {"query": Q, "k": 20})
|
|
239
|
+
ids_off = [r["node"]["id"] for r in s_off["results"]]
|
|
240
|
+
check("⑦默认关:mdcg_search 缺省 expired 可见", "mem_old" in ids_off, ids_off)
|
|
241
|
+
|
|
242
|
+
rc = MS.call_tool(cg, "mdcg_recall",
|
|
243
|
+
{"query": Q, "k": 20, "budget_tokens": 6000,
|
|
244
|
+
"validity": True})
|
|
245
|
+
pids = [p.get("id") for p in rc["pack"]]
|
|
246
|
+
check("⑦mdcg_recall 透传 validity(expired 剔除)",
|
|
247
|
+
"mem_old" not in pids, pids)
|
|
248
|
+
rc_off = MS.call_tool(cg, "mdcg_recall",
|
|
249
|
+
{"query": Q, "k": 20, "budget_tokens": 6000})
|
|
250
|
+
check("⑦默认关:mdcg_recall 缺省 expired 可见",
|
|
251
|
+
"mem_old" in [p.get("id") for p in rc_off["pack"]], rc_off.get("pack"))
|
|
252
|
+
|
|
253
|
+
out = MS._cg_call(cg, {"op": "read", "query": Q, "k": 20, "validity": True})
|
|
254
|
+
oids = _ids(out.get("results") or [])
|
|
255
|
+
check("⑦cg op=read 透传 validity(expired 剔除)", "mem_old" not in oids, oids)
|
|
256
|
+
out_off = MS._cg_call(cg, {"op": "read", "query": Q, "k": 20})
|
|
257
|
+
check("⑦默认关:cg op=read 缺省 expired 可见",
|
|
258
|
+
"mem_old" in _ids(out_off.get("results") or []),
|
|
259
|
+
_ids(out_off.get("results") or []))
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
def main():
|
|
263
|
+
tmp = tempfile.mkdtemp(prefix="mdcg_validity_")
|
|
264
|
+
try:
|
|
265
|
+
_t1()
|
|
266
|
+
_t2(os.path.join(tmp, "core"), os.path.join(tmp, "sec"))
|
|
267
|
+
_t3(os.path.join(tmp, "rrf"))
|
|
268
|
+
_t4(os.path.join(tmp, "mcp"))
|
|
269
|
+
except Exception:
|
|
270
|
+
traceback.print_exc()
|
|
271
|
+
_bad.append("未捕获异常")
|
|
272
|
+
finally:
|
|
273
|
+
shutil.rmtree(tmp, ignore_errors=True)
|
|
274
|
+
print("\nvalidity 验收:%d 通过%s" % (
|
|
275
|
+
_ok, (",%d 失败:%s" % (len(_bad), "; ".join(_bad))) if _bad else ""))
|
|
276
|
+
return 1 if _bad else 0
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
if __name__ == "__main__":
|
|
280
|
+
sys.exit(main())
|
|
@@ -26,6 +26,7 @@ for _p in (_ROOT, _HERE, os.path.join(_HERE, "whitebox_kb"),
|
|
|
26
26
|
from wisdom_book import (ConditionDex, DEFAULT_DB, DEFAULT_MD_ROOT, # noqa: E402
|
|
27
27
|
BASE_ENTRIES, BASE_RELATIONS)
|
|
28
28
|
from md_access import MdConn, read_conn # noqa: E402
|
|
29
|
+
from md_whitebox import corpus_ready # noqa: E402
|
|
29
30
|
from md_store import MdStore # noqa: E402
|
|
30
31
|
from aeis_core import ConditionSpace, MemoryLayer, STNode # noqa: E402
|
|
31
32
|
|
|
@@ -44,8 +45,11 @@ def ok(cond, label):
|
|
|
44
45
|
# ---------------------------------------------------------------------------
|
|
45
46
|
|
|
46
47
|
def group_a(tmp):
|
|
47
|
-
|
|
48
|
-
|
|
48
|
+
# 语料就绪判据 = 「目录存在**且含 .md**」(2026-09-20 v14 缺陷 F):空壳
|
|
49
|
+
# 目录(兄弟测试经 MdCGOS(root) 的 makedirs 留下)不算就绪,否则组A 会
|
|
50
|
+
# 以「md 语料行数 0 > 4000」的失败示人,而真实原因是真源根本不在位。
|
|
51
|
+
if not (os.path.exists(DEFAULT_DB) and corpus_ready(DEFAULT_MD_ROOT)):
|
|
52
|
+
print("组A SKIP:生产对拍数据不全(派生库缺失或语料根缺失/空壳)")
|
|
49
53
|
return
|
|
50
54
|
print("组A · 生产数据对拍(全量)")
|
|
51
55
|
# sqlite 侧用副本隔离:search_content 的 increment_access 会落库,
|
|
@@ -258,7 +262,7 @@ def group_d(tmp):
|
|
|
258
262
|
ok(not dex2._md_mode, "fresh=True → sqlite 形态(兼容 api.py 兜底)")
|
|
259
263
|
dex2.close()
|
|
260
264
|
|
|
261
|
-
if
|
|
265
|
+
if corpus_ready(DEFAULT_MD_ROOT):
|
|
262
266
|
dex3 = ConditionDex(md_root=DEFAULT_MD_ROOT)
|
|
263
267
|
try:
|
|
264
268
|
conn = read_conn(dex3) # duck:MdStore.conn → MdConn
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""写入路径拦截器链(writepipe)验收:Pi 钩子化移植(交接文档 §3⑥)。
|
|
3
|
+
|
|
4
|
+
验收口径:
|
|
5
|
+
- 默认链行为与重构前 _cg_dispatch write 分支逐字节一致
|
|
6
|
+
(ACCEPT 直写 / REJECT 负记忆 / DEFER 入队 + 幂等去重联动 / gated 闸 /
|
|
7
|
+
冲突 cvd 透传);
|
|
8
|
+
- 新增/移除一个拦截器不改核心文件(register/unregister 即插即拔);
|
|
9
|
+
- 角色/层权限校验结构上不可被拦截器绕过(反面清单:信任必须结构强制);
|
|
10
|
+
- REWRITE(拦截器改写 ctx["a"]["content"])/ 链序 / 短路 / after 观测者。
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
import json
|
|
14
|
+
import os
|
|
15
|
+
import shutil
|
|
16
|
+
import sys
|
|
17
|
+
import tempfile
|
|
18
|
+
import traceback
|
|
19
|
+
|
|
20
|
+
from . import writepipe
|
|
21
|
+
from .mdcos import MdCGSecure
|
|
22
|
+
from .security import AccessDenied, Principal
|
|
23
|
+
|
|
24
|
+
_ok = 0
|
|
25
|
+
_bad = []
|
|
26
|
+
_seq = [0]
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _check(name, cond, detail=""):
|
|
30
|
+
global _ok
|
|
31
|
+
if cond:
|
|
32
|
+
_ok += 1
|
|
33
|
+
print(" ok %s" % name)
|
|
34
|
+
else:
|
|
35
|
+
_bad.append("%s %s" % (name, detail))
|
|
36
|
+
print(" FAIL %s %s" % (name, detail))
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _mk_cg(tmp, role="designer"):
|
|
40
|
+
_seq[0] += 1
|
|
41
|
+
root = os.path.join(tmp, "root_%02d_%s" % (_seq[0], role))
|
|
42
|
+
p = Principal(tenant="default", actor="t_" + role, role=role,
|
|
43
|
+
can_write=(role != "guest"),
|
|
44
|
+
can_admin=(role == "designer"))
|
|
45
|
+
return MdCGSecure(root, principal=p)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _policy(tmp, required=("PASSED",), forbidden=("FORBIDDEN_WORD",)):
|
|
49
|
+
path = os.path.join(tmp, "policy.json")
|
|
50
|
+
with open(path, "w", encoding="utf-8") as f:
|
|
51
|
+
json.dump({"forbidden": list(forbidden),
|
|
52
|
+
"required": list(required)}, f)
|
|
53
|
+
os.environ["MDCG_POLICY_FILE"] = path
|
|
54
|
+
return path
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def main():
|
|
58
|
+
tmp = tempfile.mkdtemp(prefix="writepipe_")
|
|
59
|
+
old_policy = os.environ.pop("MDCG_POLICY_FILE", None)
|
|
60
|
+
try:
|
|
61
|
+
_run(tmp)
|
|
62
|
+
except Exception:
|
|
63
|
+
traceback.print_exc()
|
|
64
|
+
_bad.append("未捕获异常")
|
|
65
|
+
finally:
|
|
66
|
+
shutil.rmtree(tmp, ignore_errors=True)
|
|
67
|
+
if old_policy is not None:
|
|
68
|
+
os.environ["MDCG_POLICY_FILE"] = old_policy
|
|
69
|
+
else:
|
|
70
|
+
os.environ.pop("MDCG_POLICY_FILE", None)
|
|
71
|
+
print("\nwritepipe 验收:%d 通过%s" % (
|
|
72
|
+
_ok, (",%d 失败:%s" % (len(_bad), "; ".join(_bad))) if _bad else ""))
|
|
73
|
+
return 1 if _bad else 0
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def _run(tmp):
|
|
77
|
+
policy = _policy(tmp)
|
|
78
|
+
|
|
79
|
+
# ---------- 1 默认链:与重构前 write 分支行为一致 ----------
|
|
80
|
+
pipe = writepipe.install_default_gates(writepipe.WritePipeline())
|
|
81
|
+
cg = _mk_cg(tmp)
|
|
82
|
+
|
|
83
|
+
out = pipe.execute(cg, {"content_kind": "text",
|
|
84
|
+
"content": "要素齐全 PASSED",
|
|
85
|
+
"layer": "knowledge"})
|
|
86
|
+
_check("默认链ACCEPT直写", out.get("committed") is True and out.get("ok") is True,
|
|
87
|
+
repr(out))
|
|
88
|
+
_check("冲突闸cvd透传", isinstance(out.get("consistency"), dict), repr(out))
|
|
89
|
+
|
|
90
|
+
out = pipe.execute(cg, {"content_kind": "text",
|
|
91
|
+
"content": "含 FORBIDDEN_WORD 的违禁内容",
|
|
92
|
+
"layer": "knowledge"})
|
|
93
|
+
_check("REJECT负记忆", out.get("moved_to") == "rejected"
|
|
94
|
+
and out.get("committed") is False, repr(out))
|
|
95
|
+
|
|
96
|
+
# DEFER(无规则文件 → 无法判定 → 入审核队列)+ 幂等去重联动
|
|
97
|
+
os.environ.pop("MDCG_POLICY_FILE", None)
|
|
98
|
+
a = {"content_kind": "text", "content": "无规则可判的内容",
|
|
99
|
+
"layer": "knowledge"}
|
|
100
|
+
out1 = pipe.execute(cg, dict(a))
|
|
101
|
+
_check("DEFER入队", out1.get("moved_to") == "review_queue"
|
|
102
|
+
and bool(out1.get("pid")), repr(out1))
|
|
103
|
+
out2 = pipe.execute(cg, dict(a))
|
|
104
|
+
_check("幂等去重联动", out2.get("dedup") is True
|
|
105
|
+
and out2.get("dup_of") == out1.get("pid"), repr(out2))
|
|
106
|
+
os.environ["MDCG_POLICY_FILE"] = policy
|
|
107
|
+
|
|
108
|
+
out = pipe.execute(cg, {"content_kind": "text",
|
|
109
|
+
"content": "PASSED 情境记忆", "gated": True,
|
|
110
|
+
"layer": "contextual"})
|
|
111
|
+
_check("gated闸代执行", "gate" in out, repr(out))
|
|
112
|
+
|
|
113
|
+
# ---------- 2 注册/移除拦截器不改核心文件 ----------
|
|
114
|
+
singleton = writepipe.default_pipeline()
|
|
115
|
+
|
|
116
|
+
def _block_skipme(ctx):
|
|
117
|
+
if "SKIPME" in (ctx["a"].get("content") or ""):
|
|
118
|
+
return {"ok": False, "committed": False,
|
|
119
|
+
"moved_to": "hook_blocked", "by": "test_block"}
|
|
120
|
+
return None
|
|
121
|
+
|
|
122
|
+
singleton.register_before("test_block", _block_skipme)
|
|
123
|
+
out = singleton.execute(cg, {"content_kind": "text",
|
|
124
|
+
"content": "PASSED SKIPME",
|
|
125
|
+
"layer": "knowledge"})
|
|
126
|
+
_check("注册即拦", out.get("moved_to") == "hook_blocked", repr(out))
|
|
127
|
+
singleton.unregister_before("test_block")
|
|
128
|
+
out = singleton.execute(cg, {"content_kind": "text",
|
|
129
|
+
"content": "PASSED SKIPME",
|
|
130
|
+
"layer": "knowledge"})
|
|
131
|
+
_check("移除即恢复(核心文件零改动)", out.get("committed") is True, repr(out))
|
|
132
|
+
# 默认 before 链含 linkref→deps→audit→consistency→gated(2026-09-19 deps 闸门新增)
|
|
133
|
+
_check("单例默认链", singleton.names()["before"]
|
|
134
|
+
== ["linkref", "deps", "audit", "consistency", "gated"],
|
|
135
|
+
repr(singleton.names()))
|
|
136
|
+
_check("单例默认after链", singleton.names()["after"]
|
|
137
|
+
== ["linkref", "trust"],
|
|
138
|
+
repr(singleton.names()))
|
|
139
|
+
|
|
140
|
+
# ---------- 3 链序 / 短路 / REWRITE ----------
|
|
141
|
+
p3 = writepipe.WritePipeline()
|
|
142
|
+
order = []
|
|
143
|
+
p3.register_before("g1", lambda ctx: order.append("g1") or None)
|
|
144
|
+
p3.register_before("g2", lambda ctx: order.append("g2") or None)
|
|
145
|
+
p3.register_before("g3", lambda ctx: order.append("g3")
|
|
146
|
+
or {"ok": False, "moved_to": "halt"})
|
|
147
|
+
cg3 = _mk_cg(tmp)
|
|
148
|
+
out = p3.execute(cg3, {"content_kind": "text", "content": "PASSED",
|
|
149
|
+
"layer": "knowledge"})
|
|
150
|
+
_check("链序按注册序", order == ["g1", "g2", "g3"], repr(order))
|
|
151
|
+
_check("终态短路不落盘", out.get("moved_to") == "halt"
|
|
152
|
+
and "id" not in out and "committed" not in out, repr(out))
|
|
153
|
+
|
|
154
|
+
p4 = writepipe.WritePipeline()
|
|
155
|
+
p4.register_before("rw", lambda ctx:
|
|
156
|
+
ctx["a"].__setitem__("content",
|
|
157
|
+
ctx["a"].get("content") + " REWRITTEN")
|
|
158
|
+
or None)
|
|
159
|
+
cg4 = _mk_cg(tmp)
|
|
160
|
+
out = p4.execute(cg4, {"content_kind": "text", "content": "PASSED",
|
|
161
|
+
"layer": "knowledge"})
|
|
162
|
+
_check("REWRITE改写生效", out.get("committed") is True
|
|
163
|
+
and bool(cg4.search("REWRITTEN")), repr(out))
|
|
164
|
+
|
|
165
|
+
# ---------- 4 权限结构上不可被拦截器绕过 ----------
|
|
166
|
+
cg_guest = _mk_cg(tmp, role="guest")
|
|
167
|
+
p5 = writepipe.WritePipeline()
|
|
168
|
+
p5.register_before("always_pass", lambda ctx: None)
|
|
169
|
+
try:
|
|
170
|
+
p5.execute(cg_guest, {"content_kind": "text", "content": "PASSED",
|
|
171
|
+
"consistency": False, "layer": "knowledge"})
|
|
172
|
+
_check("权限不可绕", False, "guest 写入未被拒")
|
|
173
|
+
except AccessDenied:
|
|
174
|
+
_check("权限不可绕", True)
|
|
175
|
+
except Exception as e: # noqa: BLE001
|
|
176
|
+
_check("权限不可绕", False,
|
|
177
|
+
"异常类型=%s:%s" % (type(e).__name__, e))
|
|
178
|
+
|
|
179
|
+
# ---------- 5 after 观测者 ----------
|
|
180
|
+
p6 = writepipe.WritePipeline()
|
|
181
|
+
seen = []
|
|
182
|
+
p6.register_after("spy", lambda ctx, out: seen.append(out.get("committed")))
|
|
183
|
+
cg6 = _mk_cg(tmp)
|
|
184
|
+
p6.execute(cg6, {"content_kind": "text", "content": "PASSED",
|
|
185
|
+
"layer": "knowledge"})
|
|
186
|
+
_check("after观测成功", seen == [True], repr(seen))
|
|
187
|
+
|
|
188
|
+
def _boom(ctx, out):
|
|
189
|
+
raise RuntimeError("after钩子故障")
|
|
190
|
+
|
|
191
|
+
p6.register_after("boom", _boom)
|
|
192
|
+
try:
|
|
193
|
+
p6.execute(cg6, {"content_kind": "text", "content": "PASSED 二次",
|
|
194
|
+
"layer": "knowledge"})
|
|
195
|
+
_check("after故障不吞", False, "异常被静默")
|
|
196
|
+
except RuntimeError:
|
|
197
|
+
_check("after故障不吞", True)
|
|
198
|
+
|
|
199
|
+
# ---------- 6 注册表卫生 ----------
|
|
200
|
+
p7 = writepipe.WritePipeline()
|
|
201
|
+
p7.register_before("x", lambda ctx: None)
|
|
202
|
+
p7.register_before("x", lambda ctx: None) # 同名幂等替换
|
|
203
|
+
_check("同名幂等注册", p7.names()["before"] == ["x"], repr(p7.names()))
|
|
204
|
+
try:
|
|
205
|
+
p7.register_before("y", 123)
|
|
206
|
+
_check("类型守卫", False, "非可调用未拒")
|
|
207
|
+
except TypeError:
|
|
208
|
+
_check("类型守卫", True)
|
|
209
|
+
_check("注销返回布尔", p7.unregister_before("x") is True
|
|
210
|
+
and p7.unregister_before("x") is False)
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
if __name__ == "__main__":
|
|
214
|
+
sys.exit(main())
|
package/md_cg/theory.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
2
|
"""md_cg · 版本层(蜂群互联 · 层0)
|
|
3
3
|
|
|
4
|
-
设计依据:`docs/蜂群互联_v0.1.md` §2.3 / §3.1 / §10。
|
|
4
|
+
设计依据:`docs/swarm/蜂群互联_v0.1.md` §2.3 / §3.1 / §10。
|
|
5
5
|
|
|
6
6
|
版本更新权 = 蜂群**唯一核心权限**(严格唯一,仅 designer)。本模块只做
|
|
7
7
|
「声明 + 校验」两件事,不做跨节点证据交换(§9.1 属存在约束,留待 v0.3):
|
|
@@ -52,24 +52,29 @@ class TheoryError(Exception):
|
|
|
52
52
|
# 存储
|
|
53
53
|
# --------------------------------------------------------------------------
|
|
54
54
|
|
|
55
|
+
# 生效条件:path 为真值时返回 path,否则取 os.environ.get(THEORY_FILE_ENV),该值为空串/未设置时回落模块常量 DEFAULT_THEORY_FILE。
|
|
55
56
|
def theory_file(path: str = None) -> str:
|
|
56
57
|
return path or os.environ.get(THEORY_FILE_ENV) or DEFAULT_THEORY_FILE
|
|
57
58
|
|
|
58
59
|
|
|
60
|
+
# 生效条件:传入可被 json 序列化的 payload(dict)时返回 json.dumps(payload, sort_keys=True, ensure_ascii=False, separators=(",", ":"))。
|
|
59
61
|
def _canon(payload: dict) -> str:
|
|
60
62
|
"""规范化 JSON:键排序 + 紧凑分隔符(哈希可复现的唯一前提)。"""
|
|
61
63
|
return json.dumps(payload, sort_keys=True, ensure_ascii=False,
|
|
62
64
|
separators=(",", ":"))
|
|
63
65
|
|
|
64
66
|
|
|
67
|
+
# 生效条件:payload 可被 _canon 序列化时返回 hashlib.sha256(_canon(payload).encode("utf-8")).hexdigest() 的十六进制摘要。
|
|
65
68
|
def declaration_hash(payload: dict) -> str:
|
|
66
69
|
return hashlib.sha256(_canon(payload).encode("utf-8")).hexdigest()
|
|
67
70
|
|
|
68
71
|
|
|
72
|
+
# 生效条件:对 SEALED_FIELDS 中每个 k 用 decl.get(k) 取值(缺键即 None),返回同集合长度的 dict。
|
|
69
73
|
def _payload(decl: dict) -> dict:
|
|
70
74
|
return {k: decl.get(k) for k in SEALED_FIELDS}
|
|
71
75
|
|
|
72
76
|
|
|
77
|
+
# 生效条件:以 dict(decl) 为副本 d,写入 d["declaration_hash"]=declaration_hash(_payload(d)),返回该副本(不改动入参 decl)。
|
|
73
78
|
def seal(decl: dict) -> dict:
|
|
74
79
|
"""封缄:补上 declaration_hash。"""
|
|
75
80
|
d = dict(decl)
|
|
@@ -77,6 +82,7 @@ def seal(decl: dict) -> dict:
|
|
|
77
82
|
return d
|
|
78
83
|
|
|
79
84
|
|
|
85
|
+
# 生效条件:p=theory_file(path) 后,os.path.exists(p) 为假返回 None;open/json.load 抛 OSError 或 ValueError 也返回 None;否则 d 是 dict 时返回 d,不是 dict 返回 None。
|
|
80
86
|
def load(path: str = None):
|
|
81
87
|
p = theory_file(path)
|
|
82
88
|
if not os.path.exists(p):
|
|
@@ -89,6 +95,7 @@ def load(path: str = None):
|
|
|
89
95
|
return d if isinstance(d, dict) else None
|
|
90
96
|
|
|
91
97
|
|
|
98
|
+
# 生效条件:p=theory_file(path),先 os.makedirs(os.path.dirname(p) or "."),把 decl 以 ensure_ascii=False, indent=1 写入 p+".tmp" 再 os.replace 到 p,os.chmod(p, 0o600) 抛 OSError 时忽略,最终返回 p。
|
|
92
99
|
def save(decl: dict, path: str = None) -> str:
|
|
93
100
|
p = theory_file(path)
|
|
94
101
|
os.makedirs(os.path.dirname(p) or ".", exist_ok=True)
|
|
@@ -107,6 +114,7 @@ def save(decl: dict, path: str = None) -> str:
|
|
|
107
114
|
# 声明与校验
|
|
108
115
|
# --------------------------------------------------------------------------
|
|
109
116
|
|
|
117
|
+
# 生效条件:v=(version or PROTOCOL_VERSION).strip(),v 为空串时抛 TheoryError;accepted 为假值(None/空)时 acc=[v],否则 list(accepted) 并剔除 strip 后为空的项,v 不在 acc 时 insert(0, v);declared_by 假值回落 "designer",declared_at=float(now if now is not None else time.time()),返回含 SCHEMA 的 dict。
|
|
110
118
|
def make_declaration(version: str = None, accepted=None,
|
|
111
119
|
declared_by: str = "designer", now: float = None) -> dict:
|
|
112
120
|
"""构造(未封缄的)版本声明。accepted 默认只含自身版本。"""
|
|
@@ -122,6 +130,7 @@ def make_declaration(version: str = None, accepted=None,
|
|
|
122
130
|
"declared_at": float(now if now is not None else time.time())}
|
|
123
131
|
|
|
124
132
|
|
|
133
|
+
# 生效条件:d=decl if decl is not None else load(theory_file(path));d 为假时返回 theory_ok=True、auto=True、version=PROTOCOL_VERSION 的状态;d.get("schema")!=SCHEMA、hmac.compare_digest(str(d.get("declaration_hash") or ""), declaration_hash(_payload(d))) 不通过、v=d.get("theory_version") 为假、或 v 不在 acc=[str(x) for x in (d.get("accepted_versions") or [])] 时各自返回同一个 base 且 theory_ok=False,其余情况 base 更新为 theory_ok=True、reason="ok"。
|
|
125
134
|
def check(path: str = None, decl: dict = None) -> dict:
|
|
126
135
|
"""校验版本声明 → 状态字典(永不抛异常,供启动路径安全调用)。
|
|
127
136
|
|
|
@@ -164,6 +173,7 @@ def check(path: str = None, decl: dict = None) -> dict:
|
|
|
164
173
|
return base
|
|
165
174
|
|
|
166
175
|
|
|
176
|
+
# 生效条件:p=theory_file(path),仅当 load(p) 为 None 时尝试 declare(PROTOCOL_VERSION, actor="system:autoinit", path=p, append_audit=False) 并吞掉 TheoryError/OSError,最终返回 check(p)。
|
|
167
177
|
def ensure(path: str = None) -> dict:
|
|
168
178
|
"""启动路径:无声明则落盘默认声明(声明常态化),再返回校验状态。
|
|
169
179
|
|
|
@@ -179,6 +189,7 @@ def ensure(path: str = None) -> dict:
|
|
|
179
189
|
return check(p)
|
|
180
190
|
|
|
181
191
|
|
|
192
|
+
# 生效条件:version=None/空串时在 make_declaration 内回落 PROTOCOL_VERSION(仅回落结果 strip 后仍为空才抛 TheoryError),seal 后若 append_audit 为真值则把 (old or {}).get("audit") 或 [] 追加一条(仅 old 为真时追加)写入 decl["audit"],save(decl, path) 后返回 {'ok': True, 'declaration': decl, 'status': check(path)}。
|
|
182
193
|
def declare(version: str = None, accepted=None, actor: str = "designer",
|
|
183
194
|
path: str = None, append_audit: bool = True) -> dict:
|
|
184
195
|
"""写入版本声明(版本更新权的唯一物理落点)。"""
|
|
@@ -195,11 +206,13 @@ def declare(version: str = None, accepted=None, actor: str = "designer",
|
|
|
195
206
|
return {"ok": True, "declaration": decl, "status": check(path)}
|
|
196
207
|
|
|
197
208
|
|
|
209
|
+
# 生效条件:以 path 调 load、check、theory_file,返回 {"declaration": load(path), "status": check(path), "theory_file": theory_file(path)}。
|
|
198
210
|
def show(path: str = None) -> dict:
|
|
199
211
|
return {"declaration": load(path), "status": check(path),
|
|
200
212
|
"theory_file": theory_file(path)}
|
|
201
213
|
|
|
202
214
|
|
|
215
|
+
# 生效条件:返回含模块常量 SCHEMA、PROTOCOL_VERSION、list(SEALED_FIELDS)、list(ESCAPE_OPS) 及 theory_file() 与 THEORY_ENV/THEORY_FILE_ENV 的自描述 dict。
|
|
203
216
|
def catalog() -> dict:
|
|
204
217
|
"""版本层自描述(供 whoami / service_info 暴露)。"""
|
|
205
218
|
return {"schema": SCHEMA, "theory_file": theory_file(),
|
|
@@ -213,10 +226,12 @@ def catalog() -> dict:
|
|
|
213
226
|
# CLI
|
|
214
227
|
# --------------------------------------------------------------------------
|
|
215
228
|
|
|
229
|
+
# 生效条件:把 json.dumps(obj, ensure_ascii=False, indent=1)+"\n" 写入 sys.stdout,无返回值。
|
|
216
230
|
def _print(obj):
|
|
217
231
|
sys.stdout.write(json.dumps(obj, ensure_ascii=False, indent=1) + "\n")
|
|
218
232
|
|
|
219
233
|
|
|
234
|
+
# 生效条件:argv 交给 ap.parse_args(argv 为 None 时 argparse 读 sys.argv),子解析器 required=True 缺 cmd 时由 argparse 报错退出;a.cmd 为 declare/check/ensure/show/catalog 之一且未抛 TheoryError 时打印并返回 0,抛出 TheoryError 时写 stderr 返回 2。
|
|
220
235
|
def main(argv=None):
|
|
221
236
|
ap = argparse.ArgumentParser(
|
|
222
237
|
prog="python -m md_cg.theory",
|
|
@@ -256,4 +271,4 @@ def main(argv=None):
|
|
|
256
271
|
|
|
257
272
|
|
|
258
273
|
if __name__ == "__main__":
|
|
259
|
-
sys.exit(main())
|
|
274
|
+
sys.exit(main())
|