@furongjun1999/dsh-memory 0.6.0 → 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 -572
- 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/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/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_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/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_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/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 -3547
- package/md_cg/mdcos.py +4491 -4159
- 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/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_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 +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_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_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 +3 -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/zcode/AGENTS.md +10 -10
|
@@ -149,7 +149,7 @@ judge_qualification(mdcg.py:709-714)
|
|
|
149
149
|
| `md_cg/codeindex.py` | **改造** | ①`render()` 改产 CCG 5 要素(修 B)②后缀注册化(`.py` 走 AST、`.ts/.js` 走弱提取器)③`index_dir` 返回 `truncated` 与 `files_scanned`(修 F)④`node_id` 保持稳定(重复索引幂等)⑤`skip_matcher()` + `skip_dirs`(R3.2,两条链路的唯一实现) |
|
|
150
150
|
| `md_cg/mcp_server.py` | **扩** | 新增 `op=ref`(`action=read\|check\|stat\|prune`,兼回读 / 巡检 / 清退);`op=index_code` 保留为别名并透传 `truncated`;`op=index_doc`;op 枚举与 `:542-546` 同步;`index_code`/`index_doc` 索引后自动节点级对账(`prune` 默认开、`prune_dry_run` 可预演,R3.1);`skip_dirs` 透传 + `skipped_dirs` 回报(R3.2) |
|
|
151
151
|
| `md_cg/tokens.py` | **改** | `ALL_OPS`(`:56-59`) 加 `ref` / `index_doc`(漏改会被权限层直接拒),`test_p21_tokens.py` 同步 |
|
|
152
|
-
| `md_cg/sustain.py` | **扩** | `diagnose` 增巡检项 `ref_stale` / `ref_dangling`,`heal`
|
|
152
|
+
| `md_cg/sustain.py` | **扩** | `diagnose` 增巡检项 `ref_stale` / `ref_dangling`,`heal` **只对 `ref_stale` 自动重建**;`ref_dangling` 只检出(`fix=None`),处置走 `op=ref action=prune`(软删)或恢复真源后重建(派生物,符合现有自愈边界 `:12-14`) |
|
|
153
153
|
| `md_cg/mdcos.py` | **扩** | 检索结果带 `ref` 与 `ref_kind`(读侧不改召回逻辑,只加字段) |
|
|
154
154
|
| `md_cg/test_p26_refindex.py` | **新增** | 见 §七 |
|
|
155
155
|
|
|
@@ -434,7 +434,7 @@ md 不是代码,切块风险集中在「把不该当标题的当标题」。
|
|
|
434
434
|
| `md_cg/refindex.py` | **新增**:统一 ref 协议(`REF_KEYS` / `registry()` / `kind_of_path()` / `extract()`,无提取器后缀**显式 `raise`**);`Ledger` 索引水位(`_refindex.json`,**键 = 源文件绝对路径**,逐文件记 `(size, mtime)` + 节点区间 + **源大域 `root`**);`index_dir()` 统一调度(`fresh`/`on_file` 钩子 + 增量跳过 + 截断留痕 + `prune`/`reconcile` 对账);`probe_ref()`/`read_ref()` 回读;`check_refs()` 漂移·悬空巡检;`rebuild()` 修复动作 | 修 D、修 F |
|
|
435
435
|
| `md_cg/codeindex.py` / `md_cg/docindex.py` | `index_dir` 加 `fresh`/`on_file` 钩子与 `skipped_unchanged` 计数(默认 `None` → 与改造前**逐字一致**) | 修 F |
|
|
436
436
|
| `md_cg/mcp_server.py` | `op=index_code`/`index_doc` 改走 `refindex.index_dir` + `refindex.add_items`(**落盘唯一实现**);新增 `incremental`/`max_items` 参数(传 `sensitivity` 时自动退回全量);`_ref_call` 重写为 `action=read\|check\|stat`,**未知 action `raise`**;`read` 与检索结果带 `ref`/`ref_kind` | 修 D、修 F |
|
|
437
|
-
| `md_cg/sustain.py` | `diagnose` 增 `ref_stale`/`ref_dangling`/`ref_check_truncated` 巡检项与 `ref_checked`/`ref_stale`/`ref_dangling` stats;`heal` 增 `rebuild_refs`
|
|
437
|
+
| `md_cg/sustain.py` | `diagnose` 增 `ref_stale`/`ref_dangling`/`ref_check_truncated` 巡检项与 `ref_checked`/`ref_stale`/`ref_dangling` stats;`heal` 增 `rebuild_refs` 动作(**2026-09-29 收窄:只对 `ref_stale` 触发**——悬空域重切只会 0 文件,`ref_dangling` 只检出、处置走 `op=ref action=prune` 或恢复真源;派生物,符合既有自愈边界) | 修 D 接入 |
|
|
438
438
|
| `md_cg/test_p28_refcheck.py` | **新增** 验收用例(**45 断言,全绿**) | §七 |
|
|
439
439
|
|
|
440
440
|
### 14.2 三个真实缺陷(首轮实测 39 通过 / 6 失败 → 修后 45/0)
|
|
@@ -583,6 +583,42 @@ md 不是代码,切块风险集中在「把不该当标题的当标题」。
|
|
|
583
583
|
|
|
584
584
|
受影响面 10 模块**全绿**:`test_p2_mcp` 63/0、`test_p9_forget_protect` 37/0、`test_p14_sustain` 44/0、`test_p21_tokens` 31/0、`test_p26_refindex` 55/0、`test_p27_docindex` **75/0**、`test_p28_refcheck` 45/0、`test_p29_session_ingest_export` 48/0、`test_p30_maintain` 55/0、`test_p32_backfill` 58/0。改动文件 lint 0 错误。
|
|
585
585
|
|
|
586
|
+
## 十六、批次86 取证纪录(2026-09-29):ref 判据分工 + 自愈止血 + 水位保全 + 写序
|
|
587
|
+
|
|
588
|
+
### 16.1 改动清单
|
|
589
|
+
|
|
590
|
+
| 文件 | 改动 | 对应 |
|
|
591
|
+
|---|---|---|
|
|
592
|
+
| `md_cg/refindex.py` | `check_refs` 快路径 v2:按 node_id 去重;探测数据**只取节点自身 frontmatter ref**(`_fast_path_verdict`,也是红基线定点变异锚点);`agree` 扩为 `(hash,lineno,end,path,root)` 五项全等;不一致记 `ledger_mismatch` 归因行(带两侧 root,`watermark_behind` / `collision_multi_root`),**不进 ok**。`index_dir` 增 `commit`(写序)与零信息纪律(`seen` 为空不对账);`note_index` 增 `empty_scan`;`rebuild` 增悬空 root 拦截(`roots_missing`)+ 逐组回报(`rebuilt`)+ 空扫置 `ok=False` | §#4 / H1 / H1b / H2 / #2 |
|
|
593
|
+
| `md_cg/mcp_server.py` | `op=index_code`/`index_doc` 改「`index_dir(commit=False)` → `add_items` → `led.save()`」;`op=ref action=check` 的 note 按 H3 口径重写 | #2 / H3 配套 |
|
|
594
|
+
| `md_cg/sustain.py` | `heal` 只对 `ref_stale` 触发 `rebuild_refs`;`ref_dangling` 的 `fix` 改 `None`(只检出);`act` 用上 `fn()` 返回值(`ok` 与 `result` 摘要进动作与审计);新增进程内失败记忆 `_HEAL_MEMO` + `_repeat_skip`/`_repeat_remember`(同因不重试 + `heal_interval×2^n` 退避,上限 1h,仅常驻循环启用);`_tick_heal` 传 `heal_interval`/`repeat_guard` 并记 `failed` | H3 / H4 / H6 |
|
|
595
|
+
| `md_cg/sources.py` | `_ledger_stat` 的 `Ledger.stat()`(不存在,异常被吞 ⇒ 水位面永远为空)改 `summary()`;`ingest_dir` 同写序(`commit=False` + `add_items` 后 `save`) | H5 / #2 |
|
|
596
|
+
| `md_cg/test_p28_refcheck.py` | 段⑥⑦(判据分工三组构造 + 撞 id 现场)、段⑫(自愈止血与水位保全,11 断言)、**段⑬口径钉子**(id 手算复算 + 对 root/正文/行位不敏感 + 五项逐项不跳过 + 文档 §3.1 字面/指纹/行级引用同源)、**段⑭常驻循环止血**(`repeat_guard` / 不静默 / 不假装修好)、`--head-baseline` 红基线(`EXPECTED_RED=(A1,B1,C1)`,锚点漂移 ⇒ 退出码 2) | §七 / #5 |
|
|
597
|
+
| `docs/plans/真源索引_通用机制_v0.3.md` | §3.1 口径写正(选 B:id 位置化、幂等判据由区间哈希承担);**行级引用对齐实现现址**(`refindex.py:445`/`:459`/`:503`/`:520`/`:588`/`:784`、`srcindex.py:71`/`:86`)——原值系按「旧号 + 固定偏移」推得,与现址不符 | #5 |
|
|
598
|
+
| `scripts/linux_verify.sh` | `test_p28_refcheck` 进 python 套件清单;`test_p28_refcheck --head-baseline` 进自证清单 | §七 |
|
|
599
|
+
|
|
600
|
+
### 16.2 实测(全部临时库 + 显式 root,未触活库)
|
|
601
|
+
|
|
602
|
+
- **#4 判据分工**(同夹具下 HEAD 与 v2 对照):①只推水位 ⇒ HEAD `ok=True`(漏报)→ v2 `ok=False`/`stale=2`;②只推节点 ⇒ HEAD `ok=False`/`stale=2`(误报)→ v2 `ok=True`/`stale=[]`;③篡改节点 `ref.hash` ⇒ HEAD `ok=True`(静默)→ v2 `ok=False`/`stale=[该 id]`;④撞 id 稳态两版都 `ok=True`,但 v2 多出 `collision_multi_root` 归因行;⑤撞 id 且两域源都改 ⇒ HEAD `stale 4 行/dup=2` → v2 `2 行/dup=0`(夹具每文件 2 节点)。
|
|
603
|
+
- **H1/H1b/H2**:目录仍在零文件 ⇒ 水位条目与节点数保住 + `last_index.files=0/empty_scan=True`;目录已删且**直调** `index_dir` ⇒ 仍剪空(prune 保留的代价,op 面已被 isdir 拒);`rebuild` 遇悬空 root ⇒ `ok=False`/`roots_missing=[root]`/水位 files 保住;空扫组 ⇒ `ok=False` + errors + `rebuilt[*].empty_scan=True`。
|
|
604
|
+
- **H3/H4/H6**:仅 `ref_dangling` ⇒ `heal` 动作里**没有** `rebuild_refs`;漂移+悬空混存 ⇒ `rebuild_refs` 记 `applied=True/ok=False` 且带 `result.roots_missing`(失败不被记成成功);同信号再跑 ⇒ `applied=False/reason=repeat_failure/streak=1/wait_s=300`,退避窗过后放行重试。
|
|
605
|
+
- **H5/写序**:`FileDispatcher._ledger_stat()` 返回 `{path,files,nodes,age_s,exists,last_index,…}`(改前恒 `{}`);节点写入失败时水位**未落盘**(`index_read` 抛 `RuntimeError`、`_refindex.json` 不存在)。
|
|
606
|
+
- **#5 口径钉子 + 循环止血(段⑬⑭,本批次收尾补写)**:`python -m md_cg.test_p28_refcheck` 绿态 **90 通过 / 0 失败(exit 0)**;`--head-baseline` **恰好命中 A1/B1/C1(exit 0)**,段⑬⑭ 在变异态**仍绿**(故红项表不必扩)。就地定点探针(只改动—跑—还原,sha256 复核还原)四条各自转红:`node_id` 改内容寻址 ⇒ 段⑬ 5 项(F1/F2/F3 等);§3.1 措辞改回「内容寻址」⇒ F6 两项(字面缺失 + 指纹陈化);文档行级引用挪一行 ⇒ F7 一项;`_tick_heal` 去掉 `repeat_guard=True` ⇒ G1/G3 两项。回归:`test_logref` 92/0、`test_p14_sustain` 44/0(各 exit 0)。
|
|
607
|
+
|
|
608
|
+
### 16.3 生效条件(常驻进程)与人工步骤
|
|
609
|
+
|
|
610
|
+
1. **在役 MCP 进程不吃新码**:常驻 `md_cg.mcp_server` 进程在启动时 import,之后一直跑**旧字节码**。上面全部改动要**该进程重启/重连后**才生效;在此之前它仍按旧序写水位、仍按旧口径 heal(含对 dangling 重建、无重复失败记忆)。判定与处置走 `scripts/mdcg_stale_servers.py`(`list` 只读枚举 + `--fail-on-stale`;`kill --pid` 显式处置,`mtime` 只是「源码已改、进程未重载」的附加信号)。
|
|
611
|
+
2. **活库水位复原是人工步骤**(本仓不做、不在本批次内执行):被旧代码剪空的水位(`files=0`)按域重跑 `op=index_code` / `op=index_doc`——旧 raw root 已不存在,须按**真源现址**重跑;因 `code_`/`doc_` id 不含 root,重跑会覆盖同名 id 节点并把 `ref.root` 指向新址,悬空随之消解。验收=`op=ref action=stat` 的 `files>0`、`last_index.files>0` 且 `empty_scan` 缺省,`op=ref action=check` 的 `truncated=False` 且 dangling 按域下降。H1/H1b 只防「再被写成空」,**不自动复原历史**。
|
|
612
|
+
3. **无可再生路径的悬空**:`op=ref action=prune` 软删(`regenerable` 与带 `doc_ref.src.file_hash` 的节点会被跳过)或恢复真源后重建;转写被清理的日志节点走 `scripts/_mdcg_reindex_dshlogs.py --repair 1`。
|
|
613
|
+
|
|
614
|
+
### 16.4 诚实边界(批次86 新增)
|
|
615
|
+
|
|
616
|
+
1. **H6 的退避是进程内记忆**(重启即清),且只在常驻循环里启用;手动 `op=sustain action=heal` 不受影响(手动=显式要求试一次)。
|
|
617
|
+
2. **H6 不是「不报」**:首次失败永远真跑并如实记 `ok=False`;被拦下的重复失败记 `applied=False/reason=repeat_failure/streak`,并单列进 `_tick_heal` 的 `heals[*].failed`。
|
|
618
|
+
3. **没有 op 级的水位清理出口**:H1 之后,「已搬走的域」的水位条目不再被自动清(prune 只在 root 非目录且被直调 `index_dir` 时触发)。要清只剩重跑/直调,或另加 `op=ref action=ledger_prune`(本批次未含)。
|
|
619
|
+
4. **`ref_watermark_empty` / `ref_watermark_mismatch` 的 diagnose 归因项未加**:其 severity(建议 info)需业主裁定;当前 `ledger_mismatch` 由 `op=ref action=check` 原样透出,故撞 id / 写序断裂已非静默。
|
|
620
|
+
5. **P28 守卫的共享库根不自动清空**(`_md_cg_p28`,gitignored):`corpus.reset_root` 默认**保留**目录(幂等靠同 id 原子覆盖,见其头注)。若历史轮次写入过**别的 id 口径**的节点(例:把 `codeindex.node_id` 改成内容寻址跑过一轮,实测过),那批节点永不被覆盖、其 `ref.root` 指向已被删除的临时源 ⇒ 此后**每轮** §5/§8 都多报 dangling(实测 2 项红,与实现无关)。排障=删 `_md_cg_p28` 后重跑;判据侧刻意不自动清(`rmtree` 在批量删除被拒的环境里会静默失败,反而不如让它显性报红)。
|
|
621
|
+
|
|
586
622
|
---
|
|
587
623
|
|
|
588
|
-
*计划生成:2026-09-10 · 证据:§一 全部为源码行级核对结果 · 未核对项已单列 §1.3 / §十 · §十一 为 R0 实测取证 · §十二 为 R1 实测取证 · §十三 为 R2 实测取证 · §十四 为 R3 实测取证 · §十五 为 R3.1 / R3.2
|
|
624
|
+
*计划生成:2026-09-10 · 证据:§一 全部为源码行级核对结果 · 未核对项已单列 §1.3 / §十 · §十一 为 R0 实测取证 · §十二 为 R1 实测取证 · §十三 为 R2 实测取证 · §十四 为 R3 实测取证 · §十五 为 R3.1 / R3.2 实测取证 · §十六 为批次86(ref 判据分工 / 自愈止血 / 水位保全 / 写序)实测取证*
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
# 全中文编码与蜂巢任务标识契约 v2
|
|
2
|
+
|
|
3
|
+
> 日期:2026-09-29 | 对象:`D:\program\dsh-memory-main`
|
|
4
|
+
> 状态:**四项已全部裁决(2026-09-29)**;**已动码(2026-09-30,见 §五.1)**
|
|
5
|
+
> 已签收:①编号 4 位、溢出**显式报错** ②身份/任务/单元**必填**,且单元槽取**蜂巢五单元**闭集 ③编号分配**由 Rust 侧实现**(判据单点)④**不做控制台兼容**——取用通道走 python,README 明写全 UTF-8
|
|
6
|
+
> 上位裁定:使用者 2026-09-29「中文入路径;我们代码所有路径都要是 utf-8 兼容中文字符;我们以后是全中文编码」。
|
|
7
|
+
> 本稿同时承接优化清单 **H-1**(MCP 面绕过去重 + `new_job_id` 同毫秒碰撞)的 id 形态裁决——使用者的四槽式设计(目录/任务/编号/身份)取代「时间戳 + pid」。
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## 一 决定与它的真实作用面
|
|
12
|
+
|
|
13
|
+
使用者裁定两件事:**①路径全面 UTF-8 兼容中文**;**②蜂巢任务标识改用语义四槽组合,不再用毫秒+pid**。
|
|
14
|
+
|
|
15
|
+
关键判断(本文档的立论):**这两件事的性质完全不同**。
|
|
16
|
+
|
|
17
|
+
- ① 不是"新建能力",而是"把已有能力钉成不依赖环境变量的硬约束"——三个面的编码层**已经**是 UTF-8 端到端(§二 实测),但撑住它的是**环境变量 + 每个入口自己记得设**,而仓里有 224 处裸 `open()`(§三)。
|
|
18
|
+
- ② 不是"编码问题",而是**一道故意的安全闸**在挡路——`valid_job_id` 只收 ASCII,放宽它是全案唯一的**安全面**改动(§四.4)。
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## 二 现状实测(2026-09-29,四条,全部实跑)
|
|
23
|
+
|
|
24
|
+
| 面 | 观测 | 判定 |
|
|
25
|
+
|:--|:--|:--|
|
|
26
|
+
| Windows Python | `sys.getfilesystemencoding()='utf-8'`;中文目录 `zcode端_灵枢迭代_反思单元_0001` 与中文文件名的建/写/读/列全部正确 | 通 |
|
|
27
|
+
| 容器 `rust:bookworm`(门禁栈一同镜像) | `LANG`/`LC_ALL` **全空**(C locale)下 `fsenc='utf-8'`、`stdout='utf-8'`;中文目录与 `优化报告_灵枢迭代_v1.0.md` 全部正常 | 通 |
|
|
28
|
+
| Windows Rust(真 `hive.exe`,release 二进制) | 以中文池 `…/zcode端_灵枢迭代` 与中文 spec 路径 `任务_反思单元_1号.json` 提交:exit 0,`jobs_dir` 回读中文**逐字完好**,`spec.json` 以 UTF-8 原样落盘并回读正确 | 通 |
|
|
29
|
+
| 仓内既有事实 | `docs/` 已全为中文文件名(本批报告 `优化第二批_依赖透传与对拍口径与负缓存_v1.0.md`),python 全量与容器双栈门禁一路绿 | 通 |
|
|
30
|
+
|
|
31
|
+
结论:**编码层不需要重做**。容器那一条尤其重要——`rust:bookworm` 里 locale 是 C,本该退化成 ASCII,实测仍是 utf-8,是 PEP 538/540 的 C locale 强制转换在起作用;这条链路不能默认它永远生效(见 §四.2)。
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## 三 脆弱点(必须钉住的东西)
|
|
36
|
+
|
|
37
|
+
1. **系统 locale 是 GBK。** 本机 `locale.getlocale()` = `('Chinese (Simplified)_China', '936')`,而 Python 裸 `open()` 的缺省编码取 `locale.getpreferredencoding(False)` ⇒ **不设 UTF-8 模式时,裸 `open()` 在本机走 cp936**。
|
|
38
|
+
2. **今天没炸,靠的是环境变量。** 本机环境里设了 `PYTHONUTF8=1` 与 `PYTHONIOENCODING=utf-8`。入口也已显式钉:`scripts/run_tests.py:88`(给子进程覆盖)、`scripts/linux_verify.sh:9`(export)、TS 桥层 `src/lib/mdcg_client.ts`(同名注释见 `hive/hive_mcp/mcp_server.py:852`)。
|
|
39
|
+
3. **裸 `open()` 224 处**(`md_cg/` `hive/` `scripts/`,未传 `encoding=`)。ASCII 数据无碍,一旦内容或路径含中文,就看第 1 条的脸色。
|
|
40
|
+
4. **已有乱码实证**:本会话中 mdcg 的 stderr 打出过 `[mdcg-mcp] 鈿� 瀵嗙骇閰嶇疆…`——这一类通道未对齐编码的现成样本。
|
|
41
|
+
5. **零处归一化**:全仓无 NFC/NFD 归一化。中文受的影响小,但混排拉丁带音调与 CJK 兼容表意文字(U+FA10 等**确实**会 NFD 分解)会咬人;macOS 按 NFD 存储名字,而 `depends_on` 判据是**字符串相等** ⇒ 不归一化即静默失配。
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## 四 硬约束(要落成什么)
|
|
46
|
+
|
|
47
|
+
### 四.1 路径与文件名一律 UTF-8 + NFC
|
|
48
|
+
|
|
49
|
+
- 一切落盘的可读文本固定 UTF-8;一切用户可见的路径/文件名/id 固定 **NFC**。
|
|
50
|
+
- 归一化在**写入边界**执行一次(提交面/落盘点),比较前亦归一化;判据单点,不得各处自持。
|
|
51
|
+
|
|
52
|
+
### 四.2 进程自保证 UTF-8(不依赖使用者 profile)
|
|
53
|
+
|
|
54
|
+
- 每个入口(`hive/hive_mcp/mcp_server.py`、`hive/orch.py`、`hive/exec.py`、`scripts/*`、TS 桥)在最早处检查 `sys.flags.utf8_mode`;未开则设置 `PYTHONUTF8=1`/`PYTHONIOENCODING=utf-8` 并**重启自身**,不可重启时 **fail-fast 报错**(同 N200「无模型密钥启动前置报错」的形状:不静默降级)。
|
|
55
|
+
- 这一条落地后,§三.3 那 224 处的**大部分风险直接消失**(UTF-8 模式下裸 `open()` 即 UTF-8),比自己逐个补 `encoding=` 便宜一个数量级。
|
|
56
|
+
|
|
57
|
+
### 四.3 机械守卫:裸 `open()` 不许新增
|
|
58
|
+
|
|
59
|
+
- AST 扫描生产模块(`md_cg/` `hive/` `scripts/`,排除 bench)里未传 `encoding=` 的 `open()` 与无 `encoding=` 的 `Path.read_text/write_text`,白名单化存量,**新增即红**。
|
|
60
|
+
- 与 §四.2 互为兜底:自保证是运行期防线,守卫是提交期防线。守卫须自带定点变异自证(本仓惯例)。
|
|
61
|
+
|
|
62
|
+
### 四.4 id 契约 v2(安全面改动,最需谨慎的一节)
|
|
63
|
+
|
|
64
|
+
**形态**:`h_<身份>_<任务>_<单元>_<编号>`,例 `h_zcode端_灵枢迭代_反思单元_0001`。
|
|
65
|
+
|
|
66
|
+
| 槽 | 例 | 必填 | 来源 | 说明 |
|
|
67
|
+
|:--|:--|:--|:--|:--|
|
|
68
|
+
| 身份 | `zcode端` | **是** | 提交面显式声明(env/入参) | 提交者/端别;**不允许静默推导**(猜错且静默正是 H-1 那半边缺陷的形状) |
|
|
69
|
+
| 任务 | `灵枢迭代` | **是** | 显式声明 | 工作流或迭代名;目录可作它的父段(成 5 段) |
|
|
70
|
+
| 单元 | `反思单元` | **是** | 显式声明,**闭集** | 见下「五单元闭集」 |
|
|
71
|
+
| 编号 | `0001` | 分配得出 | Rust 侧分配器 | 该 (身份,任务,单元) 前缀下的序号,**定宽 4 位**,溢出 **显式报错**(不加宽——加宽会破坏定宽前提) |
|
|
72
|
+
|
|
73
|
+
**五单元闭集(本契约的关键收窄,使用者 2026-09-29 裁定)**:单元槽**不是自由文本**,取值限定为**蜂巢五单元**,真源是 `md_cg/identity.py:55-67` 的 `POSITIONS`:
|
|
74
|
+
|
|
75
|
+
| pos | 单元 | effect | duty |
|
|
76
|
+
|:--|:--|:--|:--|
|
|
77
|
+
| `record` | 记录单元 | 全 | 保存观测、过程、结果和误差 |
|
|
78
|
+
| `reflect` | 反思单元 | 新 | 发现差异、遗漏条件和新的路径 |
|
|
79
|
+
| `verify` | 验证单元 | 稳 | 判断规则、执行结果和结构是否有效 |
|
|
80
|
+
| `output` | 输出单元 | 通 | 与外部系统协作并表达边界 |
|
|
81
|
+
| `sustain` | 维生系统 | 存 | 维护存在、预算、回滚和整体结构 |
|
|
82
|
+
|
|
83
|
+
- 好处:任务的身份里直接带**它归哪个职能单元**,而词表有既有真源、不会漂移;提交面校验即 `∈ POSITION_ORDER`。
|
|
84
|
+
- 守卫:**沿用仓内既有的同源断言形态**(`md_cg/test_p21_tokens.py:218`「与 `identity.POSITIONS` 同源校验」)——id 词表与 `POSITIONS` 必须逐项一致,任一侧增删即红。
|
|
85
|
+
- `POSITION_ORDER`(`identity.py:67`)同时给出**规范顺序**,可用于校验与排序,不必在 id 契约里另立一份。
|
|
86
|
+
- 注意:**副代理不是第六个单元**(`docs/hive/子代理配置标准_v0.5.md:151`:副代理是融合位 𝓕 本身,五单元是 𝓕 的功能分解)⇒ 词表恰为 5,不得为副代理增设槽值。
|
|
87
|
+
|
|
88
|
+
**编号分配算法=独占创建即分配**:对目标名执行 `fs::create_dir`(Rust)/`os.mkdir`(Python)——目标已存在时**必然失败**,失败即编号加一重试(有界重试,超限显式报错)。性质:
|
|
89
|
+
- 碰撞**结构上不可能**,不再依赖「毫秒恰好不同 ∧ pid 恰好不同」;
|
|
90
|
+
- **H-1 的碰撞那半因此被消解而非被修补**——不需要进程内计数器、不需要提高熵;
|
|
91
|
+
- 复用的是仓内既有原语同款(`claimed.lock` 的 `create_new` 独占)。
|
|
92
|
+
- `now_ms()` 保留为 `status.created_ts` 的时钟源,**退出 id**。
|
|
93
|
+
|
|
94
|
+
**字符集闸(`job.rs` 与 `mcp_server.py` 这对双胞胎须同改)**:由「ASCII 字母数字 + `_`」放宽为「**区块白名单内的字母/数字** + `_`」,**同时保留并新增拒收项**。
|
|
95
|
+
|
|
96
|
+
> **判据换面(2026-09-30 使用者裁定 ①-(c);本节按换面后的口径写)**:判据 = **一份编译期嵌入的区块表** `hive/id_charset_blocks.txt` 的**区间并集**,**不查任何 Unicode 属性库**——三处读者(Rust `job.rs` 的 `include_str!` + 首次使用惰性解析;`mcp_server.py` 导入时读一次;`md_cg/units.py` 同样读这份文件)**共读同一份数据**,故两侧属性表版本差**结构性不可能**(旧机制下「全码点同判」不成立)。表由 `scripts/gen_id_charset_blocks.py` **生成、可重跑、带陈化守卫**(`--check`:重算 ≠ 盘上即退出 1):在候选区块(ASCII / 拉丁 / 希腊 / 西里尔 / 假名 / 谚文音节 / CJK 与扩展 / 注音)内逐码点取「类目 ∈ {Lu,Ll,Lt,Lm,Lo,Nd} ∧ **单字符 NFC 稳定**」的交集后**归并成最小区间**——即**区块选择**决定表达面,**类目 + 稳定性**过滤掉区块内的标点/符号/会改写的形态;`_`(U+005F) 与 `.`(U+002E) 不在表内,由结构分支处理。三处读者一律 **fail-closed**(表缺失/解析出空区间集 ⇒ 判据返回 false,绝不放行)。**有意不收**(契约条款,不是遗漏):谚文 Jamo(1100-11FF)与兼容 Jamo(3130-318F)、CJK 兼容表意(F900-FAFF)与兼容表意补充(2F800-2FA1F)、其余兼容/带圈/上下标/数学字母数字/全角半角区块。
|
|
97
|
+
|
|
98
|
+
拒收项(**逐条显式断言**,不靠「白名单外所以默认被拒」):
|
|
99
|
+
|
|
100
|
+
| 拒收 | 理由 |
|
|
101
|
+
|:--|:--|
|
|
102
|
+
| `/`、`\` | 路径分隔(原始缺陷载体:`kill ..` 池外写、`poll ../victim` 池外读) |
|
|
103
|
+
| 单独的 `.` 与 `..`(**整串**等于),以及**按 `_` 分段的任一段**等于 `.`/`..`(**段级**,2026-09-30 加固) | 相对路径。段级读法见下方说明 |
|
|
104
|
+
| `:` | Windows 盘符 |
|
|
105
|
+
| 控制字符、零宽字符、双向控制字符 | 同形异义与不可见注入 |
|
|
106
|
+
| 首尾空白、尾点 | Windows 会静默剥掉 ⇒ 两个不同 id 落到同一目录 |
|
|
107
|
+
| Windows 保留设备名(`CON`/`NUL`/`AUX`/`PRN`/`COM1`…) | 本仓 v0.5.1 刚修过这一类(issue #39),勿回退 |
|
|
108
|
+
|
|
109
|
+
**这是全案唯一的攻击面改动**:必须有守卫(合法/非法两侧用例 + 跨语言对照,沿用 H-6 的 18-id 对照形态),必须做定点变异,且守卫要覆盖 `kill`/`poll`/`depends_on` 三条拼路径入口。
|
|
110
|
+
|
|
111
|
+
> **`.`/`..` 的读法是「段级」(2026-09-30 明确,原表「单独的 `.` 与 `..`」一句有歧义)**:判据 = 按 `_` 分段后,**任一段**等于 `.` 或 `..` 即拒;**整串**等于 `.`/`..` 与**尾点**两条判据一并保留。三处闸**同判**:`hive/src/job.rs::valid_job_id`、`hive/hive_mcp/mcp_server.py::_job_id_reject_reason`、`md_cg/units.py::_valid_job_id`。**范围只加这一条**——不拒空段(`h__x`)、不拒 `...` 段(段是 `...` 而非 `.`/`..`)、不动非尾点 `.` 的既有 accept 契约。**为什么是段级**:id 的每一段都可能被宿主当作目录名或**父目录段**用(本节上表「任务」槽「目录可作它的父段」⇒ 5 段形态),段级 `..` 因此是真实载体;**现状定性如实**:段级口径下被拒的 `h_.._x` / `h_a_.._b` 在旧整串口径下曾被收,其拼出的路径**停在池内、不构成穿越** ⇒ 本项是**面向重构的纵深加固**,不是现实缺陷的修补。守卫 = `hive/test_job_id_segment_dot.py`(A–D 组 49 条断言:段级拒 + 范围边界三例仍收 + 存量零迁移逐个列出 + 语料两例在位且全语料三方复算逐例一致;`--branch-baseline` 三处定点变异 + 三处假阳性对照);语料 = `hive/id_contract_corpus_v2.txt` 第 103–104 行(`h_.._x` / `h_a_.._b`,hex 编码);存量实测:在役 `hive/jobs` 15 个目录被三处闸判非法者 **0**。
|
|
112
|
+
|
|
113
|
+
### 四.5 排序口径:把代理换成真值
|
|
114
|
+
|
|
115
|
+
id 不再含时间后,「字典序 = 提交时序」这条**代理**失效,四个消费者须改为**直接按 `status.created_ts` 排序**(每个 status 都已有该字段):
|
|
116
|
+
|
|
117
|
+
| 站点 | 现语义 | 改后 |
|
|
118
|
+
|:--|:--|:--|
|
|
119
|
+
| `hive/src/main.rs:223` `find_active_by_hash` | 名升序取首个 = 最老活跃同哈希 | 活跃同哈希中 `created_ts` 最小者 |
|
|
120
|
+
| `hive/src/main.rs:473` poll 列表 | 名升序 | `created_ts` 升序 |
|
|
121
|
+
| `hive/src/main.rs:696` doctor | 名升序 | `created_ts` 升序 |
|
|
122
|
+
| `hive/src/scheduler.rs:213`/`:300` 领取顺序 | 名升序 = FIFO | `created_ts` 升序 = FIFO(**语义保持**) |
|
|
123
|
+
|
|
124
|
+
### 四.6 控制台:**明确不做兼容**(使用者 2026-09-29 裁决)
|
|
125
|
+
|
|
126
|
+
先摆事实(实测,真 `hive.exe` + 中文池路径):CLI 写出的字节是**裸 UTF-8**。同一串字节两种读法——
|
|
127
|
+
|
|
128
|
+
| 读法 | 结果 |
|
|
129
|
+
|:--|:--|
|
|
130
|
+
| 按 UTF-8 解码(管道 / 文件场景的正确读法) | `…\Temp\控制台试验_hucyqfhm` |
|
|
131
|
+
| 按 cp936 解码(= 代码页 936 的窗口所见) | `…\Temp\鎺у埗鍙拌瘯楠_hucyqfh` |
|
|
132
|
+
|
|
133
|
+
机制:输出到管道或文件时没有"代码页"这一层,写读双方约定 UTF-8 即可;问题只出在**目标是 Windows 控制台**时——控制台按**自身代码页**解释收到的字节。Python 不出这个问题是因为 PEP 528 让它对控制台改走宽字符 API `WriteConsoleW`(绕过代码页);Rust 的 `println!` 走 `WriteFile` 直写字节,没有这层翻译。
|
|
134
|
+
|
|
135
|
+
**裁决:不做控制台兼容。** 不实现 Rust 侧的 `WriteConsoleW` 分支,也不要求使用者 `chcp 65001`;把正确性收进**约定**而不是**显示层**:
|
|
136
|
+
|
|
137
|
+
1. **取用通道固定为 Python**:本仓一切读写与判定走 `python -X utf8 …`(沿既有工程纪律第 15 条「命令统一走 python + 显式 UTF-8」)。Python 对控制台本就正确,走它即绕开问题。
|
|
138
|
+
2. **README 明写约束**:全仓文本与路径一律 UTF-8(无 BOM),并显式向 AI / 接入方声明「这里是 UTF-8、请走 python、别拿控制台当取用通道」(已落 `README.md` 的「编码约定」节)。
|
|
139
|
+
3. **Rust CLI 的控制台渲染不在承诺面**:CLI 输出为裸 UTF-8 字节;管道/文件读法即正确。控制台里看到什么,属显示层,不承诺。
|
|
140
|
+
|
|
141
|
+
**代价与残余**:若有人偏要在代码页 936 的窗口里直接读 CLI 输出,会看到乱码——这是**已知且接受**的边界;文档已写明规避方式(走 python、或把输出重定向到文件后按 UTF-8 读)。### 四.7 明确不改的面
|
|
142
|
+
|
|
143
|
+
- **`h` 前缀保留**:`valid_job_id` 的 `starts_with('h')`、`list_jobs` 的 `h` 前缀过滤、以及全部依赖它的守卫不动。去掉它要牵动一大片,收益为零。
|
|
144
|
+
- **存量零迁移**:旧 `h<13位毫秒>_<4hex>` 与新形态**同时满足**放宽后的闸、都以 `h` 开头被 `list_jobs` 收;§四.5 改按 `created_ts` 排序后两种形态共存无碍。
|
|
145
|
+
- **依赖无环性依据不动**:`scheduler.rs:1017-1019` 的注释(**勘误**:本稿早期写作 690-697,那是 `retry_spawn` 的重试循环,非本论点所在;同论点另在 `spec.rs:49-50`、`job.rs:54-55`、`main.rs:285-286`、`mcp_server.py:187-188`、`hive/README.md:281` 共五处)写明依据是「**无法引用提交时尚不存在的任务**」即**存在性闸**(`main.rs:282-298` + H-6 新增的 MCP 侧 `_dep_gate`),**不是** id 的时间序。语义 id 不危及 I-1。(该注释原「由 job_id 时间序结构性保证」的措辞与本表述的出入**已按本批 C 项订正**:逐处清单与落点见 §五.1「C 论证订正」行。)
|
|
146
|
+
- **MCP/编排面共用一处生成器**:现状是 `mcp_server.py:339` 自造 `h{ms}_{uuid4[:6]}`,本契约要求两侧共用同一分配语义;「谁持唯一实现」是 §五 待裁项。
|
|
147
|
+
### 四.8 已落 vs 未落(收口时如实登记)
|
|
148
|
+
|
|
149
|
+
- **已落**:§四.4(四槽标识、五单元闭集、独占创建即分配、4 位溢出显式报错、字符集闸双胞胎同改 + 新增拒收、`hive alloc-id` 子命令、`_submit` 自造 id 退场)、§四.5(六处消费者 + MCP 两处改按 `created_ts`,含保序证明与「按真值」反证)、§四.7 的论证订正(五处)。
|
|
150
|
+
- **实现级收窄(须知情)**:§四.1 的「归一化在写入边界执行一次」在 id 面上收窄为「**必须已是 NFC,否则拒收**」——理由:`hive/Cargo.toml` 的 `[dependencies]` 是空段(零依赖),手写完整 NFC 不可行;拒收比静默归一化更严、两侧判据可证同判。Python 侧禁止用 `unicodedata.normalize` 悄悄放行。
|
|
151
|
+
- **残点①(两侧同判只在语料上成立)→ 本批收口(2026-09-30 裁定 ①-(c))**:判据换成**纯区块区间成员判定**——唯一真源 `hive/id_charset_blocks.txt`(Rust `include_str!` 编译期嵌入 + 惰性解析;`mcp_server.py`、`md_cg/units.py` 读同一份文件),**不查任何 Unicode 属性库** ⇒ 两侧接受集由**同一份数据**决定,版本差**结构性不可能**。守卫随之从「方向安全」升到「**零分歧**」:两侧各自的「**判据 ≡ 表**」全码点遍历(限测试内,不进生产热路径)合起来即两侧零分歧,不再需要喂真 `hive.exe` 逐码点的 44 分钟路径。旧实测的 9713 个 Cn 残余随旧的 `is_alphanumeric` 口径一并退场。
|
|
152
|
+
- **残点②(第三份闸未同步)→ 本批收口(2026-09-30 裁定 ②-(a):同口径)**:`md_cg/units.py::_valid_job_id`(N178,`复制契约不 import` 的家法)的 ASCII 白名单换成**读同一份区块表**(**读数据不是 import**,不破零依赖家法),**保留**其本地既有放宽——**不要求 `h` 前缀**(poll 面向任意宿主派发器写出的 job 目录)、允许 `.` 与 `-`(既有对外契约,不为「同口径」收紧)——连同长度上限 128、首尾无空白、首尾非点等既有结构判据。放宽带来的静默风险**显式挡住**:零宽/双向控制/控制字符逐条显式拒(判据下 `Cf`/`Cc` 天然不入,但不靠这个)。新增守卫:md_cg 侧对 `hive/id_contract_corpus_v2.txt` **逐例对照**,与 `job.rs` **每一例同判**(语料里带 `h` 前缀的样例才适用于 md_cg 面的前缀差异,其余按「md_cg 不要求前缀」的有意差异处理并写进断言注释,**不改语料**)。三处调用点(submit 落点前 / poll / wait fail-fast)语义不变。
|
|
153
|
+
- ~~残点③(编号用尽文案)~~ **已收口(2026-09-30)**:分配器不再把「起点越界」当用尽——搜索序改为**在号位间回绕**(先 `hint..=MAX_UNIT_SEQ`、再 `1..hint`;两区间不重叠、并集恰为 `1..=9999`、每号位至多试一次),池内只存 `…_9999` 时仍能分到空闲的 `…_0001`(不再白扔 9998 个号位);**编号值不回绕**(恒 4 位定宽、用尽显式报错、不溢出成 `0001` 复用已发布 id)。文案如实:`编号用尽: 前缀 {prefix} 的 9999 个号位已全部被占用(4 位定宽、不自动加宽)——请换任务槽或另起单元`(保留「末次碰撞 …」,去掉会被读成「前面也满了」的「已无可用编号」);用尽与否是**可证事实**(循环跑完 = 全部 9999 个号位的 `create_dir` 都返回 `AlreadyExists`)。**落点**:`hive/src/job.rs::seq_candidates` + `alloc_job_id` 的循环与文案 + `next_seq_hint` 的「起点越界 ≠ 用尽」语义;守卫 `hive/test_id_contract_v2.py` B 组(含**真构造 9999 个号位**的「真用尽」例)+ `--branch-baseline` 的三处 rust 定点变异(回绕退场 / 静默加宽 / 文案回退)。
|
|
154
|
+
- ~~残点④(既有编码守卫的成功路径覆盖)~~ **已收口(2026-09-30)**:两条守卫各**补一趟成功路径**,原失败趟**保留**(两趟并存)。成功趟把 `HIVE_EXE` 指向真实 release 二进制、`HIVE_JOBS_DIR` 指向守卫**自建临时池**,断言 `ok=true` + `job_id` 为四槽中文形态 `h_…_…_…_NNNN` + 任务目录落临时池内 + 未拉起 serve(`alloc-id` 是纯分配通道,不启动 serve);二进制缺失(未构建的新检出)**显式 SKIP 并打印期望路径**,计入 SKIP 计数、**不计入通过数**、汇总另打 ⚠ 行(不静默判绿)。**落点**:`scripts/test_utf8_boot_guard.py` 的 `E2E-HIVE-OK`(`check_e2e_hive_ok` + `Skipped` 三态 + 自我锚)与 `md_cg/test_issue39_utf8_stdio.py` 的成功趟(2e–2k + `skip()` 三态 + `_hive_bin` 两级查找)。该路径另由 `hive/test_orch.py`(真实 `_hm._submit`)与 `hive/hive_mcp/smoke_test.py` 覆盖。
|
|
155
|
+
- ~~残点⑤(B4 措辞歧义)~~ **已收口(2026-09-30)**:「单独的 `.` 与 `..`」明确为**段级**读法——按 `_` 分段,**任一段**等于 `.` 或 `..` 即拒(整串与尾点判据一并保留),三处闸**同判**(`job.rs::valid_job_id` / `mcp_server._job_id_reject_reason` / `md_cg/units.py::_valid_job_id`);范围**只加这一条**(不拒空段 `h__x` 与 `...` 段等未被裁定的形态)。**落点**:三处闸 + 守卫 `hive/test_job_id_segment_dot.py`(A–D 组 49 条断言 + `--branch-baseline` 三处定点变异 + 三处假阳性对照)+ 语料 `hive/id_contract_corpus_v2.txt` 第 103–104 行两条 reject 例(`h_.._x` / `h_a_.._b`,hex 编码)。存量零迁移实测:在役 `hive/jobs` 15 个目录被三处闸判非法者 **0**。
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
### 四.9 使用者裁定(2026-09-30:追认一项、否决一项)
|
|
161
|
+
|
|
162
|
+
- **md_cg 派发面的 Windows 保留设备名拒收 → 追认保留**。本批换面时因「语料逐例同判(c10)」被迫在 `md_cg/units.py::_valid_job_id` 补入设备名拒收(整 id 首个 `.` 之前部分,以及按 `_` 分段的任一段,ASCII 大小写不敏感;语料第 87–91 行五例为触发器)。旧 ASCII 白名单下 `CON`/`con`/`COM1`/`LPT9`/`CON.txt` 是**放行**的,故这是对该面**对外契约的收紧**——使用者裁定保留(与 hive 两侧同集同判正是 ②-(a) 的目的)。实测存量无损伤(在役 `hive/jobs` 15 个目录被该闸拒者 0)。**已知边界**:非 Windows 宿主上该判据纯属多余的严;段级判据严于 Win32 实际语义(设备名解析按整条路径分量做,`X_CON_Y` 作单个目录名不会命中)。**若日后回退,须同时把语料第 87–91 行改标为「md_cg 面有意差异」**。
|
|
163
|
+
- **R4「非追踪件面」瞬时红 → 不立项查证**(使用者裁定)。判据:发布面(`package.json` 的 `files` = `lib, src, md_cg, data/policy.json, skills, README.md, dsh, codebuddy, zcode, docs`)内出现未被 git 追踪的文件即红(`scripts/check_publish_artifact.py:46` `ALLOW_NONTRACKED=("lib/",)`;明细行的「允许」是**落在 `lib/` 的件数、不是上限**)。当前读数 `非追踪=28,允许=28`,28 件全为 `lib/` 构建产物。执行者曾两次记录瞬时红(清单 1444、命中 `docs/plans/蜂巢模型密钥配置面_v1.0.md`),复核员与编排侧多次实跑**均未复现**,且该文件经 `git ls-files` 确认**被追踪** ⇒ 与「未追踪」不符,成因未定。**不做专项查证**;若日后复现,须**逐字留档 R4 那一段**(含它点名的越界件与 `非追踪/允许` 两层计数),因为本次缺的正是那份日志。
|
|
164
|
+
|
|
165
|
+
## 五 裁决记录
|
|
166
|
+
|
|
167
|
+
**已签收(使用者 2026-09-29)**:
|
|
168
|
+
|
|
169
|
+
| # | 项 | 裁决 |
|
|
170
|
+
|:--|:--|:--|
|
|
171
|
+
| 1 | 编号位宽与溢出 | **4 位**(每单元上限 9999);溢出 **显式报错**,不自动加宽 |
|
|
172
|
+
| 2 | 身份/任务/单元是否必填 | **必填**;单元槽取**蜂巢五单元**闭集(真源 `identity.POSITIONS`) |
|
|
173
|
+
| 3 | 分配器的唯一实现 | **Rust 侧实现**(MCP/编排面调用它,不自持第二份) |
|
|
174
|
+
| 4 | §四.6 控制台 | **不做控制台兼容**:不投 Rust `WriteConsoleW`、不要使用者 `chcp`;取用通道固定走 python + README 明写全 UTF-8 约定(详见 §四.6) |
|
|
175
|
+
|
|
176
|
+
**因此确定的实现分工**:Rust 侧新增「分配原语」(按 `<身份>_<任务>_<单元>` 前缀 + 独占创建选编号),并同时是字符集闸与归一化的单点;`hive/hive_mcp/mcp_server.py` 与 `hive/orch.py` 走它,不再自造 id(`mcp_server.py:339` 现有 `h{ms}_{uuid4[:6]}` 随之退场)。
|
|
177
|
+
|
|
178
|
+
**实现前仍需落地的两项前置**(本契约外、但顺序上先于 id 改造):§四.2 进程自保证 UTF-8、§四.3 裸 `open()` 守卫。理由:id 一旦含中文,任何未对齐编码的读写通道都会立刻显形,先把编码面钉死再改 id,可以避免把两类故障混在一起排查。
|
|
179
|
+
|
|
180
|
+
### 五.1 已动码(2026-09-30 落地)
|
|
181
|
+
|
|
182
|
+
**前置(先于 id 改造,已落地)**:§四.2 进程自保证 UTF-8 + §四.3 裸 `open()` 守卫 —— `fa894e32`(入口自保证单点助手 `utf8_boot.ensure_utf8` + 文本模式裸 `open` 守卫 `scripts/verify_open_encoding.py`)、`08c21cd0`(守卫自身接入口自保证,clean env 下退出码不再与违例同码)。
|
|
183
|
+
|
|
184
|
+
**本批(id 契约 v2 + 排序口径)**:
|
|
185
|
+
|
|
186
|
+
| 面 | 落地件 |
|
|
187
|
+
|:--|:--|
|
|
188
|
+
| A 排序口径换真值 | `hive/src/job.rs::list_jobs_by_created`(created_ts 升序、次键 id 字典序、缺/坏值归 `i64::MIN`、**只读**);消费者改接同一单点:`main.rs::find_active_by_hash`/`cmd_poll`/`cmd_doctor`、`scheduler.rs` 领取段/`recover_orphans`/`scan_corrupt`、`hive/hive_mcp/mcp_server.py::_t_poll`/`_t_doctor`。`list_jobs` **未改**(仍是名升序,A4)。保序/按真值证明:`job.rs` 单测 `list_jobs_by_created_preserves_legacy_order`、`…_sorts_by_truth_not_name`、`…_handles_missing_and_bad_ts` |
|
|
189
|
+
| B1–B3 分配器 | `job.rs::alloc_job_id`(独占创建即分配、4 位定宽、`MAX_UNIT_SEQ=9999` 越界显式报错、不加宽不回绕)+ `init_job_with_slots`(写序单点);CLI `hive alloc-id` |
|
|
190
|
+
| B2 五单元闭集 | `job.rs::UNITS` / `unit_canonical`(英文键或中文名都收、落 id 一律中文名),与真源 `md_cg/identity.py::POSITIONS` 同源断言钉死(`job.rs::tests::units_match_identity_positions`) |
|
|
191
|
+
| B4/B5 字符集闸 | `job.rs::valid_job_id`(放开时= Unicode 字母/数字 + `_`;拒收路径成分/首尾空白/尾点/控制/零宽/双向控制/保留设备名/非 NFC 稳定形态)与 `mcp_server.py::_valid_job_id` **孪生同改**;跨语言对照语料 `hive/id_contract_corpus_v2.txt`(54 例,Hex 编码防不可见样本被改坏),两侧共读同一份。(**字符集判据本身已于 2026-09-30 换面为区块白名单**,见 §四.4/§四.8 残点①;本行留作批次一的动作记录) |
|
|
192
|
+
| B7/B8 提交面 | CLI `hive submit --identity/--task/--unit`(env 兜底 `HIVE_JOB_IDENTITY/HIVE_JOB_TASK/HIVE_JOB_UNIT`)+ `hive alloc-id`;`mcp_server.py` 的 `hive_spawn` 三槽参数**必填**、`_submit` **退场自造 id**(改调 `HIVE_EXE alloc-id`,不可用即显式报错);`orch.py::spawn_subtask` 三槽透传(显式 > 编排者 spec 同名键,缺则报错,不兜底造 id);schema / `README.md` / `hive/README.md` 同步 |
|
|
193
|
+
| B9 断言同步 | `hive/src/job.rs` 合法/非法样态断言(合法样本含四槽中文 id)、`hive/hive_mcp/smoke_test.py` 的 `startswith("h")` 断言改为四槽中文 id + 4 位定宽 |
|
|
194
|
+
| C 论证订正 | 五处「无环性由 job_id 时间序结构性保证」订正为**存在性闸**(`main.rs` 的 depends_on 存在性检查 + MCP 侧 `_dep_gate` + 运行期 `scheduler.rs::deps_gate`),并注明「旧形态 id 恰好也带时间序、新形态不再有此性质,故论证不得依赖它」。同类措辞一并订正(C2):`hive/README.md`(depends_on 行)、`md_cg/sources.py::HiveJobsSource`(「job_id 名升序 = 时间升序」的代理口径)、`md_cg/units.py::_job_id` 的 docstring(「同 `_submit`」已失实)。**逐处落点(按内容搜,非旧行号)**:`scheduler.rs::deps_gate` 头注、`spec.rs::Spec.depends_on` 头注、`main.rs` 提交面 depends_on 块、`mcp_server.py::_dep_gate` 头注;**第五处(`job.rs` 原 `new_job_id` doc 的「I-1 拓扑序无环性的结构性根基」)随 B7 该函数退场而删除**,订正后的论证落在 `job.rs` 的「id 契约 v2」头注(同处记明被删的原文)。三闸均由**运行期代码坐实**:`main.rs` 的 `jobs.join(dep).is_dir()`(存在性)、`mcp_server._dep_gate` 的 `os.path.isdir(os.path.join(jobs, d))`(存在性,`_valid_job_id` 先行)、`scheduler.rs::deps_gate` 的 `job::valid_job_id` + `ddir.is_dir()`(运行期复核)。C2 全仓内容搜(「时间序」「结构性保证」「名序」「拓扑序」「无环」)另订正 `job.rs::list_jobs` 头注(原「名升序 = 时间升序,job_id 含毫秒时间戳保证」在 B7 后失实)与两处**历史文档**(`docs/hive/蜂巢M6_ingest实施计划_v0.1.md` 的 T1 依据、`docs/eval/故障注入实测_v1.0.md` 的 FI-R06)——后者是留档实测记录,**只在文末追加带日期的订正留痕、不改原记录字节**(其行号被 `docs/eval/缺陷挖掘_自主迭代_v19.md` 按 `:192-193`/`:416` 引用过,故不内联插入) |
|
|
195
|
+
| D 守卫 | 新增 `hive/test_id_contract_v2.py`(A–I 九组 **307 条断言** + `--branch-baseline` **12 处定点变异 + 1 处假阳性对照**(红项集/退出码**逐项实测后写死**,2026-09-30)+ `--head-baseline` 红基线(**逐条谓词用各自的批次锚点**:10 条谓词在锚点上必红、在工作区上必绿)。**c13/c14 同轮升级**:C 组新增第三处读者(`md_cg/units.py`)的「判据 ≡ 同一张表」全码点遍历(C8e/C8f/C8h)与 Rust 侧 fail-closed 单测在位核验(C8g);变异面新增**数据面**(表删一段 / 多塞一段:Python 内存副本 + Rust 临时 crate 副本的表文件)、**解析失败改 fail-open**、**md_cg 退回 ASCII 白名单**;守卫自身 UTF-8 自保证经 clean env(无 `PYTHONUTF8`、无 `-X utf8`)实测:**带锚 rc=0;去掉锚 rc=1 且 `UnicodeEncodeError`**(与违例同码,正是 c14 要防的形态)) |
|
|
196
|
+
|
|
197
|
+
**本批两处实现级裁决**(均按 §五 的「严于必需且两侧可证同判」取舍):
|
|
198
|
+
|
|
199
|
+
1. **B5 的 NFC = 拒收,而非归一化**。§四.1 原表述为「归一化在写入边界执行一次」;实现层收窄为「**须已是 NFC 稳定形态,否则拒收**」。理由:`hive/Cargo.toml` 的 `[dependencies]` 是空段(零依赖 D-005),手写完整 NFC 不可行;**拒收严于静默归一化**(后者会把不同字节静默改成同一 id,掩盖提交方的编码错误),且拒收判据可用有限的「会改写区块」闭集在两侧逐区间对齐、可证同判(`job.rs::NFC_REWRITE_BLOCKS` ↔ `mcp_server.py::NFC_REWRITE_BLOCKS`,守卫逐区间比对)。**残余边界(已实测、已声明)**:两侧的「字母数字」判据各随自己的 Unicode 表演化(Rust 工具链 vs Python `unicodedata`)——实测(2026-09-30,Python `unicodedata` 15.0.0 × 本机 rust 工具链):Python 判「未分配(Cn)」的 825345 个码点中,Rust 收而 Python 拒的 **9713 个(56 区间,即 Unicode 15.1/16 新增字母,如 Todhri / CJK 扩展 I)**。该残余**方向安全**(Python 永不超过 Rust 的受理面 ⇒ 越权面为零),且不嵌静态表(表会随 Unicode 版本陈化且无廉价陈化检测,收益为零);守卫 `test_id_contract_v2.py` 的 C8 以**全码点批量+二分**完备证明「Python 收 ⟹ Rust 收」,C9 逐轮实测残余的定性(只许落在 Python-Cn 上)与方向,`--cn-sweep` 提供残余重导入口(≈44 分钟)。另 `Other_Alphabetic` 一类(Mn/Mc/So 共 1405 码点)已由实测导出表补齐,**语义分歧已消除**(该表由守卫 C7 每轮以真 exe 重导比对,陈化即红)。
|
|
200
|
+
> **(2026-09-30 订正留痕 · 裁定 ①-(c))本条的机制叙述已被取代**:`NFC_REWRITE_BLOCKS` 闭集、`Other_Alphabetic` 补齐表与 Cn 残余声明**整段退休**(不许新旧两套并存),判据换成 `hive/id_charset_blocks.txt`(唯一真源,三处共读,**不查任何 Unicode 属性库**);正文里的 9713 残余、C8/C9 与 `--cn-sweep` 的「44 分钟必需性」表述随之退场,改由两侧各自的「判据 ≡ 表」全码点遍历(零分歧)承载。原文保留作批次一(提交 `15fb2bcd`)的历史记录,现状口径以 §四.4 与 §四.8 为准。
|
|
201
|
+
|
|
202
|
+
2. **B8 的四槽接口名与 `alloc-id` 子命令**。CLI 旗标 `--identity/--task/--unit`(env `HIVE_JOB_IDENTITY/HIVE_JOB_TASK/HIVE_JOB_UNIT`);新增子命令 **`hive alloc-id --identity X --task Y --unit Z`**(只分配、打印 id,**目录即分配凭证**;不写 spec/status);MCP 工具 `hive_spawn` 参数 `identity/task/unit`(必填,不落 spec);内部函数名 `job::alloc_job_id(jobs, identity, task, unit)` / `job::init_job_with_slots(...)` / `mcp_server._alloc_job_id` / `mcp_server._submit(jobs, spec, identity, task, unit)`。失败一律非 0 退出 / `ok:false` 且带可读原因(缺槽错误文本含四槽名与一个可照抄示例)。
|
|
203
|
+
|
|
204
|
+
### 五.2 本批发现但**未动码**的残余(如实登记,不静默)
|
|
205
|
+
|
|
206
|
+
| # | 位置 | 事实 | 处置 |
|
|
207
|
+
|:--|:--|:--|:--|
|
|
208
|
+
| 1 | `md_cg/units.py::_job_id`(`job_id()` 在 `:342` 被消费) | 存在**第三处** id 生成器 `h<毫秒>_<uuid6>`——契约 §四.7 只点名 `mcp_server.py:339`,B8 的改动面也只列 CLI/MCP 提交面/orch 透传;**md_cg 复核派发面不在其中** | 未改码。旧形态 id 在新契约下仍合法(存量零迁移)⇒ 不闯闸、不破坏领取;但与「分配器唯一实现在 Rust 侧」(§五 裁决 3)不齐。已在函数 docstring 里如实标注(C2 同类措辞订正),留待单独裁决(牵动 md_cg 派发/等待链语义与 `md_cg/test_units_poll.py`) |
|
|
209
|
+
| 2 | `md_cg/sources.py::HiveJobsSource._jobs()` | 按**名升序**枚举 job 目录、原 docstring 断言「名序 = 时间序」(契约 v2 起失实:只有旧形态 id 才有该巧合) | 已订正 docstring(C2);**代码未改**(名序仍确定,`(session, seq)` 去重键跨轮稳定,仅不再等于时序)。若要真时间序须改走 `created_ts` 真值——牵动 md_cg 摄入链,留待单独裁决 |
|
|
210
|
+
| 3 | ~~两侧 Unicode 表版本差~~ **已收口(2026-09-30 裁定 ①-(c),见 §四.8 残点①)** | 旧口径(`is_alphanumeric` 属性判定)下 Python 判「未分配(Cn)」而 Rust 收的码点——判据**已换成同一份区块表**,两侧接受集由同一份数据决定,版本差**结构性不可能** | 该残余与 `--cn-sweep`(≈44 分钟路径)**一并退场**(不留重导入口:无属性表可陈化);改由两侧各自的「**判据 ≡ 表**」全码点遍历(零分歧)+ `--branch-baseline` 的 12 处定点变异(含数据面删/加区间、解析失败改 fail-open)承载 |
|
|
211
|
+
| 4 | `scripts/test_utf8_boot_guard.py::check_e2e_hive`、`md_cg/test_issue39_utf8_stdio.py` 的 2b/2d | 这两条探针原以「中文 `hive_spawn` 返回 ok+job_id」为证据;其隔离设计把 `HIVE_EXE` 钉在**不存在的路径**(绝不拉起真 serve),而 `_submit` 自 B8 起需真二进制 ⇒ 该成功路径在其面**结构性不可达** | 已改判为**更强**的编码判据:断言 `ok=false` 且原因里**逐字节回显**请求中的中文路径(且错误不是「缺四槽」⇒ 中文入参已过 JSON 解码与四槽闸)。改动已在两文件内**显式声明**,非静默放宽 |
|
|
212
|
+
|
|
213
|
+
---
|
|
214
|
+
|
|
215
|
+
## 六 不适用条件
|
|
216
|
+
|
|
217
|
+
- 本稿不覆盖插件宿主与前端渲染的编码面;只论本仓三面(Rust 内核、Python 认知图与执行器、TS 桥)。
|
|
218
|
+
- 存量库与存量任务的**内容**不在范围内(只论路径、文件名与任务标识);需要搬动既有存量数据的条目另开批次。
|
|
219
|
+
- 归一化形态一旦定为 NFC,原本按 NFD 存储的存量名字**不做自动迁移**——那需要单独的迁移方案与中止条件。
|
|
220
|
+
- 本稿不含实现;签名前不动码。
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
# 真源索引(通用机制)· N203 的替代路线 v0.3
|
|
2
|
+
|
|
3
|
+
> 状态:**裁定齐备,可开工**(三条裁决 + 一条简化指示,均 2026-09-28 使用者给出)
|
|
4
|
+
> 生效范围:v0.3 取代 v0.1/v0.2(v0.2 的两条待裁决按本条指示**撤回**,见 §12)。
|
|
5
|
+
> 使用者裁定与指示原文:
|
|
6
|
+
> · 「203 我们不改 dsh 的日志,我们单独建立记忆索引,同样使用认知图读,然后通过索引找日志。
|
|
7
|
+
> 这个方案是通用的,任何信息真源都能这样索引。」
|
|
8
|
+
> · 「①指针落点:真源身份指向 zstd 日志、区间指向确定性转写。②旧节点处置:按 §5 第 3 步软退休。
|
|
9
|
+
> ③一次把外部真源也纳入首期,文本,图像,语音,视频…所有的文件都可以兼容,建立索引。」
|
|
10
|
+
> · 「索引使用文件名,路径,类型即可。任意文件通用,可读文本内容才细化摘要简介和补 ccg 条件检索,
|
|
11
|
+
> 而且这些多模态文件,往往会伴随可读文本简介。」
|
|
12
|
+
|
|
13
|
+
## 0. 为什么这条路比修 id 更对
|
|
14
|
+
|
|
15
|
+
N203 的现象是「日志索引节点 id 位置化 ⇒ 中途插话即永久错位 + 静默丢索引」。但位置化
|
|
16
|
+
id 只是**症状**:真正的根因是那条路**把真源正文拷进节点**(`scripts/dsh_log_index.py:575`
|
|
17
|
+
自陈「节点 id = dsh-log-<派生标识>-<章节序号>」,且模块头注明**不写 `doc_ref` / 不登记
|
|
18
|
+
Ledger**)。既然节点是正文的**拷贝**,它就只能靠「序号」这种脆弱身份认领自己;真源一变,
|
|
19
|
+
拷贝与身份一起失效,而 `if nid in nodes: skip`(`:611-613`)把失效表现成静默。
|
|
20
|
+
|
|
21
|
+
改 id 只是在拷贝模型里打补丁。使用者给的路线是换模型:**节点不存正文,存指向真源的索引
|
|
22
|
+
条目**——真源零改动,索引可重建,读的时候经索引回到真源。
|
|
23
|
+
|
|
24
|
+
**旁证(现场发现)**:`scripts/dsh_log_index.py:575` 的 docstring 自陈「节点 id =
|
|
25
|
+
`dsh-log-<派生标识>-<章节序号>`(**内容寻址**,跨重启幂等)」,而 `:610` 的实现是
|
|
26
|
+
`nid = f"dsh-log-{token}-{i:04d}"`——**同一条句子里「章节序号」与「内容寻址」互相矛盾**:
|
|
27
|
+
意图是内容寻址,落地退化成了序号。
|
|
28
|
+
|
|
29
|
+
**本方案不把它做成内容寻址**(选 B:对齐口径 + 守卫,不改实现):id 由
|
|
30
|
+
`docindex.node_id` / `codeindex.node_id` 生成,是 `path#heading_path` 的
|
|
31
|
+
**路径-标题寻址(位置化)**——不含 root、不含正文;幂等与防静默**不靠 id**,由
|
|
32
|
+
「区间哈希 + 行位全等」承担(`md_cg/logref.py:119-128` 自陈:`hash` 与 `lineno/end`
|
|
33
|
+
全等才算已索引)。真正的文件级内容寻址在本仓是 `srcindex.src_id` 的 `file_hash`
|
|
34
|
+
(`md_cg/srcindex.py:71` 的 `src_id` 与 `md_cg/srcindex.py:86` 的 `file_hash`),
|
|
35
|
+
不是本层的 id。
|
|
36
|
+
|
|
37
|
+
## 1. 机制在仓内已存在(关键事实,勿重造)
|
|
38
|
+
|
|
39
|
+
`md_cg/refindex.py` 已经是这套东西的通用实现:
|
|
40
|
+
|
|
41
|
+
| 能力 | 现成实现 |
|
|
42
|
+
|---|---|
|
|
43
|
+
| 统一 ref 协议(指向真源的指针) | 节点 frontmatter `code_ref` / `doc_ref`(path / root / heading / level / lineno / end / anchor / **hash**)——`refindex.py:445` 与 `refindex.py:459` 两组 `_ref()` 构造 |
|
|
44
|
+
| 统一的索引节点 id(**路径-标题寻址**,位置化) | `docindex.node_id(it)` / `codeindex.node_id(it)`(`refindex.add_items` 内统一调用);要**内容寻址**的参照是 `srcindex.src_id` 的 `file_hash`(`md_cg/srcindex.py:71`、`md_cg/srcindex.py:86`) |
|
|
45
|
+
| 区间哈希(漂移判定) | `codeindex.region_hash(lines, lineno, end)`(调用点 `refindex.py:503`) |
|
|
46
|
+
| 按区间**回读真源**(单点) | `read_ref(ref, root=…)` → ok/stale/dangling + `text`(`refindex.py:520`) |
|
|
47
|
+
| 漂移 / 悬空巡检(只读、不抛、不改源) | `check_refs(cg, ledger=…)`(`refindex.py:588+`);状态集 `ok/stale/dangling/unresolved/error` |
|
|
48
|
+
| 增量水位(不全量重扫) | `Ledger` + `<root>/_refindex.json`(源文件绝对路径为键,记 size/mtime + 区间) |
|
|
49
|
+
| 切片 | `docindex.extract(source, path, suffix)` → 每项自带 `lineno/end/hash/heading/anchor/level` |
|
|
50
|
+
|
|
51
|
+
⇒ **本方案不新造协议,只补一层「源适配器」**:把「任意真源」变成 refindex 能吃的「带区间的可寻址材料」。
|
|
52
|
+
|
|
53
|
+
## 2. 三层结构(本方案)
|
|
54
|
+
|
|
55
|
+
```
|
|
56
|
+
① 源适配器(Adapter) 真源 → 可寻址材料 + 区间表
|
|
57
|
+
发现 discover() / 身份 identity() / 物化 materialize() / 密级 sensitivity()
|
|
58
|
+
② 索引(认知图 ref 节点) 一条 ref = 一个区间;id 路径-标题寻址(位置化,不含 root/正文);
|
|
59
|
+
漂移与幂等由「区间哈希 + 行位」承担;正文可留摘要/标题(不留全量拷贝)
|
|
60
|
+
③ 回读(单点) read_ref(区间) → 真源正文;check_refs → stale/dangling
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
**两层定位(本方案相对普通 doc_ref 的唯一扩展)**:日志的真源是 zstd JSONL,而可读区间落在
|
|
64
|
+
**确定性转写**上,所以 ref 要同时记:
|
|
65
|
+
|
|
66
|
+
- `src_*`:真源身份 —— 真源绝对路径 + 会话 uuid + workspace + **真源哈希**(staleness 判在真源上);
|
|
67
|
+
- `span`:可读载体区间 —— 转写文件的 `lineno/end/hash`(回读落在转写上,复用既有 `read_ref`)。
|
|
68
|
+
|
|
69
|
+
这样「真源被删/被改」能报 `dangling/stale`,「转写重新生成」也不会让节点认错自己。
|
|
70
|
+
|
|
71
|
+
## 3. 为什么这同时消灭 N203 的两个后果
|
|
72
|
+
|
|
73
|
+
| N203 后果 | 新机制下的表现 |
|
|
74
|
+
|---|---|
|
|
75
|
+
| 中途插话 ⇒ 序号平移 ⇒ 整段错位 | **防静默不靠 id**:`docindex.node_id` 是 `path#heading_path` 寻址(`docindex.py:294-299`),同标题换正文时 **id 不变而 hash 变**(P1 实跑 `md_cg/test_logref.py` 守卫②)⇒ 判据必须是「hash + 行位全等才算已索引」。旧节点:**真源层**保留 `doc_ref.src.file_hash` 水位、可判 stale;**转写层**同 id 按 hash 覆写并计数(转写是同路径暂存物,被重生成后旧区间不保留) |
|
|
76
|
+
| `if nid in nodes: skip` ⇒ 静默丢索引 | 重跑幂等的判据从「id 撞了就跳」改成「区间哈希 + 行位全等才算已索引」;不一致即覆写并在报告里计数(不再静默)——变异回「id 撞即跳」后 `updated==0` 而区间哈希复核出现不一致,守卫必转红 |
|
|
77
|
+
|
|
78
|
+
### 3.1 id 语义与设计债(选 B:对齐口径 + 守卫,不改实现)
|
|
79
|
+
|
|
80
|
+
**id 是什么**:`code_` = `sha1(路径::符号名)`、`doc_` = `sha1(路径#标题路径)`(各取前 12 位)
|
|
81
|
+
——**路径-标题寻址(位置化)**,
|
|
82
|
+
不含 root、不含正文。改 id 的方案被否,因为 id 同时是**节点文件名**
|
|
83
|
+
(`pooling.py:39`、`:81` 的前缀分流)与**外键**(`_index.json` 快照、`_link.jsonl`、
|
|
84
|
+
access 日志、删除清单、`refindex` 与 `sources` 的多处引用);既然真做内容寻址必须让正文进键,
|
|
85
|
+
后果是「改一行正文即换身份 ⇒ 外键全断 + 全库重命名 + 契约变更」,**不可有界**。
|
|
86
|
+
收益已由「区间哈希 + 行位」承担,故本期只把口径写正、把现状钉进守卫。
|
|
87
|
+
|
|
88
|
+
三条已知债(**不是**缺陷清单,是选 B 已知并接受的代价):
|
|
89
|
+
|
|
90
|
+
| 债 | 内容 | 现状与可见性 |
|
|
91
|
+
|---|---|---|
|
|
92
|
+
| **D1** | id 不含 root ⇒ 跨大域**同 rel path + 同标题**撞 id;`add_items` 同 id upsert 整条替换 ⇒ 节点只认**后写域**,`rebuild` 只按节点 `ref.root` 分组 ⇒ **先写域不再被重建** | 撞 id 现场实测:两域 id 相同、该 id 只留一条节点、`ref.root`=后写域(`md_cg/test_p28_refcheck.py` 的「撞 id 现场」组钉住)。**新增可见性**(`check_refs` 快路径 v2):该状态产出 `ledger_mismatch` 归因行、`kind_of_gap=collision_multi_root`(带 `watermark_root` / `node_root`)——不再是静默;恢复=重跑该域的 `index_code` / `index_doc` |
|
|
93
|
+
| **D2** | id 不含正文 ⇒ **不得**以「id 存在即已索引」作判据 | 判据一律走 `logref.node_state`(hash + 行位全等)或 `refindex.check_refs`;新落库路径禁止自建「id 撞即跳」(N203 的静默就是它) |
|
|
94
|
+
| **D3** | dup 后缀(`docindex.py:297-298`)是位置化键的补丁 | 插 / 删同标题章节 ⇒ 该文档 id 全量重算,旧代靠 `prune_orphans`(`refindex.py:784`)清退;幂等性靠「同 id 原子覆盖 + 旧代清退」,不靠 id 稳定 |
|
|
95
|
+
|
|
96
|
+
**巡检侧的分工**(`md_cg/refindex.py` 的 `check_refs`,与本节同源):漂移 / 悬空**只由节点自身
|
|
97
|
+
frontmatter ref 判**;水位(`_refindex.json`)只判「能不能跳过」(节点 ref 与水位条目
|
|
98
|
+
`(hash, lineno, end, path, root)` 五项全等 + 源 size/mtime 未变 + 同 id 只判一次)。五项含
|
|
99
|
+
`path`/`root`:否则 D1 状态在稳态不可见、且不一致会重复报红。不一致只记归因行,**不改判定**。
|
|
100
|
+
|
|
101
|
+
## 4. 通用化边界(使用者要的「任何真源都能这样索引」)
|
|
102
|
+
|
|
103
|
+
| 真源形态 | 适配器 | 区间落点 | 状态 |
|
|
104
|
+
|---|---|---|---|
|
|
105
|
+
| 代码 / 文档(仓内文件) | 已有(codeindex / docindex) | 文件行区间 | **现成** |
|
|
106
|
+
| DSH 会话日志(zstd JSONL) | 新增(复用 `dsh_log_index` 的转写与章节切分) | 转写文件行区间 + 真源身份 | **本方案** |
|
|
107
|
+
| 工作区文档(大域) | 已有(`workspace_index.py` / doc_ref 多 root) | 文件行区间 | 现成 |
|
|
108
|
+
| 外部真源(URL / API / 远端库) | 需新适配器(拉取 → 本地缓存 → 行/字段区间 + 内容哈希) | 缓存物化件区间 + 远端身份(ETag/版本) | **后续**(未含在本方案首期) |
|
|
109
|
+
| 不可寻址真源(一次性管道 / 无稳定路径) | 不适用 | —— | 见 §8 不适用条件:退回拷贝模式 |
|
|
110
|
+
|
|
111
|
+
## 5. 迁移(真源零改动,节点面三步)
|
|
112
|
+
|
|
113
|
+
1. **新建**:按 §2 建立 ref 索引(首批 = 现有 DSH 会话),写进**临时库**先对账,不碰在役库;
|
|
114
|
+
2. **对账**:同一批会话在「旧位置化节点」与「新 ref 节点」上做内容比对(区间回读文本 vs 旧节点正文),确认零遗漏、零错位(这一步是 第5条 的固化解);
|
|
115
|
+
3. **退休**:对账过后把旧位置化 `dsh-log-*` 节点**软退休**(`forget` 移 trash + 留痕,可 restore),不物理删除;**DSH 日志本身及其摄取脚本的读写语义不动**。
|
|
116
|
+
|
|
117
|
+
## 6. 守卫(新机制必须自带判别力)
|
|
118
|
+
|
|
119
|
+
守卫落成件:`md_cg/test_logref.py`(P1 起并入 `scripts/linux_verify.sh` 门禁)。
|
|
120
|
+
|
|
121
|
+
| 守卫 | 判据 | 红基线做法 |
|
|
122
|
+
|---|---|---|
|
|
123
|
+
| 幂等 | 同源重跑 → `updated == 0`、**节点 `doc_ref` 与当前源区间逐条一致**(区间哈希全 ok) | 定点变异把「hash+行位全等才算已索引」改回「id 撞即跳」→ 在**内容已变**的源上 `updated==0` 而区间哈希复核出现不一致 ⇒ 必转红(守卫①②) |
|
|
124
|
+
| 插话场景(两代构造) | v1 旧式 → 插话 v2:`missing/misplaced/roundtrip` 全 0、`updated == updated_expected`;**剔除根章**(最外层章 end=全文行数,任何改动都让它变)后旧式错位槽 ≥ 2——为 0 即报「对账无判别力」并失败 | 构造插入版源(插入点必须在**首条 user 消息之后**,否则改 `derive_session_token` 的 basis 会换掉旧式 id 空间),断言零静默跳过(守卫③) |
|
|
125
|
+
| 悬空 | 删**真源** → src 层 dangling;删**转写** → 区间层 dangling 而 **src 层仍 ok**(两层解耦,各自独立可判);带 `regenerable` 的节点不被 `prune_dangling` 列入(计数透出),非 regenerable 的悬空节点**必须**被列入 | 临时目录删源/删转写,断言状态迁移与清退名单(守卫④) |
|
|
126
|
+
| 回读逐字节 | `read_ref(区间)` 的文本 == **转写**同区间切片 | 与 `docindex.extract` 同源断言(守卫③) |
|
|
127
|
+
| 密级跟源 | 索引节点密级 == `docindex.sensitivity_for(src["path"], None)` 的**真源路径判**(不得因索引而放宽,且不低于 internal);落进 `crypto.ENCRYPTED_LEVELS` 时**报告必须出现加密告警计数**(否则 v1.0「误标 private → 加密 → 检索零命中」会静默复现) | 私有提示路径的合成真源 → 节点必须 private 且计数 > 0(守卫⑤) |
|
|
128
|
+
| path 口径 | `doc_ref.root` == 转写根、path 为**相对**路径;同 sid 跨 ws 时裸文件名口径必撞 id、`<ws>/<sid>/<sid>.md` 口径零撞(绝对 path 当 rel 在 `probe_ref` 里仍 status=ok = 既有实现不拦,故口径必须由落库侧写死) | 合成同 sid 跨 ws 夹具,断言零撞 id(守卫⑥) |
|
|
129
|
+
|
|
130
|
+
## 7. 分步落地(每步可独立验收,可停)
|
|
131
|
+
|
|
132
|
+
| 步 | 内容 | 验收 |
|
|
133
|
+
|---|---|---|
|
|
134
|
+
| P0 | 适配器接口 + 真源身份/区间表(纯函数,不写库) | 单测:给定日志 → 区间表;篡改一处 → 哈希变化可判 |
|
|
135
|
+
| P1 | ref 索引写入(临时库)+ 对账脚本 —— 落地件:适配器 `srcindex.session_src_id`/`log_units`、桥与读路径 `md_cg/logref.py`、入口 `scripts/_mdcg_reindex_dshlogs.py`(`--reconcile {off,generation,full}` / `--repair 1` / `--src-path {abs,rel,hash}`,**只写 tempfile 库**) | 与旧式位置化节点两代比对报告:新增 > 0、遗漏 = 0、错位 = 0,且旧式错位槽(剔根章)≥ 2(判别力);守卫 `md_cg/test_logref.py` |
|
|
136
|
+
| P2 | 读路径接通(检索命中 → `read_ref` 回读真源区间)—— 落地件:`mcp_server._ref_call` 的 `read/get` 分支对带 `doc_ref.src` 的日志节点改走 `logref.read_index_node`(**区间文本仍由 `refindex.read_ref` 取**,只补 span 状态机与再生指路;非日志节点逐字不变),`with_src` 显式开真源身份层探针 | 端到端:问句 → 命中索引 → 取回原文区间(`md_cg/test_logref.py` 守卫⑦:逐字节 + 幂等 + 插入场景,含生产读面 7l-7n) |
|
|
137
|
+
| P3 | 守卫五条 + 红基线 | 上表全绿且变异必红 |
|
|
138
|
+
| P4 | 旧节点软退休 + 在役库切换 | 退休可 restore;检索面无回退 |
|
|
139
|
+
|
|
140
|
+
## 8. 不适用条件与诚实边界
|
|
141
|
+
|
|
142
|
+
- **不适用于不可寻址真源**(无稳定路径/一次性管道):那时只能退回拷贝模式,并接受拷贝模型的错位风险——本方案不假装覆盖它。
|
|
143
|
+
- **不适用于跨机真源**:指针含绝对路径,跨机失效;要么改用「工作区锚 + 相对路径 + 内容哈希」的自洽校验,要么重新索引。
|
|
144
|
+
- **不解决真源本身的语义漂移**:索引只保证「指得准、读得到、改了知道」;真源内容里的事实是否过期,靠既有 `verification_state` 面,不在本机制内。
|
|
145
|
+
- **不引入外部服务**:仍零第三方依赖,仍走认知图与既有 op 面。
|
|
146
|
+
- **不是「不改 dsh 的日志」的豁免面**:本方案确不动 DSH 日志与其摄取脚本的读写语义;但**新索引路径要新增代码**,这属正常落地,不等于「零改动」。
|
|
147
|
+
|
|
148
|
+
## 9. 三条待裁决 → **已定**(2026-09-28 使用者裁定)
|
|
149
|
+
|
|
150
|
+
| # | 裁定 | 对方案的影响 |
|
|
151
|
+
|---|---|---|
|
|
152
|
+
| 1 | 指针落点=**真源身份指 zstd 日志 / 区间指确定性转写** | §2 的「两层定位」原样采纳(本方案推荐项) |
|
|
153
|
+
| 2 | 旧节点处置=**软退休**(§5 第 3 步) | 迁移第 3 步按「对账过后 forget 移 trash + 留痕 + 可 restore」执行 |
|
|
154
|
+
| 3 | 首期范围=**一次纳入外部真源:文本/图像/语音/视频…所有文件** | 见 §11——区间模型必须按模态泛化,且首期即要求「任何文件类型可兼容」 |
|
|
155
|
+
|
|
156
|
+
## 10. 多模态推广(v0.3 按指示简化):**通用层 + 细化层 + 伴随文本**
|
|
157
|
+
|
|
158
|
+
关键简化:**不必为每种模态设计 locator**。任意文件先用「文件名 / 路径 / 类型」进通用层;
|
|
159
|
+
只有当真源**本身可读文本**时才细化(摘要简介 + CCG 条件检索);而多模态文件**往往自带可读文本
|
|
160
|
+
简介**,语义面就走那条伴随文本,无需解码器、无需模型。
|
|
161
|
+
|
|
162
|
+
### 10.1 通用层(任意文件,零解码、零模型、零第三方依赖)
|
|
163
|
+
|
|
164
|
+
```
|
|
165
|
+
条目 = { path, name, type, size, mtime, file_hash, reader=原文件 }
|
|
166
|
+
span = whole(整文件粒度)
|
|
167
|
+
text_view = 文件名 + 路径 + 类型(+ 若有伴随可读文本,一并挂上——见 §10.3)
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
- **类型**判据=扩展名 + 魔数嗅探(几十字节前缀即可,零依赖);类型只进检索面与分组标签,
|
|
171
|
+
**不决定「能不能索引」**——任何文件都能进通用层。
|
|
172
|
+
- 这一层覆盖文件索引的绝大多数用法:「我知道那个文件叫什么 / 在哪 / 是什么类型」。
|
|
173
|
+
- 哈希用文件级 sha256 ⇒ `stale`(真源被改)与 `dangling`(真源被删)都判得出来,复用 `check_refs`。
|
|
174
|
+
|
|
175
|
+
### 10.2 细化层(仅当真源含可读文本)
|
|
176
|
+
|
|
177
|
+
- **章节切分**:`docindex.extract` → 每节一条 ref(line 区间 + 区间哈希),并产**摘要/简介**;
|
|
178
|
+
- **补 CCG 条件检索**:正文含 `# 生效条件:` 一类 CCG 行的,走既有 `condition_space` 同源机制
|
|
179
|
+
(`refindex.add_items` 已支持),让条件面参与检索与路由——这正是「可读文本才细化」的落点;
|
|
180
|
+
- 不可读文本(图像/音视频/二进制)**不强行细化**:它们停在通用层,靠 §10.3 的伴随文本获得语义面。
|
|
181
|
+
|
|
182
|
+
### 10.3 伴随文本:多模态的语义通道(替代 v0.2 的模型路线)
|
|
183
|
+
|
|
184
|
+
多模态文件在实践中**总是伴随可读文本**(使用者原话:「往往会伴随可读文本简介」):
|
|
185
|
+
|
|
186
|
+
| 伴随形态 | 例 |
|
|
187
|
+
|---|---|
|
|
188
|
+
| 同名 sidecar | `a.png` ↔ `a.md` / `a.txt` / `a.json` / `a.caption` |
|
|
189
|
+
| 目录级说明 | 同目录 `README.md` / `_index.md` / 清单文件 |
|
|
190
|
+
| 文件内嵌文本段 | EXIF/XMP 的 Description/UserComment、ID3 标签、容器 title/comment |
|
|
191
|
+
| 项目清单 | 生成该媒体的脚本/配置/提示词(prompt 文件本身就是最好的简介) |
|
|
192
|
+
|
|
193
|
+
处理方式:**伴随文本本身就是真源**(有自己的路径与哈希)⇒ 照常索引(通用层 + 细化层照走);
|
|
194
|
+
媒体条目与伴随文本条目**互相关联**(同一 `group`/目录锚)。于是语义检索由此进入:
|
|
195
|
+
查「海边日落视频」命中其伴随描述或 prompt 文件 → 经关联回到媒体文件本体。
|
|
196
|
+
|
|
197
|
+
### 10.4 撤回(v0.2 的两条待裁决,按本条指示不再需要)
|
|
198
|
+
|
|
199
|
+
- ~~媒体解码器(ffmpeg)~~ → 通用层是**整文件粒度**,首期不按时间点/区域切片 ⇒ 不需要解码器;
|
|
200
|
+
- ~~为媒体调模型产「派生文本」~~ → 语义面走**伴随文本**(真源自带)⇒ 首期不需要模型;
|
|
201
|
+
仅当某媒体**确无任何伴随文本**又需要语义时才另议(列入 §12 后续可选,不进首期)。
|
|
202
|
+
|
|
203
|
+
## 11. 落地顺序(v0.3)
|
|
204
|
+
|
|
205
|
+
| 步 | 内容 | 验收 |
|
|
206
|
+
|---|---|---|
|
|
207
|
+
| P0 | 契约层:单位(`whole` / `line` 两类即可)+ `src_id`(路径+文件名+类型+文件哈希)+ 适配器接口 | 单测:任意文件可入通用层;改一字节 → 哈希变化可判 |
|
|
208
|
+
| P1 | 文本/日志细化:章节 ref(区间哈希)+ 摘要简介 + CCG 条件面 | 与旧节点对账:遗漏/错位 = 0 |
|
|
209
|
+
| P2 | 读路径:检索命中 → `read_ref` 回读(整文件 / 区间) | 端到端:问句 → 命中 → 取回 |
|
|
210
|
+
| P3 | 通用层全类型扫描(含图像/音视频/二进制走整文件条目) | 覆盖计数与不落项如实报出 |
|
|
211
|
+
| P4 | 伴随文本关联(sidecar / 目录说明 / 内嵌文本段 → `group` 关联) | 关联命中率对账 |
|
|
212
|
+
| P5 | 守卫五条(幂等 / 插话 / 悬空 / 回读逐字节 / 密级跟源)+ 红基线 | 变异必红 |
|
|
213
|
+
| P6 | 旧位置化节点**软退休**(可 restore)+ 在役切换 | 检索面无回退 |
|
|
214
|
+
|
|
215
|
+
## 12. 后续可选(非首期,明确不入本期实现)
|
|
216
|
+
|
|
217
|
+
1. **按时间点/区域切片回读**(音视频时间段、图像区域):需解码器/图像库,属「粗粒度不够用」时再加;
|
|
218
|
+
2. **无伴随文本媒体的语义化**:可用 hive 委托模型产派生文本(派生文本亦是真源、照常索引,密级随真源);
|
|
219
|
+
3. **跨机真源**:指针含绝对路径,跨机须改「工作区锚 + 相对路径 + 内容哈希」。
|
|
220
|
+
|
|
221
|
+
## 13. 不适用条件(沿用 §8,补两条)
|
|
222
|
+
|
|
223
|
+
- 不适用于**不可寻址真源**(无稳定路径/一次性管道)——退回拷贝模式并承担错位风险;
|
|
224
|
+
- 不适用于把「不改 dsh 的日志」读成「零改动」(新索引路径要新增代码,仅真源与既有摄取脚本读写语义不动);
|
|
225
|
+
- **不适用于要求「索引即全量正文」的场景**:本机制存的是定位与文本面(文件名/路径/类型/摘要/条件),
|
|
226
|
+
正文按需回读——这是「索引 + 真源」的分工,不是缺陷;
|
|
227
|
+
- 不适用于**无文件实体**的真源(纯流式/内存对象):须先物化成有路径的文件。
|