@furongjun1999/dsh-memory 0.6.0 → 0.7.0
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 +618 -572
- package/codebuddy/CODEBUDDY.md +10 -10
- package/data/policy.json +27 -0
- package/docs/README.md +1 -0
- package/docs/discipline/harnesses.yaml +31 -0
- package/docs/discipline/templates/full.md.tmpl +1 -1
- package/docs/discipline/templates/rules.mdc.tmpl +1 -1
- package/docs/eval/N225_/347/264/242/345/274/225/346/227/245/345/277/227/351/235/236/345/257/271/350/261/241/350/243/205/350/275/275/351/235/242/347/261/273/345/236/213/351/227/270_v1.0.md +418 -0
- package/docs/eval/issue43_/351/273/230/350/256/244/347/255/226/347/225/245/344/270/216/351/224/256/347/261/273/345/236/213/351/227/270_v1.1.md +441 -0
- package/docs/eval/issue43_/351/273/230/350/256/244/347/255/226/347/225/245/345/212/240/350/275/275/344/270/216/345/207/255/346/215/256/346/230/216/346/226/207/351/230/237/345/210/227_v1.0.md +398 -0
- package/docs/eval//344/274/230/345/214/226/347/254/254/344/270/200/346/211/271_/346/216/245/347/272/277/344/270/216/347/255/211/344/273/267/345/217/230/346/215/242_v1.0.md +498 -0
- package/docs/eval//344/274/230/345/214/226/347/254/254/344/270/211/346/211/271_/351/227/250/347/246/201/350/275/254/346/255/243/344/270/216/350/260/203/345/272/246/346/255/242/350/241/200/344/270/216/351/227/250/346/216/247/346/224/266/345/217/243_v1.0.md +669 -0
- package/docs/eval//344/274/230/345/214/226/347/254/254/344/272/214/346/211/271_/344/276/235/350/265/226/351/200/217/344/274/240/344/270/216/345/257/271/346/213/215/345/217/243/345/276/204/344/270/216/350/264/237/347/274/223/345/255/230_v1.0.md +683 -0
- package/docs/eval//345/207/272/350/264/247/351/235/242/345/206/222/347/203/237_/350/277/233/350/264/247/351/227/250/347/246/201_v1.0.md +460 -0
- package/docs/eval//345/217/221/345/270/20307_/345/244/226/351/203/250/346/212/245/345/221/212/345/233/233/346/211/271/344/277/256/345/244/215/344/270/216/346/217/222/344/273/266/351/235/242/345/212/240/345/233/272_v1.0.md +205 -0
- package/docs/eval//345/217/221/345/270/20308_/350/207/252/350/277/255/344/273/243/344/270/216/347/235/241/347/234/240_/345/233/276/346/243/200/347/264/242/350/267/257/344/270/216/346/235/203/351/207/215_v1.0.md +97 -0
- package/docs/eval//345/275/222/344/270/200/345/261/202/347/274/272/347/234/201/347/277/273/345/205/263_/344/277/256/345/244/215/350/256/260/345/275/225_v1.0.md +458 -0
- package/docs/eval//346/200/247/350/203/275/344/270/223/351/241/271_/345/206/267/346/237/245/350/257/242/344/270/216/345/206/205/345/255/230_v1.0.md +218 -0
- package/docs/eval//346/225/205/351/232/234/346/263/250/345/205/245/345/256/236/346/265/213_v1.0.md +10 -0
- package/docs/eval//347/274/226/347/240/201/351/235/242/345/211/215/347/275/256_/345/205/245/345/217/243/350/207/252/344/277/235/350/257/201UTF8/344/270/216/346/226/207/346/234/254open/345/256/210/345/215/253_v1.0.md +646 -0
- package/docs/eval//347/274/272/351/231/267/346/214/226/346/216/230_/350/207/252/344/270/273/350/277/255/344/273/243_v20.md +283 -0
- package/docs/eval//347/274/272/351/231/267/346/214/226/346/216/230_/350/207/252/344/270/273/350/277/255/344/273/243_v21.md +224 -0
- package/docs/eval//347/274/272/351/231/267/346/214/226/346/216/230_/350/207/252/344/270/273/350/277/255/344/273/243_v22.md +223 -0
- package/docs/eval//347/274/272/351/231/267/346/214/226/346/216/230_/350/207/252/344/270/273/350/277/255/344/273/243_v23.md +293 -0
- package/docs/eval//347/274/272/351/231/267/346/214/226/346/216/230_/350/207/252/344/270/273/350/277/255/344/273/243_v24.md +303 -0
- package/docs/eval//347/274/272/351/231/267/346/214/226/346/216/230_/350/207/252/344/270/273/350/277/255/344/273/243_v25.md +230 -0
- package/docs/hive//346/243/200/347/264/242/347/256/227/346/263/225/345/217/243/345/276/204/345/257/271/347/205/247_v0.1.md +136 -11
- 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 +52 -2
- package/docs/hive//350/234/202/345/267/242M6_ingest/345/256/236/346/226/275/350/256/241/345/210/222_v0.1.md +1 -1
- package/docs/mdcg/README/350/257/246/347/273/206/347/211/210_v0.4.10.md +100 -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 +40 -40
- package/docs/mdcg//345/217/221/345/270/203/351/227/250/347/246/201/351/223/276_v0.1.md +57 -9
- 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 +7 -2
- package/docs/mdcg//347/235/241/347/234/240/345/221/250/346/234/237_/350/277/220/347/273/264/345/211/215/346/217/220/344/270/216/347/273/264/346/212/244/346/214/207/345/215/227_v1.0.md +183 -0
- 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 +39 -3
- package/docs/plans//345/205/250/344/270/255/346/226/207/347/274/226/347/240/201/344/270/216/350/234/202/345/267/242/344/273/273/345/212/241/346/240/207/350/257/206/345/245/221/347/272/246_v2.0.md +220 -0
- package/docs/plans//347/234/237/346/272/220/347/264/242/345/274/225_/351/200/232/347/224/250/346/234/272/345/210/266_v0.3.md +227 -0
- package/docs/plans//347/235/241/347/234/240/344/270/216/350/207/252/350/277/255/344/273/243_/345/212/237/350/203/275/344/274/230/345/214/226/350/256/276/350/256/241_v0.4.md +547 -0
- package/docs/plans//350/234/202/345/267/242/346/250/241/345/236/213/345/257/206/351/222/245/351/205/215/347/275/256/351/235/242_v1.0.md +385 -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 +460 -460
- package/lib/bridge.js +24 -2
- package/lib/hooks.d.ts +31 -2
- package/lib/hooks.js +219 -16
- package/lib/init.js +16 -1
- package/lib/lib/prompt_safety.d.ts +52 -1
- package/lib/lib/prompt_safety.js +76 -1
- package/md_cg/audit.py +652 -379
- package/md_cg/bench6_arms.py +1 -1
- package/md_cg/bench_e2e_locomo_qa.py +11 -4
- package/md_cg/bench_p0.py +1 -1
- package/md_cg/branches.py +2 -2
- package/md_cg/ccgc.py +1071 -1006
- package/md_cg/chain.py +48 -1
- package/md_cg/consistency.py +97 -9
- package/md_cg/consolidate.py +34 -9
- package/md_cg/crypto.py +59 -26
- package/md_cg/docindex.py +16 -1
- package/md_cg/evolution.py +17 -1
- package/md_cg/export.py +1 -1
- package/md_cg/forgetting.py +309 -13
- package/md_cg/freshness.py +511 -0
- package/md_cg/fsutil.py +454 -8
- package/md_cg/generation.py +409 -0
- package/md_cg/hotcache.py +4 -1
- package/md_cg/identity.py +3 -3
- package/md_cg/insight.py +24 -3
- package/md_cg/linkref.py +1 -1
- package/md_cg/logref.py +327 -0
- package/md_cg/mcp_server.py +4261 -3818
- package/md_cg/mdcg.py +4695 -3547
- package/md_cg/mdcos.py +4672 -4159
- package/md_cg/mreview/pipeline.py +15 -1
- package/md_cg/nodefile.py +713 -576
- package/md_cg/protect.py +158 -11
- package/md_cg/protocol.py +41 -2
- package/md_cg/provenance.py +22 -4
- package/md_cg/reach.py +4 -4
- package/md_cg/readcache.py +76 -15
- package/md_cg/reconcile.py +1 -1
- package/md_cg/refindex.py +246 -37
- package/md_cg/refine.py +1 -1
- package/md_cg/routing.py +32 -5
- package/md_cg/run_tests.py +17 -0
- package/md_cg/scrub.py +30 -3
- package/md_cg/security.py +47 -1
- package/md_cg/self_state.py +5 -5
- package/md_cg/semantic/canonical.py +22 -0
- package/md_cg/semantic/unify.py +73 -22
- package/md_cg/semantic/unify_fixture.json +25 -0
- package/md_cg/sleep.py +1297 -0
- package/md_cg/sources.py +23 -6
- package/md_cg/srcindex.py +352 -0
- package/md_cg/stg.py +47 -3
- package/md_cg/subgraph.py +2 -2
- package/md_cg/sustain.py +1436 -1168
- package/md_cg/tasks.py +469 -470
- package/md_cg/test_auto_defaults.py +424 -0
- package/md_cg/test_b1_auto_id_multiproc.py +277 -0
- package/md_cg/test_b1b2_write_face.py +417 -0
- package/md_cg/test_b2_sensitivity_landing.py +223 -0
- package/md_cg/test_b3_merge_keeps_content.py +576 -0
- package/md_cg/test_b4_shard_dir_selfheal.py +843 -0
- package/md_cg/test_b4_shard_dir_selfheal_guard.py +238 -0
- package/md_cg/test_boundary_hit.py +410 -0
- package/md_cg/test_c3_transient_read_negative.py +793 -0
- package/md_cg/test_c8_search_rrf_gates.py +502 -0
- package/md_cg/test_ccg_form_parity.py +188 -0
- package/md_cg/test_ccgc.py +28 -3
- package/md_cg/test_en_pipeline.py +22 -13
- package/md_cg/test_generation_guard.py +352 -0
- package/md_cg/test_govern_directread.py +17 -4
- package/md_cg/test_h2_session_view_norm.py +233 -0
- package/md_cg/test_h4_sustain_snapshot.py +1387 -0
- package/md_cg/test_hive_ingest.py +285 -285
- package/md_cg/test_issue39_utf8_stdio.py +307 -16
- package/md_cg/test_issue43_default_policy.py +865 -0
- package/md_cg/test_legacy_p3_node_id_type.py +375 -0
- package/md_cg/test_linkref.py +10 -3
- package/md_cg/test_lock.py +2 -2
- package/md_cg/test_logref.py +1109 -0
- package/md_cg/test_m3_h9_bucket_health_protect_mark.py +301 -0
- package/md_cg/test_m3_h9_semantic_guard.py +525 -0
- package/md_cg/test_mr_m2.py +15 -3
- package/md_cg/test_n130_verify_falsified_protect.py +1 -1
- package/md_cg/test_n139_dek_provision_failclosed.py +185 -0
- package/md_cg/test_n176_link_trust.py +221 -0
- package/md_cg/test_n178_units_jobid_gate.py +212 -0
- package/md_cg/test_n184_keys_concurrent_provision.py +307 -0
- package/md_cg/test_n195_writepath_reload.py +283 -0
- package/md_cg/test_n196_stale_gate_skip.py +169 -0
- package/md_cg/test_n197_n208_write_face_gates.py +471 -0
- package/md_cg/test_n198_tokens_corrupt_failclosed.py +225 -0
- package/md_cg/test_n199_tokens_concurrent_write.py +378 -0
- package/md_cg/test_n201_proposal_visibility.py +382 -0
- package/md_cg/test_n202_session_notes_visibility.py +461 -0
- package/md_cg/test_n204_n205_n226_n227_n228_n229_exit_gates.py +542 -0
- package/md_cg/test_n206_stdio_jsonrpc_type.py +403 -0
- package/md_cg/test_n209_verify_write_face_gates.py +461 -0
- package/md_cg/test_n212_n213_n224_generation_gates.py +519 -0
- package/md_cg/test_n214_n215_n221_n222_write_face_gates.py +563 -0
- package/md_cg/test_n225_nonobject_load.py +1110 -0
- package/md_cg/test_n230_dirty_replay.py +375 -0
- package/md_cg/test_n62_tenant_bind_failclosed.py +200 -0
- package/md_cg/test_neg_condition_hits.py +333 -0
- package/md_cg/test_neg_tail_honesty.py +663 -0
- package/md_cg/test_none_id_write_guard.py +4 -3
- package/md_cg/test_opt_batch1_md_cg.py +451 -0
- package/md_cg/test_p1.py +5 -5
- package/md_cg/test_p11_consistency.py +161 -12
- package/md_cg/test_p26_refindex.py +2 -2
- package/md_cg/test_p27_docindex.py +777 -774
- package/md_cg/test_p28_refcheck.py +740 -13
- package/md_cg/test_p29_session_ingest_export.py +2 -2
- package/md_cg/test_p2_mcp.py +12 -1
- package/md_cg/test_p2_six_elements.py +463 -0
- package/md_cg/test_p30_maintain.py +33 -2
- package/md_cg/test_p31_insight.py +1 -0
- package/md_cg/test_p3_legacy_closure.py +433 -0
- package/md_cg/test_p4_freshness.py +680 -0
- package/md_cg/test_p8_subgraph_chain.py +14 -1
- package/md_cg/test_p9c_dedup_hints.py +276 -0
- package/md_cg/test_policy_required_ccg.py +426 -0
- package/md_cg/test_protocol.py +22 -0
- package/md_cg/test_rank_parity_score_mode.py +522 -0
- package/md_cg/test_read_face_input_gates.py +363 -0
- package/md_cg/test_read_face_semantics.py +489 -0
- package/md_cg/test_recall_face_guards.py +812 -0
- package/md_cg/test_rejected_credential_forms.py +188 -0
- package/md_cg/test_rejected_redact.py +146 -0
- package/md_cg/test_retr_s1b.py +2 -2
- package/md_cg/test_retr_s5.py +20 -7
- package/md_cg/test_review_conformance.py +21 -3
- package/md_cg/test_security_audit_v21.py +2 -2
- package/md_cg/test_semantic_canonical.py +5 -3
- package/md_cg/test_server_version.py +67 -0
- package/md_cg/test_sleep.py +611 -0
- package/md_cg/test_sleep_p1.py +784 -0
- package/md_cg/test_srcindex.py +171 -0
- package/md_cg/test_tenant_env_override_warn.py +63 -50
- package/md_cg/test_time_core_lint.py +968 -0
- package/md_cg/test_token_lowercase_form.py +315 -0
- package/md_cg/test_unify_default_off.py +701 -0
- package/md_cg/test_unify_scope.py +182 -0
- package/md_cg/test_v21r1_package_version.py +310 -0
- package/md_cg/test_writepipe.py +10 -4
- package/md_cg/tokens.py +225 -95
- package/md_cg/tool_face.py +4 -4
- package/md_cg/trust.py +49 -5
- package/md_cg/units.py +204 -6
- package/md_cg/weights.py +4 -4
- package/md_cg/whitebox_kb/aeis_core/time_core.py +8 -0
- package/md_cg/whitebox_kb/wisdom/knowledge_points.py +1 -1
- package/md_cg/writelimit.py +42 -8
- package/md_cg/writepipe.py +651 -554
- package/package.json +4 -2
- package/skills/plugin.json +1 -1
- package/src/bridge.ts +25 -2
- package/src/hooks.ts +229 -16
- package/src/init.ts +15 -1
- package/src/lib/prompt_safety.ts +88 -1
- package/utf8_boot.py +237 -0
- package/zcode/AGENTS.md +10 -10
package/md_cg/scrub.py
CHANGED
|
@@ -715,8 +715,8 @@ def decontaminate(cg, node_ids=None, *, kinds=None, dry_run: bool = True,
|
|
|
715
715
|
# ④ 校准偏差
|
|
716
716
|
# --------------------------------------------------------------------------
|
|
717
717
|
|
|
718
|
-
# 生效条件:对 cg 中每个「layer 不在 SELF_LAYERS」且 evidence_count≥int(min_evidence)(min_evidence=0 时该比较恒假而不早退)的节点,若 protect.is_protected 为假或 override 为真,且 cg.get(nid)
|
|
719
|
-
def _apply_offset(cg, offset, *, override=False, min_evidence=1):
|
|
718
|
+
# 生效条件:对 cg 中每个「layer 不在 SELF_LAYERS」且 evidence_count≥int(min_evidence)(min_evidence=0 时该比较恒假而不早退)的节点,若 protect.is_protected 为假或 override 为真,且 cg.get(nid) 未抛异常并返回真值节点,则先以节点真层/敏感度过 protect.guard_overwrite(层闸 + 保护闸,override 为真时先快照 + 审计,越权抛 AccessDenied 且整批中止),再取 fm.get("confidence", 0.6)(缺键才回落 0.6,键存在为 None/假值不回落)为 old,算出 round(max(0.0, min(0.99, old+float(offset))),4),与 old 差<1e-9 时跳过,否则写 fm["confidence"] 与 fm["calibration"] 并调用 cg._write_node,成功后置 cg._dirty[nid]=e(**标脏**:推进读缓存代际与索引增量日志)并 adjusted+1(写回异常被吞掉不计数),返回 (adjusted, skipped),其中 skipped 只累计「layer 属 SELF_LAYERS」或被 protect 拦下且非 override 的节点。
|
|
719
|
+
def _apply_offset(cg, offset, *, override=False, min_evidence=1, actor=None):
|
|
720
720
|
nodes = _nodes(cg)
|
|
721
721
|
adjusted = skipped = 0
|
|
722
722
|
for nid, e in nodes.items():
|
|
@@ -736,6 +736,20 @@ def _apply_offset(cg, offset, *, override=False, min_evidence=1):
|
|
|
736
736
|
if not node:
|
|
737
737
|
continue
|
|
738
738
|
fm = node.get("frontmatter") or {}
|
|
739
|
+
# N222(2026-09-28,同族未接线写点):本函数是 calibrate 落库动作,此前
|
|
740
|
+
# 直调 `cg._write_node` 改写**全库** `layer∉(self,anchor)` 且
|
|
741
|
+
# `evidence_count≥1` 节点的 confidence/calibration——不过 principal 层写
|
|
742
|
+
# 白名单、不过引擎级保护闸、`override` 形参直通(含 importance≥0.7 的
|
|
743
|
+
# 不可遗忘节点),无快照无审计。持 sustain 令牌(`tokens.py:190`
|
|
744
|
+
# `layers_allow=('self',)`)即可改 knowledge 层节点置信度(实测
|
|
745
|
+
# `n_adjusted=5`,0.8→0.95;`override=True` 再 +1)。落盘前统一过
|
|
746
|
+
# `protect.guard_overwrite`:层/敏感度取**节点 fm 真值**(不信索引缓存),
|
|
747
|
+
# 解析与层闸由 `protect.require_layer` 单点承担,越权即 AccessDenied
|
|
748
|
+
# 整批中止(fail-closed,不静默跳过——否则越权面被伪装成「无可校准节点」);
|
|
749
|
+
# `override=True` 走同一闸的显式放行分支(快照 + `_protected_audit.jsonl`)。
|
|
750
|
+
protect.guard_overwrite(cg, nid, layer=fm.get("layer"),
|
|
751
|
+
sensitivity=fm.get("sensitivity"),
|
|
752
|
+
override=override, actor=actor)
|
|
739
753
|
old = float(fm.get("confidence", 0.6))
|
|
740
754
|
new = round(max(0.0, min(0.99, old + float(offset))), 4)
|
|
741
755
|
if abs(new - old) < 1e-9:
|
|
@@ -746,6 +760,18 @@ def _apply_offset(cg, offset, *, override=False, min_evidence=1):
|
|
|
746
760
|
try:
|
|
747
761
|
cg._write_node(nid, os.path.join(cg.root, e["path"]), fm,
|
|
748
762
|
node.get("content") or "")
|
|
763
|
+
# 标脏(N133 修复,对照先例 md_cg/mdcg.py 的 update_tags / verify
|
|
764
|
+
# 直写分支 `self._dirty[node_id] = e`):本函数原先写盘后**完全不
|
|
765
|
+
# 碰索引**(条目字段集不含 confidence,无需同步),但正因如此
|
|
766
|
+
# path_gen 不推进 ⇒ 读缓存(默认开)把写盘前的旧 fm 判新鲜——
|
|
767
|
+
# 同进程「calibrate 后读」拿旧 confidence(其余两处同族写点
|
|
768
|
+
# forgetting.reinforce / insight.verify 同批补标脏)。标脏同时
|
|
769
|
+
# 让本写进 `_dirty → flush → _index_log` 重放。**不下沉进
|
|
770
|
+
# `_write_node`**:该口另有「只对账索引不落盘」的调用方,下沉会
|
|
771
|
+
# 凭空产生写入代际与自重载。
|
|
772
|
+
_dirty = getattr(cg, "_dirty", None)
|
|
773
|
+
if isinstance(_dirty, dict):
|
|
774
|
+
_dirty[nid] = e
|
|
749
775
|
adjusted += 1
|
|
750
776
|
except Exception: # noqa: BLE001
|
|
751
777
|
pass
|
|
@@ -777,7 +803,8 @@ def calibrate(cg, *, apply: bool = False, override: bool = False, actor=None,
|
|
|
777
803
|
adjusted = skipped = 0
|
|
778
804
|
if apply and abs(offset) > 1e-9:
|
|
779
805
|
adjusted, skipped = _apply_offset(cg, offset, override=override,
|
|
780
|
-
min_evidence=min_evidence
|
|
806
|
+
min_evidence=min_evidence,
|
|
807
|
+
actor=actor)
|
|
781
808
|
_log(cg, "calibrate", ok=True, offset=offset, gap=gap,
|
|
782
809
|
adjusted=adjusted, actor=actor)
|
|
783
810
|
blind = 0
|
package/md_cg/security.py
CHANGED
|
@@ -382,4 +382,50 @@ def can_read_restricted(p) -> bool:
|
|
|
382
382
|
return False
|
|
383
383
|
if getattr(p, "can_admin", False):
|
|
384
384
|
return True
|
|
385
|
-
return getattr(p, "role", "") in _RESTRICTED_READER_ROLES
|
|
385
|
+
return getattr(p, "role", "") in _RESTRICTED_READER_ROLES
|
|
386
|
+
|
|
387
|
+
|
|
388
|
+
# --------------------------------------------------------------------------
|
|
389
|
+
# 读可见性可选钩子(跨层唯一实现,批次 68)
|
|
390
|
+
# --------------------------------------------------------------------------
|
|
391
|
+
# 为什么放在本模块:可见性单点 `_readable` 定义在**子类** `MdCGSecure`
|
|
392
|
+
# (mdcos.py:4028),而需要它的读数出口散在**下层/旁路模块**——trust(验证态
|
|
393
|
+
# 台账/describe)、provenance(派生边端点摘要与拓扑)、evolution(演化账本
|
|
394
|
+
# 自由文本)、forgetting(遗忘留痕 jsonl)。这些模块若 `from .mdcos import` 会
|
|
395
|
+
# 成环(mdcos 反向导入它们),故单点放在两侧都依赖的 security.py:
|
|
396
|
+
# · `visible_to(cg, entry)` —— 条目级判定(口径 = `_readable`);
|
|
397
|
+
# · `node_visible(cg, node_id)` —— 节点 id 级判定(索引取条目 + 缺席处置)。
|
|
398
|
+
# 口径与仓内既有三处可选钩子逐条一致:`mdcos._note_visible`(及本函数的
|
|
399
|
+
# 委派)、`backfill._readable_guard`、`linkref._known_ids`、`stg._scan/_preview`
|
|
400
|
+
# ——「无钩子(纯 MdCGOS,无身份/密级模型)= 不限制;有钩子 = 按判据;
|
|
401
|
+
# 判据异常 = 不可见(fail-closed);宁可少读,不可 fail-open 泄漏」。
|
|
402
|
+
# 生效条件:cg 有可调用的 _readable 时返回 bool(判定结果),判定抛异常返回 False;无该属性/不可调用(纯 MdCGOS)时返回 True。
|
|
403
|
+
def visible_to(cg, entry) -> bool:
|
|
404
|
+
"""条目级读可见性(cg 无 `_readable` 钩子时不设限)。"""
|
|
405
|
+
fn = getattr(cg, "_readable", None)
|
|
406
|
+
if not callable(fn):
|
|
407
|
+
return True
|
|
408
|
+
try:
|
|
409
|
+
return bool(fn(entry))
|
|
410
|
+
except Exception: # noqa: BLE001 —— 判据异常=不可见
|
|
411
|
+
return False
|
|
412
|
+
|
|
413
|
+
|
|
414
|
+
# 生效条件:cg 无可调用 _readable 时返回 True;有钩子且索引条目为 dict 时返回 bool(visible_to(cg, 条目))(判定异常 False);索引无该条目/条目非 dict 时返回 bool(principal.can_admin)(有身份模型但不可证可见→fail-closed,设计者不受限,否则删除后的治理留痕反被清空)。
|
|
415
|
+
def node_visible(cg, node_id) -> bool:
|
|
416
|
+
"""节点 id 级读可见性(索引取条目;**缺席 = 不可证可见 → fail-closed**)。
|
|
417
|
+
|
|
418
|
+
缺席的两种实情:① 节点从未存在/拼错 id;② 节点已被 forget(索引无条目)。
|
|
419
|
+
有身份模型时两者都不得被「当作可见」——否则 `_forgetting.jsonl` /
|
|
420
|
+
`trust` 台账 / 派生边摘要就成了「已删内容」的旁路读出(N205/N226/N229)。
|
|
421
|
+
例外只给 can_admin(设计者=全局观测与治理本职):否则删除后的裁决留痕
|
|
422
|
+
对唯一需要它的人也不可见(与 `_readable` 的 can_admin 豁免同款)。
|
|
423
|
+
"""
|
|
424
|
+
fn = getattr(cg, "_readable", None)
|
|
425
|
+
if not callable(fn):
|
|
426
|
+
return True # 无身份模型:无「越权」可言
|
|
427
|
+
e = ((getattr(cg, "index", None) or {}).get("nodes") or {}).get(node_id)
|
|
428
|
+
if isinstance(e, dict):
|
|
429
|
+
return visible_to(cg, e)
|
|
430
|
+
p = getattr(cg, "principal", None)
|
|
431
|
+
return bool(p is not None and getattr(p, "can_admin", False))
|
package/md_cg/self_state.py
CHANGED
|
@@ -355,7 +355,7 @@ def _relation_counts(cg, subject):
|
|
|
355
355
|
slug = _slug(subject)
|
|
356
356
|
nodes = ((getattr(cg, "index", None) or {}).get("nodes") or {})
|
|
357
357
|
out = inn = 0
|
|
358
|
-
for nid, e in nodes.items():
|
|
358
|
+
for nid, e in list(nodes.items()):
|
|
359
359
|
tags = set(e.get("tags") or [])
|
|
360
360
|
if TAG_RELATION not in tags and not nid.startswith(RELATION_PREFIX):
|
|
361
361
|
continue
|
|
@@ -656,7 +656,7 @@ def relations(cg, subject=None, direction="both"):
|
|
|
656
656
|
nodes = ((getattr(cg, "index", None) or {}).get("nodes") or {})
|
|
657
657
|
slug = _slug(subject) if subject else None
|
|
658
658
|
out = []
|
|
659
|
-
for nid, e in nodes.items():
|
|
659
|
+
for nid, e in list(nodes.items()):
|
|
660
660
|
tags = set(e.get("tags") or [])
|
|
661
661
|
if TAG_RELATION not in tags and not nid.startswith(RELATION_PREFIX):
|
|
662
662
|
continue
|
|
@@ -692,7 +692,7 @@ def index(cg, dim, value, limit=50, with_content=False):
|
|
|
692
692
|
"allowed": list(DIMENSIONS)}
|
|
693
693
|
tag = dim_tag(d, value)
|
|
694
694
|
nodes = ((getattr(cg, "index", None) or {}).get("nodes") or {})
|
|
695
|
-
hits = [(nid, e) for nid, e in nodes.items()
|
|
695
|
+
hits = [(nid, e) for nid, e in list(nodes.items())
|
|
696
696
|
if tag in (e.get("tags") or [])]
|
|
697
697
|
hits.sort(key=lambda kv: (-float(kv[1].get("importance") or 0),
|
|
698
698
|
-float(kv[1].get("created_at") or 0),
|
|
@@ -740,7 +740,7 @@ def audit(cg, subject=DEFAULT_SUBJECT, window=RECENT_WINDOW):
|
|
|
740
740
|
|
|
741
741
|
# 1 单例:同 subject 的状态卡只能有一张
|
|
742
742
|
slug = _slug(subject)
|
|
743
|
-
cards = [n for n, e in nodes.items()
|
|
743
|
+
cards = [n for n, e in list(nodes.items())
|
|
744
744
|
if TAG_STATE in (e.get("tags") or [])
|
|
745
745
|
and f"state_subject:{slug}" in (e.get("tags") or [])]
|
|
746
746
|
if len(cards) > 1:
|
|
@@ -878,7 +878,7 @@ def audit(cg, subject=DEFAULT_SUBJECT, window=RECENT_WINDOW):
|
|
|
878
878
|
continue
|
|
879
879
|
for v in (vals or []):
|
|
880
880
|
tag = dim_tag(d, v)
|
|
881
|
-
holders = [n for n, e in nodes.items()
|
|
881
|
+
holders = [n for n, e in list(nodes.items())
|
|
882
882
|
if tag in (e.get("tags") or [])]
|
|
883
883
|
if not holders:
|
|
884
884
|
issues.append(_issue("dimension_orphan", "warn",
|
|
@@ -31,6 +31,28 @@ from . import zh_en_atoms
|
|
|
31
31
|
WIN = 6 # 组合共现窗口(token 距离,探针实证口径)
|
|
32
32
|
_ATOMS_ZH = None
|
|
33
33
|
|
|
34
|
+
# 中文段判据(单点,2026-09-30 检索归一层作用域收窄时立):
|
|
35
|
+
# 中文 = CJK 统一表意文字基本区 U+4E00–U+9FFF。与 en_normalizer 分词字符类
|
|
36
|
+
# `[A-Za-z\u4e00-\u9fff]` 的中文区间同区间;rust/src/text.rs::is_zh 是同判据的
|
|
37
|
+
# Rust 侧单点(两侧归一层共用同一判据;改动须两侧同步,否则 rank_parity 漂移)。
|
|
38
|
+
# 消费方:unify.py::unify_query(中文段逐字保留)、rust/src/atoms.rs::Atoms::unify。
|
|
39
|
+
ZH_LO, ZH_HI = "\u4e00", "\u9fff"
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
# 生效条件:单参 ch 为长度恰为 1 的 str 时返回 ZH_LO <= ch <= ZH_HI(CJK 基本区);长度 != 1(空串/多字符串)一律返回 False(2026-09-30 清理批次把边界收紧为「长度恰为 1」——此前只在 ch 为空串时为假,多字符串按字典序比较可能被误判为真)。
|
|
43
|
+
def is_zh_char(ch):
|
|
44
|
+
"""中文段判据(单点):CJK 统一表意文字基本区 U+4E00–U+9FFF。
|
|
45
|
+
|
|
46
|
+
边界约定(2026-09-30 清理批次乙2):**仅长度恰为 1 的串可为真**——
|
|
47
|
+
空串与长度 >1 的串一律 False。此前只有空串为假(链式比较
|
|
48
|
+
`ZH_LO <= ch` 对空串即假),而多字符串按字典序比较:`is_zh_char("中文")`
|
|
49
|
+
判真(首字符在区间即真)=判据外溢。两侧调用点清点结果:本函数只有
|
|
50
|
+
`unify.py::_runs` 与 `test_unify_scope.py::zh_runs` 两处消费,均逐字符
|
|
51
|
+
传入 ⇒ 本收紧对存量行为零变更;Rust 侧 `text.rs::is_zh` 收 `char`,
|
|
52
|
+
天然无空/多字符二态,边界语义两侧一致(docstring 已互指)。
|
|
53
|
+
"""
|
|
54
|
+
return len(ch) == 1 and ZH_LO <= ch <= ZH_HI
|
|
55
|
+
|
|
34
56
|
|
|
35
57
|
# 生效条件:模块级常量 `_ATOMS_ZH` 为 None 时按 `__file__` 所在目录的 atoms.json 取 `data.get("atoms", [])` 各项 `zh` 建集合并缓存,非 None 时直接返回 `_ATOMS_ZH`。
|
|
36
58
|
def atoms_zh():
|
package/md_cg/semantic/unify.py
CHANGED
|
@@ -1,45 +1,96 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
2
|
# -*- coding: utf-8 -*-
|
|
3
|
-
"""灵枢 · 检索 query 统一归一层(批次 15
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
-
|
|
8
|
-
segment 展开为标准原子(「牛肉」→「牛 肉」,与 doc 侧
|
|
9
|
-
原子形态同构);
|
|
10
|
-
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
3
|
+
"""灵枢 · 检索 query 统一归一层(批次 15 立;2026-09-30 使用者裁定收窄作用域)
|
|
4
|
+
|
|
5
|
+
口径:**只对英文内容做翻译归一,中文内容原样不动**。
|
|
6
|
+
query 先按「中文段 / 非中文段」切开,只有**非中文段里的英文**进归一链路:
|
|
7
|
+
- 非中文段(含 ASCII 字母):en_normalizer(时态还原→停用词剔除→en→zh 语素
|
|
8
|
+
映射)→ segment 展开为标准原子(「牛肉」→「牛 肉」,与 doc 侧
|
|
9
|
+
fm.semantic 原子形态同构);
|
|
10
|
+
- 中文段(CJK 统一表意文字基本区 U+4E00–U+9FFF,判据单点 =
|
|
11
|
+
`canonical.is_zh_char`):**逐字保留、绝不切分、绝不送 segment**;
|
|
12
|
+
- 非英文段(数字/标点等,段内无 ASCII 字母):原样保留(不是英文即不翻译);
|
|
13
|
+
- 两段按原顺序以单空格 join,空白(段内/段间)一律折叠为单空格;
|
|
14
14
|
- 专名/未登录词原样保留(词表边界 unknown_keep,非错误)。
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
16
|
+
为什么收窄(2026-09-30 定因,实测于 locomo-zh-500 公开题池 500 题 / 567 池、
|
|
17
|
+
eval_common jaccard + GLOBAL_CAP 解除、k=5):旧口径「query 含任一 ASCII 字母
|
|
18
|
+
即整条归一」会把**中夹英** query 的中文部分逐字切开(「自我接纳」→
|
|
19
|
+
「自 我 接 纳」、「慈善跑」→「慈 善 跑」);500 题里含 ASCII 字母的 145 条
|
|
20
|
+
**全部**也含中文(无纯英文 query)。单点实测:整条归一 lexical hit@1 = 94.4% /
|
|
21
|
+
lexical,fuzzy = 87.2%;只译英文片段 lexical = 95.8% / lexical,fuzzy = 96.6%;
|
|
22
|
+
归一层全关 lexical = 96.4% / lexical,fuzzy = 97.2%。
|
|
23
|
+
|
|
24
|
+
开关:MDCG_UNIFY_QUERY **默认关**(2026-09-30 使用者裁定,缺省由 "1" 翻为 "0")。
|
|
25
|
+
定因:本层本职是让**英文** query 命中中文节点,对中文检索池是**纯开销**——
|
|
26
|
+
locomo-zh-500 公开题池(池 567 / 题 500)缺省关态 lexical hit@1 96.4% /
|
|
27
|
+
lexical+fuzzy 97.2%,开态 95.8% / 96.6%(各跑两遍逐位相同)。
|
|
28
|
+
|
|
29
|
+
三态判据(两侧同语义,唯一开态 = 显式 "1"):
|
|
30
|
+
|
|
31
|
+
MDCG_UNIFY_QUERY Python unify_on() Rust Atoms::from_env()
|
|
32
|
+
未设 False None(不载入)
|
|
33
|
+
"0" False None(不载入)
|
|
34
|
+
"1" True Some(Atoms)(载入)
|
|
35
|
+
|
|
36
|
+
显式 "1" 仍能开回(英文对照路要用);两侧同源 en_zh_map.json
|
|
37
|
+
(scripts/rank_parity.py --dataset 对拍在两侧同开关态进行)。
|
|
20
38
|
|
|
21
39
|
归一失败(semantic 模块缺失等)静默原样返回——与 en_zh_terms 同降级
|
|
22
40
|
风格,不阻断检索主链路。
|
|
41
|
+
|
|
42
|
+
两侧同构:rust/src/atoms.rs::Atoms::unify 同语义、同中文判据;逐位对拍 fixture
|
|
43
|
+
= `md_cg/semantic/unify_fixture.json`(本侧守卫 md_cg/test_unify_scope.py 与
|
|
44
|
+
Rust 单测 unify_mixed_fixture_matches_python 钉同一份期望值)。
|
|
23
45
|
"""
|
|
24
46
|
import os
|
|
25
47
|
import re
|
|
26
48
|
|
|
27
|
-
|
|
49
|
+
from .canonical import is_zh_char
|
|
50
|
+
|
|
51
|
+
# 生效条件:无 required 形参,锚定环境变量名 MDCG_UNIFY_QUERY;**未设**时取缺省 "0"(2026-09-30 使用者裁定:缺省关),当 os.environ.get("MDCG_UNIFY_QUERY", "0") == "1" 时返回 True,其余(未设 / "0" / 任何非 "1" 取值)返回 False。
|
|
28
52
|
def unify_on() -> bool:
|
|
29
|
-
"""
|
|
30
|
-
return os.environ.get("MDCG_UNIFY_QUERY", "
|
|
53
|
+
"""统一归一层开关(**默认关**;显式 =1 才开,=0/未设一律关)。"""
|
|
54
|
+
return os.environ.get("MDCG_UNIFY_QUERY", "0") == "1"
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
# 生效条件:单参 text 为任意字符串(空串返回空列表),按 canonical.is_zh_char 逐字符判中文段,相邻同判据字符归并为一个连续段,返回 [(是否中文段, 段文本), ...] 的保序列表。
|
|
58
|
+
def _runs(text):
|
|
59
|
+
"""按中文判据切连续段:[(True, "中文段"), (False, "非中文段"), ...]。"""
|
|
60
|
+
out = []
|
|
61
|
+
cur = None
|
|
62
|
+
buf = []
|
|
63
|
+
for ch in text:
|
|
64
|
+
k = is_zh_char(ch)
|
|
65
|
+
if buf and k != cur:
|
|
66
|
+
out.append((cur, "".join(buf)))
|
|
67
|
+
buf = []
|
|
68
|
+
cur = k
|
|
69
|
+
buf.append(ch)
|
|
70
|
+
if buf:
|
|
71
|
+
out.append((cur, "".join(buf)))
|
|
72
|
+
return out
|
|
31
73
|
|
|
32
74
|
|
|
33
|
-
# 生效条件:text 为 None
|
|
75
|
+
# 生效条件:text 为 None/空串、或开关 MDCG_UNIFY_QUERY 取值不为 "1"(含**未设**——缺省 "0",2026-09-30 起关)、或 strip 后不含 [A-Za-z] 时,原样返回 text(未 strip 的入参);否则按中文段(canonical.is_zh_char)切分——中文段与不含 ASCII 字母的非中文段逐字保留、含 ASCII 字母的非中文段经 canonical.query_atoms 归一——各段折空白后按原顺序单空格 join;归一产物为空(trim 后)或抛异常时原样返回 text。
|
|
34
76
|
def unify_query(text):
|
|
35
|
-
"""
|
|
77
|
+
"""检索入口统一归一:只译英文内容,中文段逐字原样。"""
|
|
36
78
|
t = (text or "").strip()
|
|
37
79
|
if not t or not unify_on() or not re.search(r"[A-Za-z]", t):
|
|
38
80
|
return text
|
|
39
81
|
try:
|
|
40
82
|
from .canonical import query_atoms
|
|
41
|
-
|
|
83
|
+
parts = []
|
|
84
|
+
for is_zh, run in _runs(t):
|
|
85
|
+
if is_zh or not re.search(r"[A-Za-z]", run):
|
|
86
|
+
seg = run # 中文段 / 非英文段:逐字保留
|
|
87
|
+
else:
|
|
88
|
+
atoms = query_atoms(run) # 非中文段里的英文:既有链路
|
|
89
|
+
seg = " ".join(a for a in atoms if a)
|
|
90
|
+
seg = " ".join(seg.split()) # 段内空白折叠为单空格
|
|
91
|
+
if seg:
|
|
92
|
+
parts.append(seg)
|
|
42
93
|
except Exception:
|
|
43
94
|
return text
|
|
44
|
-
u = " ".join(
|
|
95
|
+
u = " ".join(parts)
|
|
45
96
|
return u if u else text
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"_note": "统一归一层(unify_query / Atoms::unify)作用域收窄(2026-09-30 使用者裁定:只对英文内容做翻译归一,中文内容原样不动)的逐位对拍 fixture。两侧同源钉死:Python 守卫 md_cg/test_unify_scope.py 与 Rust 单测 rust/src/atoms.rs::tests::unify_mixed_fixture_matches_python 都读本文件(Rust 侧 include_str! 编译期嵌入)。任一侧实现漂移即红。含「空产物回退」例(入参含首尾空白且归一产物为空 → 两侧逐位回退**原入参本身**,2026-09-30 清理批次乙1 钉住;Python 侧语义为 unify_query 尾行 `return text`)。",
|
|
3
|
+
"_caveat": "非中文段一律用「两侧处理同构」的形态:全小写英文词(或命中 en_zh_map 的形)。两侧**既有非对称**共三条(非 2026-09-30 收窄引入,亦非本清理批次引入——HEAD 对照实测同分歧),故不入本 fixture:①未登录大写英文词——Python 保留原 case(`领养 LGBTQ 群体`→原样、`AI 记忆 系统`→原样),Rust 经 normalize_en 一律 lower(→`领养 lgbtq 群体`/`ai 记忆 系统`);②命中词表的专名——Python 双原子保留原词+译词(`Paris 报告`→`Paris 巴 黎 报告`),Rust 只出译词(→`巴 黎 报告`);③字母与数字同段——Python 丢数字(`beef 2024 报告`→`牛肉 报告`),Rust 保数字(→`牛肉 2024 报告`)。**真源 = `docs/hive/检索算法口径对照_v0.1.md` 正文「两侧既有非对称」表**(含两侧原始读数、fixture 为何避开、以及「统一到哪一侧」的待裁定项);本文件只钉两侧同构形态。",
|
|
4
|
+
"cases": [
|
|
5
|
+
{"name": "纯英·跨语归一冒烟", "in": "I eat beef yesterday", "out": "我 吃 牛肉 昨天"},
|
|
6
|
+
{"name": "纯英·单动词时态还原", "in": "wrote", "out": "写"},
|
|
7
|
+
{"name": "纯英·多词", "in": "compiler tests", "out": "编 辑 家 测 试"},
|
|
8
|
+
{"name": "纯中·复合词不切", "in": "自我接纳", "out": "自我接纳"},
|
|
9
|
+
{"name": "纯中·旧口径会切开的两字三字词", "in": "慈善跑", "out": "慈善跑"},
|
|
10
|
+
{"name": "纯中·带空格", "in": "蜂群调度 依赖门禁", "out": "蜂群调度 依赖门禁"},
|
|
11
|
+
{"name": "中夹英·英文在首", "in": "beef 报告", "out": "牛肉 报告"},
|
|
12
|
+
{"name": "中夹英·未登录英文片段+中文段逐字保留", "in": "领养 xyzzy 群体", "out": "领养 xyzzy 群体"},
|
|
13
|
+
{"name": "英夹中·英文译中文段原样", "in": "beef 牛肉", "out": "牛肉 牛肉"},
|
|
14
|
+
{"name": "前后空白·折叠", "in": " beef 报告 ", "out": "牛肉 报告"},
|
|
15
|
+
{"name": "空产物回退·原样返回原入参(不 trim)", "in": " the is ", "out": " the is "},
|
|
16
|
+
{"name": "多空格·折叠为单空格", "in": "beef 报告", "out": "牛肉 报告"},
|
|
17
|
+
{"name": "数字·非英文段原样", "in": "2024 报告", "out": "2024 报告"},
|
|
18
|
+
{"name": "数字+中文+英文混合", "in": "2024 年 beef 报告", "out": "2024 年 牛肉 报告"},
|
|
19
|
+
{"name": "半角标点·非英文段原样", "in": "beef 阅读,写作", "out": "牛肉 阅读 , 写作"},
|
|
20
|
+
{"name": "全角标点·非英文段原样", "in": "报告。很好 beef", "out": "报告 。 很好 牛肉"},
|
|
21
|
+
{"name": "未登录词·unknown_keep", "in": "xyzzy 报告", "out": "xyzzy 报告"},
|
|
22
|
+
{"name": "单字母词 I", "in": "I 支持", "out": "我 支持"},
|
|
23
|
+
{"name": "英中粘连·无空格混合", "in": "beef报告", "out": "牛肉 报告"}
|
|
24
|
+
]
|
|
25
|
+
}
|