@furongjun1999/dsh-memory 0.5.1 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +588 -552
- package/codebuddy/CODEBUDDY.md +10 -10
- package/data/policy.json +27 -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/DSH/346/227/245/345/277/227/347/264/242/345/274/225v2_/345/217/202/350/200/203dsh-TUI_v1.0.md +248 -0
- package/docs/eval/DSH/346/227/245/345/277/227/347/264/242/345/274/225/346/225/210/346/236/234/351/252/214/350/257/201_v1.0.md +209 -0
- package/docs/eval/DSH/347/253/257/347/274/272/351/231/267/344/270/223/351/241/271_v1.0.md +254 -0
- 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/P1b2_/350/257/273/351/235/242/344/273/243/351/231/205/344/277/256/345/244/215_v1.0.md +124 -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/217/221/345/270/20306_/344/270/200/351/224/256/351/205/215/347/275/256/344/270/216DSH0172_v1.0.md +171 -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//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_v18.md +183 -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_v19.md +207 -0
- package/docs/eval//347/274/272/351/231/267/346/214/226/346/216/230_/350/207/252/344/270/273/350/277/255/344/273/243_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/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 +20 -0
- 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 +12 -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 +21 -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//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//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/dsh/cordis-patch-profile-web.example.yml +35 -0
- package/lib/bridge.js +24 -2
- package/lib/cli.d.ts +3 -0
- package/lib/cli.js +66 -0
- package/lib/hooks.d.ts +31 -2
- package/lib/hooks.js +219 -16
- package/lib/init.d.ts +88 -0
- package/lib/init.js +302 -0
- 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_p0.py +1 -1
- package/md_cg/branches.py +2 -2
- package/md_cg/ccgc.py +1071 -1006
- package/md_cg/chain.py +1 -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/evidence.py +585 -582
- package/md_cg/evolution.py +17 -1
- package/md_cg/export.py +1 -1
- package/md_cg/forgetting.py +309 -13
- package/md_cg/fsutil.py +454 -8
- 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 +4152 -3818
- package/md_cg/mdcg.py +4208 -3462
- package/md_cg/mdcos.py +4491 -4137
- package/md_cg/mreview/pipeline.py +15 -1
- package/md_cg/nodefile.py +639 -575
- 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/review_cli.py +49 -4
- 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/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 +1299 -1168
- package/md_cg/tasks.py +469 -470
- 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_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_govern_directread.py +17 -4
- package/md_cg/test_h2_session_view_norm.py +233 -0
- package/md_cg/test_h4_sustain_snapshot.py +1377 -0
- package/md_cg/test_hive_ingest.py +285 -285
- package/md_cg/test_index_crossprocess_reload.py +301 -0
- 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 +513 -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_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 +166 -0
- 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_p30_maintain.py +33 -2
- package/md_cg/test_p31_insight.py +1 -0
- 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 +516 -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_cli_attribution.py +177 -0
- package/md_cg/test_review_cli_visibility.py +235 -0
- package/md_cg/test_review_conformance.py +21 -3
- package/md_cg/test_security_audit_v21.py +2 -2
- package/md_cg/test_server_version.py +67 -0
- package/md_cg/test_srcindex.py +171 -0
- package/md_cg/test_tenant_env_override_warn.py +63 -50
- package/md_cg/test_token_lowercase_form.py +315 -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/wisdom/knowledge_points.py +1 -1
- package/md_cg/writelimit.py +42 -8
- package/md_cg/writepipe.py +651 -554
- package/package.json +8 -2
- package/skills/plugin.json +1 -1
- package/src/bridge.ts +25 -2
- package/src/cli.ts +65 -0
- package/src/hooks.ts +229 -16
- package/src/init.ts +361 -0
- package/src/lib/prompt_safety.ts +88 -1
- package/zcode/AGENTS.md +10 -10
package/md_cg/routing.py
CHANGED
|
@@ -269,27 +269,54 @@ def domain_similarity(a: str, b: str) -> float:
|
|
|
269
269
|
return len(ga & gb) / len(ga | gb)
|
|
270
270
|
|
|
271
271
|
|
|
272
|
-
# 生效条件:counts 各值之和为 0(含空 dict 与全 0
|
|
273
|
-
def bucket_health(counts: dict) -> dict:
|
|
272
|
+
# 生效条件:counts 各值之和为 0(含空 dict 与全 0 计数)时——total_nodes 为假值(缺省 None 或 <=0)返回 {'ok': True, 'reason': 'empty', 'buckets': 0}(与加 total_nodes 参数之前逐位一致),total_nodes > 0 返回 {'ok': False, 'reason': 'empty_buckets', 'buckets': 0, 'nodes': total_nodes, 'problems': [...]};否则在「桶数 >1 且最大桶占比 >30%」、「桶数 >1 且单例桶占比 >50%」、「桶数 ==1(单桶,条件路由不可用)」、桶数 >1 时「期望扫描 >30%」中命中的项写入 problems,返回含 ok(=problems 为空)、buckets、nodes、max_bucket_share、singleton_ratio、expected_scan、problems 的 dict。
|
|
273
|
+
def bucket_health(counts: dict, total_nodes: int = None) -> dict:
|
|
274
274
|
"""分区健康度自检。分桶键一旦退化(巨桶或碎片化),条件路由就是纸面收益,
|
|
275
275
|
必须在写入侧就能发现,而不是等召回变差才回头查。
|
|
276
276
|
|
|
277
277
|
counts: {bucket_dir: node_count}
|
|
278
|
+
total_nodes: 调用方(`MdCG.health`)持有的**库内节点总数**,唯一用途是把
|
|
279
|
+
「真空库(无节点、无桶)」与「有节点但分桶表为空(分区信息缺失)」分开。
|
|
280
|
+
**缺省 None ⇒ 不做该比对**:空 counts 的返回与加本参数之前逐位一致,
|
|
281
|
+
故既有调用方(test_p0 / bench_axis_domain / bench6_arms,只传 counts)
|
|
282
|
+
行为一字不变——M3③ 的向后兼容面。
|
|
283
|
+
|
|
284
|
+
单桶(nb == 1)是「从未分区」而不是「分区退化」:巨桶判据(top/n == 1.0)与
|
|
285
|
+
期望扫描判据(同样恒 1.0)在 nb==1 时必触发且互为同一事实,此前把这类库报成
|
|
286
|
+
「巨桶 / 分区失效」是假话(M3①②)。现只给一条如实读数——条件路由本就不可用;
|
|
287
|
+
ok 仍为 False(单桶库的一次查询确实是全量扫描,判据未放宽)。
|
|
278
288
|
"""
|
|
279
289
|
n = sum(counts.values())
|
|
290
|
+
nb = len(counts)
|
|
280
291
|
if not n:
|
|
292
|
+
# 库里有节点却零个桶 ⇒ 分桶表与节点面不一致(分区信息缺失),
|
|
293
|
+
# 不能报 ok=True 冒充健康(M3③)。total_nodes 缺省时不作此判断。
|
|
294
|
+
if total_nodes and total_nodes > 0:
|
|
295
|
+
return {
|
|
296
|
+
"ok": False,
|
|
297
|
+
"reason": "empty_buckets",
|
|
298
|
+
"buckets": 0,
|
|
299
|
+
"nodes": int(total_nodes),
|
|
300
|
+
"problems": [f"分桶表为空但库内有 {int(total_nodes)} 个节点:"
|
|
301
|
+
"分区信息缺失,条件路由不可用(非真空库)"],
|
|
302
|
+
}
|
|
281
303
|
return {"ok": True, "reason": "empty", "buckets": 0}
|
|
282
|
-
nb = len(counts)
|
|
283
304
|
top = max(counts.values())
|
|
284
305
|
singles = sum(1 for v in counts.values() if v == 1)
|
|
285
306
|
# 期望扫描占比:随机取一节点的情境去路由,命中桶的期望大小占全库比例
|
|
286
307
|
expected_scan = sum(v * v for v in counts.values()) / n / n
|
|
287
308
|
problems = []
|
|
288
|
-
if top / n > 0.30:
|
|
309
|
+
if nb > 1 and top / n > 0.30:
|
|
289
310
|
problems.append(f"巨桶:最大桶占 {top / n:.1%}(>30% 视为分区失效)")
|
|
290
311
|
if nb > 1 and singles / nb > 0.50:
|
|
291
312
|
problems.append(f"碎片化:单例桶占 {singles / nb:.1%}(>50% 说明键含实例级字段)")
|
|
292
|
-
if
|
|
313
|
+
if nb == 1:
|
|
314
|
+
# 巨桶/期望扫描两条在 nb==1 时只是同一事实的两种写法,合并为一条如实读数。
|
|
315
|
+
# expected_scan 的**数值**仍在返回 dict 里(下游读的是数:test_p0 :92、
|
|
316
|
+
# bench_axis_domain :159),只有文案不再把「未分区」说成「巨桶退化」。
|
|
317
|
+
problems.append(f"单桶:全库仅 1 个分桶({n} 个节点同桶),"
|
|
318
|
+
"条件路由本就不可用(从未分区,非分区退化)")
|
|
319
|
+
elif expected_scan > 0.30:
|
|
293
320
|
problems.append(f"路由无效:期望扫描 {expected_scan:.1%}(接近全量)")
|
|
294
321
|
return {
|
|
295
322
|
"ok": not problems,
|
package/md_cg/run_tests.py
CHANGED
|
@@ -38,6 +38,23 @@ import sys
|
|
|
38
38
|
import tempfile
|
|
39
39
|
import time
|
|
40
40
|
|
|
41
|
+
# ---------------------------------------------------------------- 入口自保证 UTF-8
|
|
42
|
+
# 约束(工作纪律第 15 条):本调用必须在**任何文件/库 I/O 之前**——utf8_boot.ensure_utf8
|
|
43
|
+
# 在解释器未开 UTF-8 模式时以相同 argv 重启自身(-X utf8),早于它的任何 open/stdio
|
|
44
|
+
# 读写都走 locale 编码(Windows 中文机 = cp936:裸 open 抛 UnicodeDecodeError、中文写
|
|
45
|
+
# 落 GBK 字节)。本文件是包内全量入口(安装态下 README 的门禁命令),必须最先保证。
|
|
46
|
+
# 仓库根入 sys.path 的形态照 hive/exec.py::_md_cg_import 的最小写法(助手在仓根,
|
|
47
|
+
# 不是 md_cg 包目录)。
|
|
48
|
+
# 被 import(本模块非 __main__)时助手只置子进程继承面、绝不重启/退出——F6:静默重启
|
|
49
|
+
# 会吞掉调用方输入。
|
|
50
|
+
_UTF8_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
51
|
+
if _UTF8_ROOT not in sys.path:
|
|
52
|
+
sys.path.insert(0, _UTF8_ROOT)
|
|
53
|
+
from utf8_boot import ensure_utf8 # noqa: E402
|
|
54
|
+
|
|
55
|
+
ensure_utf8(__file__)
|
|
56
|
+
|
|
57
|
+
|
|
41
58
|
_REPO = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
42
59
|
#: 逐用例日志目录(重定向目标;默认临时目录,避免污染仓)
|
|
43
60
|
_LOG_DIR = os.environ.get("MDCG_TESTLOG_DIR") or os.path.join(
|
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",
|
package/md_cg/sources.py
CHANGED
|
@@ -273,8 +273,17 @@ class HiveJobsSource(Source):
|
|
|
273
273
|
result.json 终态 → **权威确认事件**(progress 可能因强杀缺失 final):
|
|
274
274
|
done → 「任务完成(job=…)」;error/timeout/killed → 「任务失败(job=…)」
|
|
275
275
|
|
|
276
|
-
|
|
277
|
-
|
|
276
|
+
排序:**job_id 名升序**(`sorted(listdir)`,确定性),**不是**时间序——
|
|
277
|
+
契约 v2(`docs/plans/全中文编码与蜂巢任务标识契约_v2.0.md` §四.5)起 id 改为
|
|
278
|
+
语义四槽 `h_<身份>_<任务>_<单元>_<编号>`,名序不再等于提交时序:旧形态
|
|
279
|
+
`h<unix_ms>_<pid>` 才是「名序 = 时间序」的**巧合代理**。据实订正(C2:本仓
|
|
280
|
+
禁止把代理当结构保证)——本源的时序由来源行自带的 ts 承载,排序只求
|
|
281
|
+
**跨 job 全序且确定**(seq 由本源按枚举顺序递增,ingest 去重键 = (session, seq),
|
|
282
|
+
名序的确定性保证该键跨轮稳定)。
|
|
283
|
+
诚实边界(**已发现、未在本批动码**):本源的 `_jobs()` 若需要**真时间序**,
|
|
284
|
+
应改走与 Rust 侧同判据的 created_ts 真值(`hive/src/job.rs::list_jobs_by_created`
|
|
285
|
+
的语义,Python 侧对应 `mcp_server._list_jobs_by_created`)——契约 §四.5 的消费者
|
|
286
|
+
清单未列本源,且改动牵动 md_cg 摄入链语义与其守卫,故留待单独裁决,不静默改。
|
|
278
287
|
解析失败的行/条目计入 self.skipped,不终杀批次。
|
|
279
288
|
"""
|
|
280
289
|
|
|
@@ -626,11 +635,14 @@ class FileDispatcher:
|
|
|
626
635
|
|
|
627
636
|
# ---- stat:看水位与支持面 ----
|
|
628
637
|
|
|
629
|
-
# 生效条件:from . import refindex 与 refindex.Ledger(self.cg.root).
|
|
638
|
+
# 生效条件:from . import refindex 与 refindex.Ledger(self.cg.root).summary() 均不抛异常时返回该 summary 结果,抛任何异常时返回 {}。
|
|
630
639
|
def _ledger_stat(self):
|
|
640
|
+
# `Ledger` 只有 `summary()`、**没有** `stat()`:此前写 `.stat()` 撞
|
|
641
|
+
# AttributeError 被下面的 except 吞成 `{}`,于是 `ingest stat` 的水位面
|
|
642
|
+
# 永远是空且无人知晓(唯一实现面是 `refindex.Ledger.summary`)。
|
|
631
643
|
try:
|
|
632
644
|
from . import refindex
|
|
633
|
-
return refindex.Ledger(self.cg.root).
|
|
645
|
+
return refindex.Ledger(self.cg.root).summary()
|
|
634
646
|
except Exception: # noqa: BLE001
|
|
635
647
|
return {}
|
|
636
648
|
|
|
@@ -702,7 +714,7 @@ class FileDispatcher:
|
|
|
702
714
|
"counts": counts,
|
|
703
715
|
"note": "预演:仅统计各链文件数,未做任何写入"}
|
|
704
716
|
|
|
705
|
-
# 生效条件:root 非目录时返回 ok=False 的「目录不存在」;dry_run 为真时返回 _dry_dir(root);否则对 doc_ref/code_ref 两链各以 patterns/max_files/max_items/incremental/ledger 调 refindex.index_dir 与 add_items,并把 root 下 **/*.jsonl 前 max_files 个逐个 ingest_jsonl 后返回 out。
|
|
717
|
+
# 生效条件:root 非目录时返回 ok=False 的「目录不存在」;dry_run 为真时返回 _dry_dir(root);否则对 doc_ref/code_ref 两链各以 patterns/max_files/max_items/incremental/ledger 调 refindex.index_dir(commit=False) 与 add_items 后 ledger.save(),并把 root 下 **/*.jsonl 前 max_files 个逐个 ingest_jsonl 后返回 out。
|
|
706
718
|
def ingest_dir(self, root, layer=None, sensitivity=None, patterns=None,
|
|
707
719
|
max_files=500, max_items=2000, incremental=False,
|
|
708
720
|
dry_run=False):
|
|
@@ -716,13 +728,18 @@ class FileDispatcher:
|
|
|
716
728
|
for ref_kind, key in (("doc_ref", "doc"), ("code_ref", "code")):
|
|
717
729
|
items, errors, stats = refindex.index_dir(
|
|
718
730
|
root, kind=ref_kind, patterns=patterns, max_files=max_files,
|
|
719
|
-
max_items=max_items, incremental=incremental, ledger=ledger
|
|
731
|
+
max_items=max_items, incremental=incremental, ledger=ledger,
|
|
732
|
+
commit=False)
|
|
720
733
|
ids, sens = refindex.add_items(self.cg, items, kind=ref_kind,
|
|
721
734
|
root=root, layer=layer,
|
|
722
735
|
sensitivity=sensitivity)
|
|
736
|
+
# 写序(同 op=index_code/index_doc):节点先落盘,水位随后——
|
|
737
|
+
# 中途被杀只会留下「节点新 + 水位旧」,下次增量重切,不会静默漏漂移。
|
|
738
|
+
ledger.save()
|
|
723
739
|
out["chains"][key] = {
|
|
724
740
|
"indexed": len(ids), "errors": len(errors),
|
|
725
741
|
"files": stats.get("files"), "truncated": stats.get("truncated"),
|
|
742
|
+
"empty_scan": stats.get("empty_scan"),
|
|
726
743
|
"skipped_unchanged": stats.get("skipped_unchanged", 0),
|
|
727
744
|
"skipped_suffixes": stats.get("skipped_suffixes", []),
|
|
728
745
|
"sensitivity": sens}
|