@furongjun1999/dsh-memory 0.8.0 → 0.8.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 +29 -2
- package/docs/eval/issue65_session_recall_/344/274/232/350/257/235/351/232/224/347/246/273_/346/240/270/351/252/214/344/270/216/344/277/256/345/244/215_v1.0.md +329 -0
- package/docs/eval//345/217/221/345/270/20314_/350/272/253/344/275/223/303/227/350/204/221/347/273/204/345/220/210_v1.0.md +10 -2
- package/docs/eval//345/217/221/345/270/20315_/344/274/232/350/257/235/351/232/224/347/246/273/344/277/256/345/244/215/344/270/216/350/272/253/344/275/223/345/272/223/345/261/225/347/244/272_v1.0.md +88 -0
- package/md_cg/mdcos.py +24 -1
- package/md_cg/test_p45_session_identity.py +250 -3
- package/package.json +1 -1
- package/skills/plugin.json +1 -1
package/README.md
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
|
|
14
14
|

|
|
15
15
|
|
|
16
|
-
**高性能 · 无幻觉 · 多智能体适用的长期记忆系统(v0.8.
|
|
16
|
+
**高性能 · 无幻觉 · 多智能体适用的长期记忆系统(v0.8.1)**
|
|
17
17
|
|
|
18
18
|
</div>
|
|
19
19
|
|
|
@@ -237,6 +237,8 @@ DSH 采用 Cordis bundle 机制,新增或更新插件后必须**重启 DSH 进
|
|
|
237
237
|
|
|
238
238
|
五件套共享同一套 18 条工作纪律与记忆闭环(见文末[工程纪律](#-工程纪律与设计者视角可选推荐)),接入方式互不牵动——只用记忆就只接大脑,不必理解蜂群与编译器。
|
|
239
239
|
|
|
240
|
+
> **🧬 完整身体库**:平台的「身」面——世界模型七层/3D 场景与角色/白箱生成/自研蜂窝神经网络/记忆可视化——已作为对外完整版独立公开:[**FuRongJun-1999/lingshu**](https://github.com/FuRongJun-1999/lingshu)(与本大脑经 MCP 组合;能力面一览见下文架构节)。
|
|
241
|
+
|
|
240
242
|
> **📣 蜂巢反馈邀请**:`hive/` 是五件套里最新的一层,目前处于**稳定阶段**——调度生命周期已闭环(原子领取 / 心跳 / 超时强杀 / kill / 崩溃恢复)并通过回归验证,但并发与崩溃恢复这类路径只有在**真实任务、真实机器**上跑得足够多才会真正稳定,因此这一层会持续迭代。我们特别**欢迎下载试用后反馈**:拉起失败、任务卡住、心跳异常、平台差异、kill 不生效等失败路径,对我们比「跑通了」更有价值。请开 [Issue](https://github.com/FuRongJun-1999/dsh-memory/issues) 并附 `hive_doctor` 输出(serve 存活 / 任务统计 / env 检查)。
|
|
241
243
|
|
|
242
244
|
---
|
|
@@ -450,10 +452,33 @@ DeepSeek Harness (cordis)
|
|
|
450
452
|
```
|
|
451
453
|
|
|
452
454
|
**记忆只有一个真源**:`md_cg/` 认知图(纯 md 文档,随包自带)。确定性规则引擎与知识库已内迁;
|
|
453
|
-
|
|
455
|
+
「身体」能力为可选后端(角色扮演生成),不再存记忆、默认不启动——其**对外完整版**即
|
|
456
|
+
[**灵枢身体库 lingshu**](https://github.com/FuRongJun-1999/lingshu)(独立公开仓 · MIT),0.8.0 起经 MCP 与本大脑直接组合。
|
|
454
457
|
|
|
455
458
|
> 其它 MCP 宿主同构:宿主工具面(`cg` / `stg` / `mdcg_*`)↔ stdio MCP ↔ `md_cg` 大脑;四端差异只在**纪律注入方式**(矩阵见[多 harness 接入](#多-harness-接入按端分目录)),大脑与记忆真源零改动。
|
|
456
459
|
|
|
460
|
+
### 🧬 完整身体库(lingshu)可以展示什么
|
|
461
|
+
|
|
462
|
+
[**FuRongJun-1999/lingshu**](https://github.com/FuRongJun-1999/lingshu) 是灵枢的「身」面——**灵×脑×身聚合仓**(独立公开 · MIT),身体件逐件筛选入仓、默认路径零 LLM 依赖。与本大脑经 MCP 组合后,各能力面开箱可看:
|
|
463
|
+
|
|
464
|
+
| 能力面 | 载体件 | 展示什么 |
|
|
465
|
+
|---|---|---|
|
|
466
|
+
| **身体×脑闭环** | `world/brain_store.py` | 经 MCP 连本大脑:从记忆重建 3D 世界(M1)/观测入脑(M2:坐标直存+状态事件记账);隔离库端到端 10/10、断线重连复读通过 |
|
|
467
|
+
| **世界模型七层闭环** | `world/seven_layer_loop.py` | 感知→记忆→理解→预测→验证→物理→决策 持续自主循环;每 tick 七层留痕+自增强曲线 |
|
|
468
|
+
| **世界推演循环** | `world/wm_simloop.py` | 世界图↔认知图显式推演(装载/推演/回写三环);拓扑生长=死区门控→假设→验证窗口→固化或 REVERT(验证不成立不固化);全程 WAL 留痕 |
|
|
469
|
+
| **3D 场景与角色** | `world/scene_model.py` · `silhouette3d.py` · `skeleton3d.py` | 语义时空图↔3D 场景绑定;角色轮廓/40+ 关节骨架确定性渲染;状态驱动呈现(状态→颜色/姿态) |
|
|
470
|
+
| **白箱生成与读回** | `gen/` 七件 | 确定性渲染器(零训练)+真实域读出器(纯 numpy+PIL:prompt 确定性解析、多物体构图逐物体读出) |
|
|
471
|
+
| **自研蜂窝神经网络** | `nn/` 十四件 | 蜂窝 CNN+并行条件路由一体(6 邻 stencil 等距感受野);核可学习而每步白箱(权重直读/响应图/梯度数值可验证) |
|
|
472
|
+
| **记忆可视化** | `tools/coggraph/` | 一条命令把认知图变可交互知识图谱(七步确定性管线:导出→推导边→会话链→命名审计→同义归并→渲染) |
|
|
473
|
+
| **理论地基** | `docs/theory/` 六件 | 世界模型(白箱固化/完整整理)· 存算一体(架构总纲/学习路线)· 自研神经网络(长期路线) |
|
|
474
|
+
|
|
475
|
+
上手两行(lingshu 仓根):
|
|
476
|
+
|
|
477
|
+
```bash
|
|
478
|
+
pip install -e ".[full]" # 轻核零依赖可裸跑;full 含 numpy / Pillow
|
|
479
|
+
python -X utf8 tests/test_brain_store.py # 身体×脑端到端(MDCG_BRAIN_PYTHONPATH 指向本大脑包目录)
|
|
480
|
+
```
|
|
481
|
+
|
|
457
482
|
---
|
|
458
483
|
|
|
459
484
|
## 📚 文档导航
|
|
@@ -461,6 +486,8 @@ AEIS 仅作可选「身体」能力后端(角色扮演生成),不再存记
|
|
|
461
486
|
| 文档 | 内容 |
|
|
462
487
|
|---|---|
|
|
463
488
|
| **[docs/ 目录索引](docs/README.md)** | 六域快速索引(mdcg / swarm / hive / theory / eval / plans)· 新文档归域规则 |
|
|
489
|
+
| **[完整身体库(lingshu)](https://github.com/FuRongJun-1999/lingshu)** | 平台「身」面(独立公开仓 · MIT):世界模型七层/3D 场景与角色/白箱生成/自研蜂窝神经网络/记忆可视化/身体×脑适配器 |
|
|
490
|
+
| **[Release v0.8.1](https://github.com/FuRongJun-1999/dsh-memory/releases/tag/v0.8.1)** | 本版变更:**外部报告修复入版(issue #65)+ 完整身体库展示位**——① issue #65 修复:`session_recall` ③ 近期事件段按会话隔离(`meta.session` 严格相等;显式 `"*"` 跨会话汇总;falsy 退回全局)——多窗口并发时 A 窗口注入包不再混入 B 窗口报文(静默跨会话污染消除,可被压缩检查点记入的错块源头切断);守卫 R65 组 12 断言(42/42)+六腿变异自证+独立复核 ACCEPT+全量 359/359 ② README:完整身体库 [lingshu](https://github.com/FuRongJun-1999/lingshu) 展示位三落点(平台全景/架构节「可以展示什么」八能力面/文档导航表) |
|
|
464
491
|
| **[Release v0.8.0](https://github.com/FuRongJun-1999/dsh-memory/releases/tag/v0.8.0)** | 本版变更:**身体×脑组合**(与对外完整身体库 [lingshu](https://github.com/FuRongJun-1999/lingshu) 组合)——① 身体×脑对接四条落地:身侧适配器 `brain_store`(MCP stdio)**零改动对接** `scene_model`——M1 读向(`cg(op=read)` 候选+适配器侧 tag 过滤+`spatial.coords3d` 直存坐标+槽位投影取状态)/M2 写向(`mdcg_remember` 含坐标直存;状态经 conn 垫片翻译为 `cg(op=state_event)` 记账——事件是源、槽位是投影);身侧端到端 10/10+断线重连复读通过;组合冒烟进发布门禁(第 12 腿 `body_e2e_smoke`,两栈同跑) ② 状态追踪链(语义时空图核心能力):鲸娘七类状态追踪(人物/地点/时间/事件/因果/物品/情感)→ 状态追踪图(世界书条目+大事记时间线+分幕大纲)→ 多主体常驻件 `state_atlas`(全角色世界书+酒馆 worldbook JSON 导出)→ **轮写入链**(会话每轮保守状态抽取 `state_extract` 接进每轮 mdcg 写入链:仅用户轮+两条高精度规则+同值去重+证据可甄别;三层同源=认知图+台账投影+原文 evidence 互指) ③ 退役边界四项裁定落地(基类检索/因果链/rust 内核接线,subgraph 结构面钉住)+ index.lock 有界自清(三闸口径) ④ issue #64 修复(外部报告):replay_check 空集当通行双修——负条件两态拦截(`neg_absent`/`neg_dropped_all` 不进 verify 不落盘)+撤销过严一票否决(自否定归 no_conflict 覆盖率口径)+ CLI 角色级 `--reflect/--verify-max-tokens`、`--timeout` 系列 |
|
|
465
492
|
| **[Release v0.7.5](https://github.com/FuRongJun-1999/dsh-memory/releases/tag/v0.7.5)** | 本版变更:**上下文自管理机制**——① DSH 端会话滑动窗口(窗口沉淀+续接注入两轨独立;真机验收=窗口写入当前会话真实 id、知识面开关互不替代)② zcode 端上下文接管(会话同步器+三件钩子:SessionStart 接续包注入/Stop 每轮镜像——会话 md 只留最近 10 条、全量转写、运行态窗口三写/UserPromptSubmit 压缩后重建+逢十轮归档提醒;库根解析与 MCP 同源)③ 系统提示词压缩三步重建(读纪律→声明→回取近 10 轮窗口)+长会话每 10 轮间歇归档(纪律真源修订+8 端产物重渲染)④ issue #63 修复:常驻循环无界等待有界化+活体进度面 |
|
|
466
493
|
| **[Release v0.7.4](https://github.com/FuRongJun-1999/dsh-memory/releases/tag/v0.7.4)** | 本版变更:世界模型(语义时空图)功能端三批落地+DSH 会话归因治本——① P1 状态事件抽取器 v4(24 条字面锚规则×11 槽位;三层消歧=用户侧声明优先/非事实三类/内容实指时点;对拍答案卷三率=漏 1/误 0/错 1(分母 25),与 v3 基线五项逐位对齐、跨目录逐字节确定性)② P2 槽位寄存器投影(state_slots——**事件是源、槽位是投影**,查询时现算不建第二真源:现值/退役/区间/变迁史)+ stg 第五 op `state_chain`(flag MDCG_STG_STATE 默认关;CLI 只读入口)③ P3 写侧记账口 `cg(op=state_event)`(五元事件 append;权限双闸=映射既有 write 词+require_write;actor 恒取令牌)+《秤》v2.1 §5.1 账本完整性探针(覆盖缺口率/无账可辨率/变迁史可查率;隔离 root、确定性、可复跑)+使用与运维文档 ④ DSH 会话归因治本(启动脚本去目录 mtime 猜测+插件运行期会话注入:写归因面 mdcg_remember/cg(op=write) 注入当前会话、读面语义不动;真机端到端验收=写入归因该会话真实 id);真实语料读数:鲸娘 25 事件→11 槽位(2 退役),账本探针缺口 3/14(形态/发色瞳色/米饭=P1 已知边界) |
|
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
# issue65 · session_recall ③ 近期事件段未按会话隔离——核验与修复报告 v1.0-r1
|
|
2
|
+
|
|
3
|
+
**日期**:2026-10-07 | **版本**:v1.0-r1(同日按读者反馈逐条修订——处置明细见 §十)| **缺陷号**:issue65(外部报告 by ducc239,对 v0.7.5 实测)| **被测面**:`<仓根>` 工作树(未提交:`md_cg/mdcos.py`、`md_cg/test_p45_session_identity.py` 两件被改,本报告为第三件新增;`git status --porcelain` 实测两件 `M`)
|
|
4
|
+
|
|
5
|
+
**证据分级声明**(本报告只写有机械证据的事):
|
|
6
|
+
|
|
7
|
+
| 标记 | 含义 |
|
|
8
|
+
|---|---|
|
|
9
|
+
| 〔本报告实跑〕 | 实施员/撰写员在本工作流内亲手执行并读数:修前/修后隔离库探针、守卫正向与变异自证、ANCHOR-MISS 探针、定向回归、`git status`、python 全量、仓内文件读码与哈希 |
|
|
10
|
+
| 〔工作流采集〕 | 本工作流的编排/独立复核阶段采集的读数(修前复现腿(§2.1)、独立复核四态判定、容器两栈)——本文转记处逐条标注 |
|
|
11
|
+
| 〔契约给定〕 | 设计者裁决随任务下发的口径(修法形态、语义矩阵、守卫清单、不许动清单) |
|
|
12
|
+
|
|
13
|
+
**一句话结论**:缺陷成立、缺口唯一(**限本 issue 面内**:`MdCGOS.session_recall` ③ 段未传会话;另有一处同类但属显式开关面的读侧缺口,见 §七-1)——两行过滤按契约落地,修后行为矩阵逐项符合;守卫 **30 → 42** 条断言(新增 R65 组 12 条:编号 `R65.1–R65.9`,其中 R65.4/5/6 各拆两态,对照表见 §5.1)+**六条定点变异腿逐条恰好命中期望红项**(基线源为运行中实现源码、**不读 git**);**ANCHOR-MISS 不在六腿之内**,它是另设的 fail-closed 口径(锚点漂移 → 退出码 2,见 §5.4);定向回归 `md_cg` 组 **249/249**;python 全量 **359/359**(5 跳过);容器双栈退出码 **0 / 0**(栈一 `linux_verify.sh full` 汇总 49 pass / 0 fail;栈二 TS 144 项 141 通过 / 0 失败 / 3 跳过);**独立复核 ACCEPT**(四态判定单 ①②③④ 全 PASS,见 §六)。一处**不在授权改动面内**的文档漂移(`src/lib/mdcg_client.ts` 注释把修前行为写成「服务端事实」)如实登记(§七)。
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## 一、缺陷定义与真实站点
|
|
18
|
+
|
|
19
|
+
**定义**:`MdCGOS.session_recall` 的「③ 近期事件」段取 `self.recent_events(limit=…)` 时**不传会话**;而 `MdCGSecure.recent_events`(改前 `md_cg/mdcos.py:5142`,改后 `:5165`)**没有 session 形参**(只有 `private`/`secret` 密级档做会话归属判断,默认档 `internal` 全放行)⇒ `recent` 段恒为**全进程窗口**:多窗口/多会话并发时,A 会话语境里混入 B 会话的报文——**静默污染,不报错、不降级**。危害链两条(r1 补证据,各点均读码核实):
|
|
20
|
+
- **注入面**:DSH 侧把窗口文本推入宿主请求上下文——`src/hooks.ts:663-667` 将 `formatSessionWindow` 的输出经 `renderUntrustedMemoryBlock` 推入 `assembly.contexts`(块名 `lingshu:session-window`)。
|
|
21
|
+
- **压缩重建面**:zcode 压缩钩子在压缩后取 `session_recall(session=sid)` 的 recent 段拼成「本会话近期对话」重建文本——`scripts/zcode_compact_hook.py:67-71` → `scripts/sync_zcode_session.py:330,344-351`,并经 `_ack_compact`(`zcode_compact_hook.py:74-88`)登记「已履行近 10 轮重建」。修前该段是全窗口,混入的他会话报文会随该文本进入注入/重建。
|
|
22
|
+
- **未观测**:「运行时确已发生混入」本身无观测记录(本 workflow 只做了库侧读数);上述两条为**代码链证据**,非现场取证。
|
|
23
|
+
|
|
24
|
+
**真实站点**(读码;行号口径见下表内标注——本报告正文其余处引用一律为**改后**工作树行号):
|
|
25
|
+
|
|
26
|
+
| 面 | 位置 | 修前形态 |
|
|
27
|
+
|---|---|---|
|
|
28
|
+
| 缺陷点 | `md_cg/mdcos.py` ③ 段(改前 L3307-3314 → 改后 L3316-3337) | `evs = self.recent_events(limit=…)` → 直接组装 `pack["recent"]`,无会话过滤 |
|
|
29
|
+
| 读隔离本体 | `MdCGSecure.recent_events`(改前 L5142-5167 → 改后 `md_cg/mdcos.py:5165-5190`) | 无 `session` 形参;只按 tenant × 密级(+ private/secret 档的会话归属)过滤 |
|
|
30
|
+
| 同族正确形态① | `session_compact` 的 `if session:` 过滤(改前 L3384-3386 → 改后 `md_cg/mdcos.py:3407`) | `if session: evs = [r for r in evs if (r.get("meta") or {}).get("session") == session]` |
|
|
31
|
+
| 同族正确形态② | `_session_notes`(`md_cg/mdcos.py:3205`,未漂移) | `session:{sid}` tag 过滤(① 段本就按会话走) |
|
|
32
|
+
| 同族正确形态③ | `md_cg/mcp_server.py:2843` `_session_call`(recall 分支 `:2863-2870`) | 已把 `session=a.get("session")` 传进 `session_recall`(`:2864`) |
|
|
33
|
+
| 写侧(均已打点) | `src/hooks.ts:698/772`、`MdCGSecure.remember_event`(改前 L5064 → 改后 `md_cg/mdcos.py:5087`) | DSH 窗口写入带 `{session: sid}`;安全档 `remember_event` 对缺会话者 `setdefault` 成本进程会话 |
|
|
34
|
+
|
|
35
|
+
⇒ **在本 issue 的面内**(`session_recall` ③ 段读侧),缺口只有一处;写侧与 MCP 调用链均正确——与编排侧核验结论一致。**「唯一」的边界**:另有一处**同类**读侧缺口(`recall(include_recent)` 的近期事件尾巴,`md_cg/mdcos.py:1952`),按 ask 口径属显式开关面、登记不回——不计入本节「缺口唯一」,见 §七-1(r1 显式限定)。
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## 二、修前现场:编排侧复现腿(逐条)+实施员探针
|
|
40
|
+
|
|
41
|
+
### 2.1 编排侧采集的复现腿(〔工作流采集〕,逐条列 payload / 异常 / 崩溃点)
|
|
42
|
+
|
|
43
|
+
以下 ①–⑤ 为缺陷/旁证腿、⑥ 为登记腿、⑦ 为对照基线(非缺陷);全部在**隔离库**(`tempfile.mkdtemp` 合成库;经 MCP 真实入口 `md_cg.mcp_server._session_call` 与直调 `cg.session_recall(...)` 两条路径读取)逐腿钉死;**全部缺陷腿为非崩溃型**——无异常抛出、无错误消息、**无崩溃点**,缺陷形态是**静默**的数据越界/混入(宿主把 recent 当用户输入处理,这正是本缺陷最危险处)。日志:`%TEMP%/issue65_probe/probe_run3.log:26-54`、`%TEMP%/issue65_probe/probe2_run2.log:2-23`。
|
|
44
|
+
|
|
45
|
+
**合成库清单(r1 补——全文共 4 个互不相同的隔离库,读数不可跨库比较)**:
|
|
46
|
+
|
|
47
|
+
| 库 | 构成(均为 `tempfile.mkdtemp` 合成库) | 出现处 |
|
|
48
|
+
|---|---|---|
|
|
49
|
+
| 库① | 五事件:`A1`/`A2`(属 `session-AAA-0000`)、`B1`(属 `session-BBBB-0000`)、`N0`(缺 `meta.session`)、`AX`(A 的**前缀延伸**会话) | §2.2、§四 |
|
|
50
|
+
| 库② | 三事件(会话 A/B 分属两会话)+腿④追加的无归属事件(共 4 条) | §2.1 腿①–④、腿⑦ |
|
|
51
|
+
| 库③ | 三事件 + 两条 `session_note`(A/B 各一) | §2.1 腿⑤ |
|
|
52
|
+
| 库④ | 复核自建:`A1`(会话 A)、`B1`(会话 B)、经安全档写侧归属 A 的裸事件、存量裸事件 `L0` | §6.2 |
|
|
53
|
+
|
|
54
|
+
同一标签(`A1`/`A2`/`AX`/`N0`/`B1`)在**不同库中代表不同事件集**(例如 A 会话读数:库① 修前 5 条 / 修后 2 条 / 修后安全档 3 条;库④ 安全档 2 条——差异源于**库构成**,非实现行为;正文各读数已就近标注库号)。
|
|
55
|
+
|
|
56
|
+
**腿①(缺陷① · 假会话 id → ③ 段返回全局窗口,应 `[]`)**
|
|
57
|
+
- payload:库内置 3 条合成事件——`('user','会话A的第一句话', meta.session='session-AAA')`、`('user','会话B的第一句话', meta.session='session-BBB')`、`('assistant','会话A的第二句话', meta.session='session-AAA')`;读入参 `session='session-zzzz-nope-0000'`(MCP:`{action:'recall', session:'session-zzzz-nope-0000', limit:5, recent_limit:10}`;直调同参)。
|
|
58
|
+
- 观测(异常 / 崩溃点):**无异常、无错误消息、无崩溃点**(`degraded=[]`、`truncated=false`、budget 1200 未触发裁剪)。`recent_len=3`,texts=`['会话A的第二句话','会话B的第一句话','会话A的第一句话']`(两入口读数一致)——**不存在的会话拿到全会话窗口**(修后目标 `recent==[]`)。
|
|
59
|
+
- 站点:`md_cg/mdcos.py:3309`(改前,③ 段 `evs=…` 不传会话);`pack['recent']` 组装 `:3310-3312`;根因面 `:5142`(`recent_events` 无 session 形参)/ 基类 `md_cg/mdcg.py:3040-3050`(无任何会话过滤)。
|
|
60
|
+
|
|
61
|
+
**腿②(缺陷② · 会话 A 的续接包混入会话 B 报文,含多实例并发形态)**
|
|
62
|
+
- payload:库②(同腿① 三事件态);`session_recall(session='session-AAA')`;另以同 root 另开第二实例 `MdCGSecure(root, principal=Principal(session='session-AAA'))` 再读(模拟多窗口/多会话并发)。
|
|
63
|
+
- 观测(异常 / 崩溃点):**无异常、无崩溃点**;`recent_len=3`,3 条中混入他会话报文 `『会话B的第一句话』`;多实例形态读数相同(3 条含 B)。
|
|
64
|
+
- 站点:同腿①。
|
|
65
|
+
|
|
66
|
+
**腿③(缺陷③ · 会话 B 的续接包混入会话 A 报文)**
|
|
67
|
+
- payload:库②(同腿① 三事件态);`session_recall(session='session-BBB')`。
|
|
68
|
+
- 观测(异常 / 崩溃点):**无异常、无崩溃点**;`recent_len=3`,含会话 A 两条(`『会话A的第二句话』『会话A的第一句话』`)。
|
|
69
|
+
- 站点:同腿①。
|
|
70
|
+
|
|
71
|
+
**腿④(缺陷④ · 指定会话下「缺 meta.session」的无归属事件未被丢弃,应丢弃)**
|
|
72
|
+
- payload:库②(在 3 条会话事件之外)追加第 4 条经 `MdCG.remember_event(cg,'user','【无归属】本条事件 meta 中无 session 字段',meta={})` **直写基类路径**的事件(绕过 `MdCGSecure.remember_event` 的 setdefault,模拟无归属事件);读 `session='session-AAA'` 与 `session='session-BBB'` 两形态。
|
|
73
|
+
- 观测(异常 / 崩溃点):**无异常、无崩溃点**;两会话下 `recent_len=4`,均含该无归属事件(raw 窗口读数 `meta.session=null`)——修前完全不丢。
|
|
74
|
+
- 站点:`:3309`(改前无过滤);写路径 `MdCGSecure.remember_event`(改后 `md_cg/mdcos.py:5080-5091`,`setdefault("session")` 在 `:5087`;无归属事件来自历史数据/直写形态)。
|
|
75
|
+
|
|
76
|
+
**腿⑤(旁证 · 同一次 session_recall 返回包内段间矛盾)**
|
|
77
|
+
- payload:库③(在 3 条会话事件外)+ `session_note('要点A:本会话在做甲任务', session='session-AAA')` + `session_note('要点B:本会话在做乙任务', session='session-BBB')`;`session_recall(session='session-AAA')`;另同库同数据 `session_compact(session='session-AAA', limit=40)`。
|
|
78
|
+
- 观测(异常 / 崩溃点):**无异常、无崩溃点**。notes 段只回 A 要点(`tags=['session','session:session-AAA']`,B 要点不在);同一次调用的 recent 段 3 条含 B——**同一返回包内,① 段已隔离、③ 段未隔离**。同族对照 `session_compact(session='session-AAA')` events=2、摘要只含 A(正确过滤)。
|
|
79
|
+
- 站点:对照面 `md_cg/mdcos.py:3205-3206`(`_session_notes` 按 `session:{sid}` 过滤)、`:3384-3386`(改前 `session_compact` 按 `meta.session` 过滤,改后 `:3407-3409`)vs `:3309`(改前 recent 段无过滤)。
|
|
80
|
+
|
|
81
|
+
**腿⑥(登记腿 · `recall(include_recent=True)` 的近期事件尾巴同样不吃 session——判定登记不回)**
|
|
82
|
+
- payload:`cg.recall('会话要点', session='session-AAA', include_recent=True, recent_limit=10, budget_tokens=4000)`。
|
|
83
|
+
- 观测(异常 / 崩溃点):**无异常、无崩溃点**;recent 尾巴 3 条含 `『会话B的第一句话』`——同类缺口事实成立;属**显式开关面**、非本 issue 修复目标,按 ask 口径如实登记、本轮不修(见 §七)。
|
|
84
|
+
- 站点:`md_cg/mdcos.py:1951-1960`(`:1952` 调 `self.recent_events(limit=…)` 不传会话;`recent_events` 亦无 session 形参)。
|
|
85
|
+
|
|
86
|
+
**腿⑦(对照基线 · `session='*'` 与 `session=None` → 全局 3 条;非缺陷)**
|
|
87
|
+
- payload:库②(三事件态);`session_recall(session='*')`;`session_recall(session=None)`。
|
|
88
|
+
- 观测(异常 / 崩溃点):两者 `recent_len=3`(全部事件)——`'*'` 修后语义即不过滤、`None` 修后退回旧行为,与修前读数一致,**不构成缺陷**(`reproduced=false`,作为语义矩阵完整性读数记录)。**无异常、无崩溃点**。
|
|
89
|
+
- 站点:`:3309`(该段修前不区分这两种输入)。
|
|
90
|
+
|
|
91
|
+
### 2.2 实施员探针(〔本报告实跑〕隔离库五事件)
|
|
92
|
+
|
|
93
|
+
**探针(库①)**:`%TEMP%/issue65_probe.py`(系统临时目录,非仓内件),在**合成隔离库**建五条事件:`A1/A2 ∈ session-AAA-0000`、`B1 ∈ session-BBBB-0000`、`N0` **缺 `meta.session`**、`AX ∈ session-AAA-0000-long`(A 的**前缀延伸**,用于钉「严格相等」)。命令:`PYTHONPATH=<仓根> python -X utf8 %TEMP%/issue65_probe.py 修前`。
|
|
94
|
+
|
|
95
|
+
| session 入参 | 修前 recent(MdCGOS 基类 / MdCGSecure 生产形态) |
|
|
96
|
+
|---|---|
|
|
97
|
+
| `'session-zzzz-nope-0000'`(假 id) | **5 条**:`['AX','N0','B1','A2','A1']` ← 最直观证据 |
|
|
98
|
+
| `'session-AAA-0000'`(A) | **5 条**,混入 `B1`(B 会话报文) |
|
|
99
|
+
| `'session-BBBB-0000'`(B) | 5 条(含 A 的两条) |
|
|
100
|
+
| `'*'` / `None` / `''` | 5 条 |
|
|
101
|
+
| 缺 `meta.session` 的 `N0` 在 `session=A` 下 | **可见**(`True`) |
|
|
102
|
+
|
|
103
|
+
旁证(同库同跑,修前即为正确形态):`session_compact(session=A)=2 | (B)=1 | (None)=5`——同族过滤在役,缺陷只在 ③ 段。
|
|
104
|
+
|
|
105
|
+
**与编排侧读数对拍**:编排侧(三事件分属 A/B)读数为「假 id → 3 条、A → 混入 B、None/"*" → 全局 3 条」;本探针五事件下同形(假 id → 5 条、A → 混入 `B1`、None/"*" → 全局 5 条),**方向与形态逐项一致**,差异仅在事件条数(探针事件数不同)。
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## 三、修法(照契约实现,逐点声明)
|
|
110
|
+
|
|
111
|
+
**契约来源与存放位置(r1 补)**:本报告所称「契约」= 本工作流的 `contract` 运行参数——定义原文「设计者裁决的修复契约:缺陷定义 + 必须怎么修 + 不许动什么(会原样进实施员与复现员的 ask)」(工作流真源 `.zcode/workflow-drafts/issue65-修复session_recall-近期事件段会话隔离.dwf.ts:15-18`),随 ask 原样下发给复现/实施/复核三方(同文件 `:104/:123/:141`)。**参数取值不落仓**(运行时注入),故仓内**无契约原文可附**。文本内可核对的契约转记面有三处:① §5.1 的契约条目对照表(R65.1–R65.7);② 本节的修法两条——可与工作区源码 `md_cg/mdcos.py:3331-3332` 逐字对照;③ 不许动清单——可用 `git status --porcelain` 与 `git diff --stat` 核对(只两件 `M`)。「与契约一致/未削弱契约」诸断言的核对方式即上述三面;若需契约原文附录,须向工作流主会话索取该运行参数值。
|
|
112
|
+
|
|
113
|
+
**1)③ 段过滤**(`md_cg/mdcos.py:3331-3332`,取 `evs` 之后、组装 `pack["recent"]` 之前):
|
|
114
|
+
|
|
115
|
+
```python
|
|
116
|
+
if session and str(session).strip() != "*":
|
|
117
|
+
evs = [r for r in evs if (r.get("meta") or {}).get("session") == session]
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
形态与契约给的两行**逐字一致**(仅缩进随所处 `try` 块)。语义矩阵:真值且非 `"*"` → 只回本会话(`meta.session` **严格相等**;缺 `meta.session` 的事件被丢弃——最坏空窗口,杜绝错块);显式 `"*"` → 不过滤(跨会话汇总合法用法,与 stg 面 `view_session` 的 `"*"` 同款);falsy(`None`/`""`)→ 退回旧行为(全局窗口,存量调用面零迁移);`strip()` 防空白包裹的 `"*"`。
|
|
121
|
+
|
|
122
|
+
**2)生效条件注释**(本仓惯例:`md_cg/mdcos.py:3234`)追加 ③ 段口径条款(含边界:「会话要点段的 tags 过滤与台账段的不过滤口径不变」)。
|
|
123
|
+
|
|
124
|
+
**3)docstring**(`md_cg/mdcos.py:3247-3254`)新增「会话口径(issue #65)」段:写明 **③ recent 段跟会话走**、`"*"` = 跨会话汇总、falsy 退回全局窗口,并点明口径分工——① 会话要点段本就按 `session:{sid}` tag 过滤;目标段与任务段是**有意跨会话**的工程面。
|
|
125
|
+
|
|
126
|
+
**不许动清单遵守**(逐条):`session_compact` 既有过滤、`_session_notes`、`recent_events` 本体(改前 L5142 → 改后 `md_cg/mdcos.py:5165`)、`recall(include_recent)`(`md_cg/mdcos.py:1952`「近期事件尾巴」,行号未漂移)、`src/hooks.ts` —— **均零改动**(`git status` 只两件 `M`;`recall` 尾巴的判定登记不回见 §七)。
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## 四、修后行为矩阵(〔本报告实跑〕,同探针同库)
|
|
131
|
+
|
|
132
|
+
| session 入参 | 修后 MdCGOS(基类) | 判据 |
|
|
133
|
+
|---|---|---|
|
|
134
|
+
| `'session-zzzz-nope-0000'` | **0 条 `[]`** | 杜绝错块(空窗口) |
|
|
135
|
+
| `'session-AAA-0000'` | **2 条** `['A2','A1']` | 只含 A;不含 B、不含裸事件、不含前缀延伸会话 |
|
|
136
|
+
| `'session-BBBB-0000'` | **1 条** `['B1']` | 只含 B |
|
|
137
|
+
| `'*'` | 5 条(全局) | 显式跨会话汇总 |
|
|
138
|
+
| `' * '` | 5 条(全局) | strip 判据 |
|
|
139
|
+
| `None` | 5 条(全局) | falsy 退回旧行为 |
|
|
140
|
+
| `''` | 5 条(全局) | falsy |
|
|
141
|
+
| 裸事件 `N0` 在 `session=A` 下 | **不可见**(`False`)/ 在 `None` 下 **可见**(`True`) | 缺 `meta.session` 丢弃;非指定会话照旧 |
|
|
142
|
+
| `session_compact(A/B/None)` | `2 / 1 / 5` | 回归:既有过滤不受影响 |
|
|
143
|
+
|
|
144
|
+
**生产形态补充(诚实口径 · 库①)**:`MdCGSecure` 实例下 `session=A` 读到 **3 条** = `A1`、`A2` + 裸事件 `N0`——原因是**写侧** `MdCGSecure.remember_event`(改后 `md_cg/mdcos.py:5087`)在写入时对缺会话者 `setdefault("session", principal.session)`,探针的裸事件被**归属成 A**;读侧语义不变(仍严格相等过滤)。即:经安全档写入的事件天然带会话归属,读侧丢弃面只覆盖**存量数据 / 非安全档直写**形态(契约要求的「不许漏」正是为此)。
|
|
145
|
+
|
|
146
|
+
**勿与 §6.2 库④ 的 2 条混淆(r1 补)**:库④ 的安全档读数 `['A1','N0']` 也是「本会话标记事件+被归属的裸事件」同构,但只有 **1 条** A 会话标记事件——3 条 vs 2 条的差在**库构成**(库① 的 A 会话有 `A1`/`A2` 两条),不在实现行为。
|
|
147
|
+
|
|
148
|
+
---
|
|
149
|
+
|
|
150
|
+
## 五、守卫、定点变异自证与 ANCHOR-MISS
|
|
151
|
+
|
|
152
|
+
**落点**:扩既有 `md_cg/test_p45_session_identity.py`(30 断言 → **42**),新 R65 组(`md_cg/test_p45_session_identity.py:245-462`,读码口径:R65 分节自 `:245` 起、`_r65_self_proof()` 于 `:462` 结束)。
|
|
153
|
+
|
|
154
|
+
### 5.1 断言清单(12 条)与契约对照
|
|
155
|
+
|
|
156
|
+
| 断言 | 契约条目 | 说明 |
|
|
157
|
+
|---|---|---|
|
|
158
|
+
| R65.1 假会话 id → `recent == []` | R65.1 | 空窗口,杜绝错块 |
|
|
159
|
+
| R65.2 会话 A → 恰为 A 的两条 | R65.2 | 用 `AX`(A 的**前缀延伸**会话)把「严格相等」与「前缀/子串匹配」区分开 |
|
|
160
|
+
| R65.3 会话 B → 恰为 B 的一条 | R65.3 | — |
|
|
161
|
+
| R65.4a `session="*"` → 全局 | R65.4 | 不过滤 |
|
|
162
|
+
| R65.4b `session=" * "` → 全局 | (细分子项) | strip 判据的判别力腿 |
|
|
163
|
+
| R65.5a/5b `None` / `""` → 全局 | R65.5 | 契约 R65.5 的两态拆成两条,便于变异定点 |
|
|
164
|
+
| R65.6a/6b 缺 `meta.session`:指定会话下丢弃 / 非指定会话照旧可见 | R65.6 | 契约的两半,拆成两条 |
|
|
165
|
+
| R65.7 `session_compact(A)` 仍只压 A | R65.7 | 回归腿(既有过滤不受影响) |
|
|
166
|
+
| R65.8 文档口径在位 | (扩展) | docstring 含「段跟会话走」「跨会话汇总」,且 `def` 上方「生效条件」注释含过滤条款 |
|
|
167
|
+
| R65.9 MCP 入口端到端 | (扩展) | 经 `mcp_server._session_call` 走 `action=recall`:本会话只回本会话、假 id 空窗口 |
|
|
168
|
+
|
|
169
|
+
> **偏离声明(r1 更正)**:契约清单为 R65.1–R65.7。本守卫相对契约的变化分两类:**新增判据 3 条**——R65.4b(strip 判据判别力)、R65.8(文档口径)、R65.9(MCP 端到端),为**加严**;**两态拆分 2 处**——R65.5→5a/5b、R65.6→6a/6b,**覆盖强度与未拆时相同**(拆分本身不加严),其价值在使变异定点可指认、判别力可见。合计 12 条;**零删减**(未削弱契约任一条);每条都有定点变异钉住(下表)。
|
|
170
|
+
|
|
171
|
+
### 5.2 正向读数(〔本报告实跑〕)
|
|
172
|
+
|
|
173
|
+
```
|
|
174
|
+
python -X utf8 -m md_cg.test_p45_session_identity → 通过 42 / 失败 0,退出码 0
|
|
175
|
+
python -X utf8 -m md_cg.test_p45_session_identity --self-proof → 六腿全 PASS,退出码 0
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### 5.3 定点变异自证(`--self-proof`)读数
|
|
179
|
+
|
|
180
|
+
模式口径:**就地变异运行中的实现源码**(`inspect.getsource` → dedent → `exec` → 临时替换 `MdCGOS.session_recall` → 跑 R65 组 → 复原),**不读 git**(绑提交做基线会在下一次改动即失效,本仓已有两次教训);每条腿声明**期望红项集合**,实跑红项必须**恰好相等**(多红=断言语义纠缠,少红=该判据空转)。
|
|
181
|
+
|
|
182
|
+
| 腿 | 变异 | 锚点 | 实跑红项 | 期望 | 判定 |
|
|
183
|
+
|---|---|---|---|---|---|
|
|
184
|
+
| ① | 删整段会话过滤(回到全进程窗口) | 两行整块 | **5**:R65.1/2/3/6a/**R65.9** | 5 | PASS |
|
|
185
|
+
| ② | 删 `"*"` 例外(星号也过滤) | `if session and … != "*":` | **2**:R65.4a / R65.4b | 2 | PASS |
|
|
186
|
+
| ③ | 去 `strip`(`" * "` 不再按汇总) | `str(session).strip() != "*"` | **1**:R65.4b | 1 | PASS |
|
|
187
|
+
| ④ | 缺 `meta.session` 改 fail-open(放行) | 过滤行 | **4**:R65.1/2/3/6a | 4 | PASS |
|
|
188
|
+
| ⑤ | 去 falsy 守卫(`None`/`""` 也过滤) | `if session and …` | **2**:R65.5a / R65.5b | 2 | PASS |
|
|
189
|
+
| ⑥ | 文档口径回退(docstring 反转) | `段跟会话走` | **1**:R65.8 | 1 | PASS |
|
|
190
|
+
|
|
191
|
+
- **未变异基线**:红项 **0**(防「变异模式自己就红」)。
|
|
192
|
+
- **复原后重跑全套(A–G + R65)**:退出码 **0(全绿)**。
|
|
193
|
+
- **签名区分度**:① 与 ④ 的差恰在 `R65.9`(MCP 腿库内无裸事件,fail-open 不影响它)——即每条腿的红项集合是**唯一指纹**。
|
|
194
|
+
- **一处修正如实记录**:变异④ 首跑实红 4 项、期望集先写为 2 项(漏算「fail-open 会让裸事件混进任一按会话腿」)——按实跑修正期望为 4 项并加注理由,**不是**改断言迎合读数(断言自始未动)。
|
|
195
|
+
|
|
196
|
+
### 5.4 ANCHOR-MISS 与退出码口径(fail-closed)
|
|
197
|
+
|
|
198
|
+
- 锚点自检:每条锚点在实现源码里**命中次数必须恰为 1**;否则打印 `ANCHOR-MISS 锚点漂移(命中 N 次,期望恰好 1)` 并**退出码 2**(正常跑与自证模式皆然)。
|
|
199
|
+
- 〔本报告实跑〕漂移探针(进程内把 `inspect.getsource` 替换为「把锚点句改写」的版本,**不动仓内件**):输出 `ANCHOR-MISS … '段跟会话走'`,`main()` 返回值 **2** ✔。
|
|
200
|
+
- 退出码:`0` 全绿 | `1` 断言失败 | `2` 锚点漂移(优先于 1)。
|
|
201
|
+
|
|
202
|
+
---
|
|
203
|
+
|
|
204
|
+
## 六、独立复核(ACCEPT)与它列出的未覆盖面
|
|
205
|
+
|
|
206
|
+
**判定:ACCEPT**——依据 = **四态判定单 ① PASS ② PASS ③ PASS ④ PASS**,issue65 修复成立。复核的全部判定依据均在复核 ask 内**亲手执行**:只读仓库 + `%TEMP%` 合成库;**未编辑任何工作区文件、未 git add/commit/push**(实验根 `%TEMP%\rev65_issue65` 用毕已删)。
|
|
207
|
+
|
|
208
|
+
**复核环境**(〔工作流采集〕):改前实现取真源 `git archive HEAD md_cg data utf8_boot.py` 解到 `%TEMP%`(首次漏 `utf8_boot.py` 致改前守卫 ImportError,补入后跑通——复核如实记录为己方探针环境的准备问题);**改前树无** `if session and str(session).strip() != "*":` 行、**现行树有**;`mdcos.py` sha256 前16:改前 `8bb2e878c411bb48` / 现行 `62e5ff193c492e5b`〔撰写员复算现行值一致〕。
|
|
209
|
+
|
|
210
|
+
### 6.1 四态判定单(复核独立读数)
|
|
211
|
+
|
|
212
|
+
**① 修前各腿输入在现行树 + 退化路径可判定 → PASS**
|
|
213
|
+
|
|
214
|
+
- 自写驱动 `rev65_driver.py`(对 old/new 两树各起子进程、2 个独立合成库 + 独立 oracle)→ ALL PASS:现行树 **11 个输入 × 2 库**全部无异常,且返回**事件短标签集合**(r1 定义:每个返回事件的**文本前 2 字**,口径同守卫 `_r65_recent` 的 `[:2]`,`md_cg/test_p45_session_identity.py:292`;**与另两处「前缀」用法无关**——§2.2 的 `AX` 是**会话 id** 的前缀延伸会话、`'session-AAA'` 是**会话 id** 的真前缀,均非事件文本标签)**== 独立 oracle**(oracle = 标准库 `json` 直读 `<store>\_recent.jsonl` 按题设语义算出的短标签集合:FAKE→`[]`、A→`['A1','A2']`、AX→`['AX']`、B→`['B1']`、`'session-AAA'`(A 的真前缀)→`[]`、`' session-AAA-0000 '`→`[]`、`'*'`/`' * '`/`' \t* '`/None/`''`→全 5 条)。
|
|
215
|
+
- 退化非空池空转判据:4 次运行 `raw_count==5` 全 PASS(库非空而非空转);裸事件 N0 在 A 下不可见、在 None 下可见(两向钉死)。
|
|
216
|
+
- 改前实现同库旁证:A / B / FAKE / None **全部返回 5 条**(含 B1 混入)——**缺陷实存**。
|
|
217
|
+
|
|
218
|
+
**② 合法输入逐位对照 → PASS**
|
|
219
|
+
|
|
220
|
+
- old vs new 同库逐位:recent 段 JSON **全等**(`None`/`''`/`'*'`/`' * '`/`' \t* '` × 2 库)、整包(None 输入)JSON 全等、`session_compact` 读数全等 `{A:2, B:1, None:5}`。
|
|
221
|
+
- `recent_events` 签名两树均为 `(limit, roles, since, newest_first)`、**无 session 形参**——证修复只能落 `session_recall` ③ 段(与 §一「本 issue 面内缺口唯一」一致)。
|
|
222
|
+
|
|
223
|
+
**③ 守卫本体 + 变异自证 + 删断言探针 → PASS**
|
|
224
|
+
|
|
225
|
+
- 改前树同命令 `python -X utf8 -m md_cg.test_p45_session_identity` → rc 0、**30/0**(「30 断言 → 42」成立);现行树 **42/0**(§5.2,〔撰写员复跑一致〕)。
|
|
226
|
+
- 复核自写 `rev65_mutation_sets.py` 收集每腿红项**集合**:①`{R65.1,R65.2,R65.3,R65.6a,R65.9}` ②`{R65.4a,R65.4b}` ③`{R65.4b}` ④`{R65.1,R65.2,R65.3,R65.6a}` ⑤`{R65.5a,R65.5b}` ⑥`{R65.8}`——逐条与声明期望**恰好相等**;①/④ 差恰为 `R65.9`(指纹唯一);还原后红项 ∅。
|
|
227
|
+
- 删断言探针 `rev65_delassert.py`(内存拦 `check`、不改文件):对照组 rc 0;删 `R65.4b` → rc 1(腿②③转红);删 `R65.6a` → rc 1(腿①④);删整组 `R65.*` → 六腿全红 rc 1(「删断言后自证必红」成立)。
|
|
228
|
+
- 退出码三态进程级实测:断言失败 → 41/1、rc 1;漂移+断言全假 → rc 2 且 **0 条 `[FAIL]`**(**2 优先于 1**)。
|
|
229
|
+
|
|
230
|
+
**④ 不得静默可观测面(ANCHOR-MISS fail-closed)→ PASS**
|
|
231
|
+
|
|
232
|
+
- 漂移探针经 `runpy` 走守卫真实 `__main__` 退出码映射:clean rc 0;`drift_doc`(进程内改写 `inspect.getsource`)→ 打印 `ANCHOR-MISS 锚点漂移(命中 0 次,期望恰好 1):'段跟会话走'`,正常与 `--self-proof` 两模式均 rc 2;`drift_dup`(锚点块复制成 2 次)→ 打印 3 条「命中 2 次」+ rc 2(>1 也拦)。
|
|
233
|
+
- 守卫文件 grep 无 `subprocess`/`git` 调用 →「基线 = 运行中源码、不读 git」成立。
|
|
234
|
+
|
|
235
|
+
### 6.2 交叉核验与仓库无痕(复核)
|
|
236
|
+
|
|
237
|
+
- 生产形态(**库④**,`MdCGSecure` + MCP):A→`['A1','N0']`(= A 会话**一条**标记事件 `A1` + 裸事件被**写侧** `setdefault` 归属 A,`mdcos.py:5087`;与 §四 库① 的 3 条差在库构成,见 §四 注)、FAKE→`[]`、`'*'`→4 条;存量裸事件 L0(绕过 Secure 直写、`meta.session=null`)在 A 下被丢弃——契约「缺 `meta.session` 丢弃」方向正确。
|
|
238
|
+
- 全仓调用点清点:生产面仅 `md_cg/mcp_server.py:2864` 与 `scripts/sync_zcode_session.py:330` 调 `MdCGOS.session_recall`,**均传 session**;`md_cg/whitebox_kb/aeis_core/api.py:900` 为同名异法(AEIS 适配器,签名 `session_recall(session_id, query, limit)`);TS 侧 `src/lib/mdcg_client.ts:351` 透传 session、`src/hooks.ts:658` 传 `sid`——**无漏传点**。〔撰写员补充清点:另有测试面调用(`md_cg/test_lifecycle_retire_leak.py:317`、`md_cg/test_n202_session_notes_visibility.py:234`、`md_cg/test_tasks.py:241` 等,均为测试用);`scripts/zcode_compact_hook.py:18` 仅注释提及〕
|
|
239
|
+
- 行号口径与报告一致:`recent_events` 改前 5142 / 现 5165;③ 段 `evs` 改前 3309 / 现 3318;`compact` 的 `if session:` 改前 3384 / 现 3407〔撰写员以 `git show HEAD:md_cg/mdcos.py` 与工作树读码逐条复核,见 §一/§三〕。
|
|
240
|
+
- 仓库无痕:复核前后 `git status --porcelain` **逐字一致**(两 `M` + 一 `??`)、`git diff --stat` 25/253、274 插入不变、`mdcos.py` 哈希复核前后一致〔撰写员复跑 `git status`/`git diff --stat` 读数一致〕。
|
|
241
|
+
|
|
242
|
+
### 6.3 复核列出的未覆盖面(uncovered)
|
|
243
|
+
|
|
244
|
+
1. **`recall(include_recent=True)` 的近期事件尾巴**(`md_cg/mdcos.py:1951-1960`):复核确认「该段调 `self.recent_events(limit=…)`(`:1952`)**不传会话**、因而不过滤」——事实成立,但属显式开关面、非本 issue 目标,**登记不回**(与 §2.1 腿⑥同口径)。〔r1 更正:`recall` 本体**有** `session` 形参(`:1850`)——「确无会话参数」原字样易被读成 recall 无 session;该面若修不需动签名,见 §七-1〕
|
|
245
|
+
2. **`src/lib/mdcg_client.ts` 的「不按 session 过滤(服务端事实)」注释**(该 docstring 内「两条服务端事实」块,核心句 `:343-344`):复核确认**仍在**(本轮未改,如实登记)——修后此句已成漂移源,待主会话收口时同步(见 §七-2)。
|
|
246
|
+
3. 复核四态判定覆盖面为**修复本体与守卫**;容器两栈、python 全量等跨面读数不在四态内(见 §八,另标来源)。
|
|
247
|
+
|
|
248
|
+
---
|
|
249
|
+
|
|
250
|
+
## 七、未采纳 / 登记项
|
|
251
|
+
|
|
252
|
+
1. **`recall(include_recent)` 的「近期事件尾巴」(`md_cg/mdcos.py:1951-1960`,`:1952` 调 `self.recent_events(limit=…)` 不传会话)判定登记不回**(契约明示):该面是 read/recall 的**显式开关**(`include_recent=True` 才取)。**r1 更正(原稿此处有误)**:`recall` 本体**接收 `session` 形参**(`md_cg/mdcos.py:1850`;`use_rrf` 分支把它传给候选层,`:1882`),只是尾部段未把它下传给 `recent_events`(该函数无 session 形参,§一)——**即该面可修,且修法与 ③ 段同款:一行调用点过滤即可,不需动签名**;原稿「无会话参数可用(签名里没有 session)/需动 read op 的签名与调用面」不成立,特此更正。按 ask 口径(显式开关面、非本 issue 修复目标)登记留池,**本轮未修**;是否另开 issue 一次收口(防新调用点重演同类静默混入)属主会话决策——本轮未创建跟踪件。
|
|
253
|
+
2. **`src/lib/mdcg_client.ts:342-344` 注释已成漂移源(不在授权改动面内,登记待同步)**:该处把**修前**行为写成「服务端事实」——「① `recent` 段取 `recent_events(limit=recent_limit)` —— **不按 session 过滤**」。修后此句反了。因契约「不许动」清单把改动面锁在 `md_cg/mdcos.py` / 守卫 / 报告三处,**本次未改 TS 注释**;建议主会话收口时同步。
|
|
254
|
+
**同源注释盘点与同步动作归属(r1 补)**:该句另有一份**编译产物**副本 `lib/lib/mdcg_client.d.ts:212-214`(`npm run build` 自真源再生——改真源后重新构建即同步,**不必手改产物**);除上述产物副本外全仓无第三处(盘点含 `src/` 测试面——无钉住该注释的断言)。仓内其余「不按 session 过滤」字样(`md_cg/mdcos.py:3245/3292`、`md_cg/test_tasks.py:7`)指**任务台账段的有意跨会话口径**(正确、非漂移)。同步动作由**主会话收口**执行(工作流真源 `.zcode/workflow-drafts/issue65-…dwf.ts:3/:244` 明示「提交与 `npm run gate` 留主会话收口」)。
|
|
255
|
+
3. **行为面影响(需对外说明——本节即该说明的落点)**:DSH 侧「【本会话近期对话】」块(`src/hooks.ts:658` → `formatSessionWindow`,定义 `:384-406`)此前实际拿到的是**全进程窗口**(跨会话混入),修后才是**本会话**窗口。三个边界(r1 补全,均读码核实):
|
|
256
|
+
- **非空面**:DSH 写侧每轮把 user/assistant 消息以 `{session: sid}` 写进窗口(`src/hooks.ts:698/772`),故本会话**已有窗口写入**时该块照常非空。
|
|
257
|
+
- **下界面**:若本会话尚无窗口条目(如新会话首轮)或条目全为空文本(`formatSessionWindow` 只收非空 preview,`:395`),返回空串 → **整块不注入**(`:660` 的 `if (winText)`)。即行为变化 =「有内容但可能是他会话」→「本会话内容**或无块**」——原稿「照常非空」未覆盖此下界,r1 更正。
|
|
258
|
+
- **`sid` 为空面(依据更正)**:读侧调用传的是 `sid`(`:658`),未观测到会话时 `sessionIdOf` 返回**空串**(`:416-420`;注释 `:413-415`「空串在上游一律等同『不分会话』(退回旧行为)」)→ `sessionRecall('')` falsy → 退回**全局窗口**(旧行为)。**注意:这与写侧兜底常量不是同一个值**——写侧此时标 `UNASSIGNED_SESSION = 'unassigned'`(**非空**,`src/lib/session_state.ts:37`;`src/hooks.ts:698`)。原稿把两者写成同一来源、读者无法判断常量取值——r1 更正为「依据是 `sessionIdOf` 的空串约定,而非 `UNASSIGNED_SESSION` 等于空串」。由此还产生一档既有(修前即如此、本轮未动)的口径差:`sid` 空时读侧取**全局**、而写侧标 `'unassigned'`——该桶事件在全局读下可见、在任何指定会话读下不可见。
|
|
259
|
+
4. **本守卫相对契约的清单变化**(R65.4b/5a/5b/6a/6b/8/9)见 §五 偏离声明——**新增 3 条**(R65.4b/8/9,加严)、**拆分 2 处**(R65.5/6,覆盖强度不变)、**零删减**(r1 更正:原「只加不减」把拆分说成了加严)。
|
|
260
|
+
|
|
261
|
+
---
|
|
262
|
+
|
|
263
|
+
## 八、验证读数与范围(逐条命令)
|
|
264
|
+
|
|
265
|
+
| 项 | 命令 | 读数 |
|
|
266
|
+
|---|---|---|
|
|
267
|
+
| 修前复现 | `PYTHONPATH=<仓根> python -X utf8 %TEMP%/issue65_probe.py 修前` | 假 id/A/B 均 5 条;裸事件在 `session=A` 下可见(§二) |
|
|
268
|
+
| 修后矩阵 | 同探针 `… 修后` | 0 / 2 / 1 / 5 / 5 / 5 / 5;裸事件 A 下不可见(§四) |
|
|
269
|
+
| 守卫正向 | `python -X utf8 -m md_cg.test_p45_session_identity` | **42 通过 / 0 失败**,退出码 0 |
|
|
270
|
+
| 变异自证 | `python -X utf8 -m md_cg.test_p45_session_identity --self-proof` | 六腿逐条 **恰好**命中期望红项;复原后全套 rc 0;退出码 0 |
|
|
271
|
+
| ANCHOR-MISS | 进程内漂移探针(见 §5.4) | `ANCHOR-MISS` + 返回值 **2** |
|
|
272
|
+
| 定向回归(ask 指定范围) | `python -X utf8 scripts/run_tests.py md_cg` | **249/249 通过,3 跳过**,退出码 0 |
|
|
273
|
+
| python 全量 | `python -X utf8 scripts/run_tests.py`(无参) | **359/359 通过、5 跳过(依赖缺失/平台不符)**,退出码 0(〔工作流采集〕与〔本报告实跑〕复跑一致) |
|
|
274
|
+
| 容器栈一(rust:bookworm) | `docker run --rm -v <仓根>:/work -w /work rust:bookworm bash scripts/linux_verify.sh full`(用法行 `scripts/linux_verify.sh:3`;python 全量+断言判别力自证+smoke 22/0+body_e2e_smoke) | 〔工作流采集〕`结果: 22 pass / 0 fail`、`[PASS] smoke_test (linux)`、`=== 汇总: 49 pass / 0 fail ===`,**退出码 0** |
|
|
275
|
+
| 容器栈二(node:22-bookworm) | `npm install --include=dev && npm run build && node --import tsx --test test/*.test.ts`(口径见 `docs/eval/发布14_身体×脑组合_v1.0.md:133-134`) | 〔工作流采集〕TS 144 项 **141 通过 / 0 失败 / 3 跳过**;`# cancelled 0 | # skipped 3 | # todo 0 | # duration_ms 12224.823459`,**退出码 0** |
|
|
276
|
+
| 单点复核(ask 点名的三个面) | `python -X utf8 -m md_cg.test_lifecycle_retire_leak` / `-m md_cg.test_n202_session_notes_visibility` / `-m md_cg.test_p45_session_identity` | 46/0、36/0、42/0(全 rc 0) |
|
|
277
|
+
| 改动面 | `git status --porcelain` | `M md_cg/mdcos.py`、`M md_cg/test_p45_session_identity.py`(+本报告新增) |
|
|
278
|
+
|
|
279
|
+
**未跑(如实声明)**:`npm run gate`、提交与推送——按工作流真源(`.zcode/workflow-drafts/issue65-…dwf.ts:3`「提交与 npm run gate 留主会话收口」、`:244`)**由主会话执行**,本工作流不含该步。**来源声明**:python 全量与容器两栈由**工作流脚本阶段**实跑(脚本 `:159-208`;实施/复核两 ask 均明示「不要跑全量与容器——脚本随后会跑」,故实施回报中的「未跑」与本表不矛盾)。本报告撰写 ask 实跑了守卫正向与变异自证(§5.2)与 python 全量(SUMMARY 359/359、5 跳过、退出码 0,与脚本读数一致);**未重跑**容器两栈与 gate。
|
|
280
|
+
|
|
281
|
+
**硬边界遵守**:未动任何在役数据根(全部读数取自系统临时目录合成库);未 `git add/commit/push`;未改 `data/policy.json`;未削弱任何 forbidden 规则。
|
|
282
|
+
|
|
283
|
+
---
|
|
284
|
+
|
|
285
|
+
## 九、残留风险
|
|
286
|
+
|
|
287
|
+
1. **空窗口面**:指定会话时,`meta.session` 缺失的事件一律不可见(契约:最坏空窗口优于错块)。命中条件 = 写侧未打会话标记的**存量/跨进程**事件;本仓三处写侧(DSH `hooks.ts:698/772`、MCP/安全档 `mdcos.py:5087` setdefault、zcode sync)均已打点,故**估计**现役数据面风险≈0。**「≈0」的量化边界(r1 补)**:本报告**未对现役库做盘点**——该估计的依据只有两条:①三处写侧代码事实;②复核实测存量裸事件在指定会话下被丢弃(§6.2)。若需确数,须对在役数据根执行一次「缺 `meta.session` 事件」盘点(本轮未做)。若未来出现新的写入通道,**须同样打 `meta.session`**(否则该通道的报文对本会话不可见)。
|
|
288
|
+
2. **`"*"` 的传播面**:`"*"` 是「显式跨会话汇总」的**唯一**开关,与 falsy 语义相反;二者已写进 docstring 与生效条件注释,但**客户端(TS)尚未声明**该口径(见 §七-2/3)——建议随注释同步一并补齐。
|
|
289
|
+
|
|
290
|
+
---
|
|
291
|
+
|
|
292
|
+
## 十、读者反馈处置(v1.0 → v1.0-r1)
|
|
293
|
+
|
|
294
|
+
读者(未参与本次修复)只读 v1.0 后给出 5 条 unclear / 6 条 unsupported / 7 条 nextQuestions;本节逐条处置,落点用 r1 章节号引用。**本轮修订只改本报告文件**(`git status --porcelain` 仍为两件 `M` + 本报告),新增证据全部来自读码(引用处已就地标注)。
|
|
295
|
+
|
|
296
|
+
### 10.1 unclear(读不懂)——5 条
|
|
297
|
+
|
|
298
|
+
| # | 反馈要点 | 处置(落点) |
|
|
299
|
+
|---|---|---|
|
|
300
|
+
| 1 | §6.1① 「返回前缀集合」未定义;与「前缀延伸会话」「真前缀」字面相近 | 改称**事件短标签集合**并给定义(文本前 2 字,同守卫 `[:2]`),注明与另两处**会话 id**「前缀」用法无关(§6.1①) |
|
|
301
|
+
| 2 | §七-3 由 `sessionTag = {session: UNASSIGNED_SESSION}` 推 `sessionRecall('')`,未证明常量是空串 | **依据更正**:常量实为 `'unassigned'`(非空,`src/lib/session_state.ts:37`);但**读侧传的是 `sid`**(`sessionIdOf` 空值=空串,`src/hooks.ts:416-420`)——结论(空→退回全局窗口)不变,依据改正(§七-3) |
|
|
302
|
+
| 3 | 结论段两套计数压缩:9 个编号指 12 条;ANCHOR-MISS 易读成六腿之一 | 拆分表述:「R65.1–R65.9 编号、12 条(4/5/6 拆两态)」;ANCHOR-MISS 明标**不在六腿之内**(结论段、§5.4) |
|
|
303
|
+
| 4 | §一「缺口唯一」与腿⑥「同类缺口成立」并读范围不明 | 显式限定「**限本 issue 面内**」+就地给出边界(§一) |
|
|
304
|
+
| 5 | 同一批标签多库复用;A 会话读数散见 5/2/3/2 条无库标注 | 补**库清单(库①–④)**;各读数标库号;3 条 vs 2 条差异归因库构成(§2.1 表、§四 注、§6.2) |
|
|
305
|
+
|
|
306
|
+
### 10.2 unsupported(文本自身支撑不了)——6 条
|
|
307
|
+
|
|
308
|
+
| # | 反馈要点 | 处置(落点) |
|
|
309
|
+
|---|---|---|
|
|
310
|
+
| 1 | 「契约」原文未随报告给出,「与契约一致/未削弱契约」无从核对 | 补**契约来源与存放位置**:契约=工作流 `contract` 运行参数(真源 `.zcode/workflow-drafts/issue65-…dwf.ts:15-18`,取值不落仓);给出三处**可核对转记面**(§三 首段) |
|
|
311
|
+
| 2 | 「均为加严」把 3 条新增与 3 处拆分混为一谈(「只加不减」同此) | 更正为两类:**新增 3 条=加严 / 拆分 2 处=覆盖强度不变 / 零删减**(§5.1 偏离声明、§七-4) |
|
|
312
|
+
| 3 | 「recall 无会话参数/需动签名」与腿⑥ payload(recall 收了 session)矛盾 | **更正**:`recall` 本体有 `session` 形参(`md_cg/mdcos.py:1850`);该面可修、**不需动签名**;原措辞有误(§七-1、§6.3-1) |
|
|
313
|
+
| 4 | 「写侧打点 ⇒ 照常非空」未覆盖下界面 | 补**三边界**:非空面/下界面(本会话无窗口条目→整块不注入,`src/hooks.ts:660`)/空 `sid` 面(§七-3) |
|
|
314
|
+
| 5 | 危害链「被压缩检查点记入」无证据位 | 补**两条代码链**:注入面 `src/hooks.ts:663-667`;压缩重建面 `zcode_compact_hook.py:67-71 → sync_zcode_session.py:330,344-351`;并明标「运行时混入未观测」(§一) |
|
|
315
|
+
| 6 | §九-1「风险≈0」无量化依据,与承认存量裸事件并存 | 明标**未盘点**、依据仅两条;确数须对在役库盘点(§九-1) |
|
|
316
|
+
|
|
317
|
+
### 10.3 nextQuestions(读者会接着问)——7 条
|
|
318
|
+
|
|
319
|
+
| # | 问题 | 答复/落点 |
|
|
320
|
+
|---|---|---|
|
|
321
|
+
| 1 | 契约原文能否附录 | §三 首段:取值不落仓;附录须向主会话索取运行参数值 |
|
|
322
|
+
| 2 | TS 注释谁同步、`src/` 侧口径、别处是否同源 | §七-2:另有产物副本 `lib/lib/mdcg_client.d.ts:212-214`(`npm run build` 再生);全仓无第三处(含测试面);由**主会话收口** |
|
|
323
|
+
| 3 | gate 与提交/推送由谁、何时执行 | §八 末段:工作流真源 `:3/:244` 明示**留主会话收口**;本工作流不含该步 |
|
|
324
|
+
| 4 | recall 尾部是否另开 issue、是否一次收口 | §七-1:本轮**未创建跟踪件**,属主会话决策;修法已明确(一行调用点过滤、不动签名),可随该 issue 一次收口 |
|
|
325
|
+
| 5 | 复核驱动已删,日后如何复现 | 守卫正向/自证**可随时重跑**(命令见 §5.2);复核自写驱动(`rev65_*.py`)与实验根已删、**未落仓**——原路径不可重放,需按 §6.1/§6.2 判据重写驱动 |
|
|
326
|
+
| 6 | falsy 静默退回全局是否有告警/审计 | **无告警/审计计划**(本轮未加)——falsy 为存量兼容面;如需收窄或告警须另立需求 |
|
|
327
|
+
| 7 | DSH 块说明写在哪里、由谁出 | §七-3(本报告该节即说明落点,随报告对外) |
|
|
328
|
+
|
|
329
|
+
**未采纳的读者意见**:无——18 条全部有对应处置(其中 4 条为**事实性更正**:10.1-2(结论依据换正)、10.2-2(「均加严」改两类)、10.2-3(`recall` 签名)、10.2-4(照常非空补下界);其余为补证据/补定义/补限定,非仅措辞)。
|
|
@@ -133,8 +133,16 @@
|
|
|
133
133
|
4. 容器双栈:栈一 `rust:bookworm` `bash scripts/linux_verify.sh full`;栈二 `node:22-bookworm`
|
|
134
134
|
`npm install --include=dev && npm run build && node --import tsx --test test/*.test.ts`。
|
|
135
135
|
|
|
136
|
-
`npm publish` 由使用者执行(编排侧无发布密钥,2026-10-04
|
|
137
|
-
|
|
136
|
+
`npm publish` 由使用者执行(编排侧无发布密钥,2026-10-04 指示)。
|
|
137
|
+
|
|
138
|
+
**发布后回验(2026-10-06 实测)**:
|
|
139
|
+
|
|
140
|
+
- registry 同步:受理后轮询 tarball 端点(每 20s × 15 次)→ **200**(处理窗口约 5 分钟);
|
|
141
|
+
- `python scripts/check_publish_artifact.py --registry 0.8.0` → **VERDICT=PASS**(FAIL 规则数=0;
|
|
142
|
+
R1 凭据面 0/0(4 条 NOTE 为测试件 dummy 形态)、R2/R3/R5/R7 PASS;registry 记录发布时间
|
|
143
|
+
2026-10-06T15:49:14Z、**1592 文件**、sha1=`5cb7b88dd75a5e475cdff69577dbb909fba3e980`);
|
|
144
|
+
- 独立 hash 对拍:本地 `npm pack` 与 registry tarball **逐字节一致**
|
|
145
|
+
(25,704,381 字节;sha256 `23c73a245790cb26504c554df292402d6c78b6101fa8baa46baac1f7ca518ec0`)。
|
|
138
146
|
|
|
139
147
|
## 九、复跑命令(中性占位)
|
|
140
148
|
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# 发布 v0.8.1:session_recall 会话隔离修复(issue #65)+ 完整身体库展示位
|
|
2
|
+
|
|
3
|
+
> 版本 0.8.1 | 提交 `733a4ad3` 及其前 2 笔(版本递增与本报告为其后一笔)| 2026-10-07
|
|
4
|
+
> 前版 v0.8.0(身体×脑组合)
|
|
5
|
+
|
|
6
|
+
## 一、本版范围(v0.8.0 → v0.8.1 共 3 笔 + 本版提交)
|
|
7
|
+
|
|
8
|
+
| # | 提交 | 内容 |
|
|
9
|
+
|---|---|---|
|
|
10
|
+
| 1 | `733a4ad3` | **issue #65 修复**:`session_recall` ③ 近期事件段按会话隔离——多窗口并发时的静默跨会话污染消除 |
|
|
11
|
+
| 2 | `2c7da0f7` | README:完整身体库 [lingshu](https://github.com/FuRongJun-1999/lingshu) 展示位三落点(平台全景/架构节「可以展示什么」八能力面/文档导航表) |
|
|
12
|
+
| 3 | `79104b08` | 发布14 报告回填(0.8.0 registry 回验读数:1592 文件/hash 逐字节对拍、dist-tags.latest=0.8.0) |
|
|
13
|
+
| 4 | 本版提交 | 版本递增(8 处)+ README 版本表 + 本报告 |
|
|
14
|
+
|
|
15
|
+
(issue #64 修复(`e5723d35`)已随 v0.8.0 发布,不在本版范围。)
|
|
16
|
+
|
|
17
|
+
## 二、issue #65 修复(本版核心)
|
|
18
|
+
|
|
19
|
+
外部报告(ducc239,对 v0.7.5 实测)——`session_recall` 的「③ 近期事件」段未按会话隔离:
|
|
20
|
+
③ 段调 `recent_events` 不传会话(`recent_events` 无 session 形参、仅 private/secret 档做
|
|
21
|
+
会话归属判断),recent 段恒为**全进程窗口**——多窗口并发时 A 窗口的注入包混入 B 窗口报文
|
|
22
|
+
(被模型当用户输入处理、可被压缩检查点记入,**静默污染**)。
|
|
23
|
+
|
|
24
|
+
**修前复现**(隔离库,MCP 真实入口与直调两路径读数一致):假会话 id → 返回 3 条全库事件;
|
|
25
|
+
会话 A → 混入「会话B的第一句话」;缺 `meta.session` 的无归属事件在两会话下均漏入。
|
|
26
|
+
|
|
27
|
+
**修复**(`mdcos.py` ③ 段):取 `evs` 后按会话过滤——真实会话 id → `meta.session` 严格相等
|
|
28
|
+
(缺 `meta.session` 者丢弃:最坏空窗口,杜绝错块);显式 `session="*"` → 不过滤(跨会话汇总,
|
|
29
|
+
与 `stg` 的 `"*"` 同款语义);falsy(None/"")→ 退回旧行为(全局窗口);`strip` 防空白包裹
|
|
30
|
+
的星号。docstring 同步口径段(recent 跟会话走;notes 已跟;tasks/goals 有意跨会话)。
|
|
31
|
+
|
|
32
|
+
**验证**:守卫扩 `test_p45_session_identity.py` **R65 组 12 断言(42/42)**——含 MCP
|
|
33
|
+
`_session_call` 端到端、缺 meta 两态、`"*"`/`" "`/None/""、compact 回归、文档口径在位;
|
|
34
|
+
**定点变异自证六腿**(删过滤/删星号例外/去 strip/放行缺 meta/去 falsy 守卫/文档反转,
|
|
35
|
+
红项数 5/2/1/4/2/1 各恰等于登记值、每腿红集为唯一指纹)+ ANCHOR-MISS 漂移探针 fail-closed;
|
|
36
|
+
**独立复核 ACCEPT**(双树对照、oracle 直读 `_recent.jsonl` 对拍、退化探针非空池防空转、
|
|
37
|
+
old/new 逐位、多实例形态)。报告见 `docs/eval/issue65_session_recall_会话隔离_核验与修复_v1.0.md`。
|
|
38
|
+
|
|
39
|
+
## 三、完整身体库展示位(README)
|
|
40
|
+
|
|
41
|
+
[FuRongJun-1999/lingshu](https://github.com/FuRongJun-1999/lingshu)(独立公开仓 · MIT)在
|
|
42
|
+
README 三落点展示:平台全景节广播段、架构节「完整身体库(lingshu)可以展示什么」小节
|
|
43
|
+
(八能力面表:身体×脑闭环/世界模型七层闭环/世界推演循环/3D 场景与角色/白箱生成与读回/
|
|
44
|
+
自研蜂窝神经网络/记忆可视化/理论地基+上手两行)、文档导航表加行;原「AEIS 仅作可选身体
|
|
45
|
+
能力后端」表述更新为指向对外完整版 lingshu。
|
|
46
|
+
|
|
47
|
+
## 四、工程面读数(发布时点实测)
|
|
48
|
+
|
|
49
|
+
| 关卡 | 读数 |
|
|
50
|
+
|---|---|
|
|
51
|
+
| python 全量 | **359/359 通过**(5 跳过) |
|
|
52
|
+
| TS 全量 | 144 项(**143 pass / 0 fail / 1 skip**) |
|
|
53
|
+
| `npm run gate` | **12 腿 PASS**(含 cargo 35、rank_parity 10/10、路径门禁、body_e2e_smoke) |
|
|
54
|
+
| 出货冒烟 | tarball `furongjun1999-dsh-memory-0.8.1.tgz`、清单 **1593 条目**、serverInfo `0.8.1`、tools `['cg','stg']`、真握手 rc=0 |
|
|
55
|
+
| 容器栈一(rust:bookworm) | `linux_verify.sh full` 汇总 **49 pass / 0 fail**(含 smoke 22/0) |
|
|
56
|
+
| 容器栈二(node:22-bookworm) | TS 144 项 **141 pass / 0 fail / 3 skip** |
|
|
57
|
+
| registry 回验 | 待使用者 `npm publish` 后回填(`check_publish_artifact --registry 0.8.1` + hash 对拍) |
|
|
58
|
+
|
|
59
|
+
## 五、边界与如实声明
|
|
60
|
+
|
|
61
|
+
- **登记不回**:`recall(include_recent=True)` 的近期尾巴(`mdcos.py:1952`)同缺口事实成立——
|
|
62
|
+
显式开关面且无会话参数可用,不随本修复动(issue #65 报告与修复报告均如实注明)。
|
|
63
|
+
- **TS 注释面**:`src/` 侧注释与构建产物 `lib/lib/mdcg_client.d.ts` 为文档漂移项(随 build 再生),
|
|
64
|
+
不影响行为。
|
|
65
|
+
- **DSH 侧实际升级验证**:报告者本机补丁的撤除与升级验证由使用者侧进行。
|
|
66
|
+
- **历史面**:issue #65 报告提及的 v0.7.5 行为均以 HEAD 复核读数为准(本版修复后行为矩阵见 §二)。
|
|
67
|
+
|
|
68
|
+
## 六、发布三关(复跑口径)
|
|
69
|
+
|
|
70
|
+
1. `python -X utf8 scripts/run_tests.py --jobs 4` → 359/359;
|
|
71
|
+
2. `npm run build` + `npm test` → 143/0/1;
|
|
72
|
+
3. `npm run gate` → 12 腿 PASS;
|
|
73
|
+
4. 容器双栈:栈一 `rust:bookworm` `bash scripts/linux_verify.sh full`;栈二 `node:22-bookworm`
|
|
74
|
+
`npm install --include=dev && npm run build && node --import tsx --test test/*.test.ts`。
|
|
75
|
+
|
|
76
|
+
`npm publish` 由使用者执行(编排侧无发布密钥,2026-10-04 指示);发布后 registry 回验
|
|
77
|
+
(tarball/hash 对拍)读数回填本报告。
|
|
78
|
+
|
|
79
|
+
## 七、复跑命令(中性占位)
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
# 本版核心修复面
|
|
83
|
+
python -X utf8 -m md_cg.test_p45_session_identity # 42/42(含 R65 组)
|
|
84
|
+
python -X utf8 -m md_cg.test_p45_session_identity --self-proof # 定点变异自证(六腿)
|
|
85
|
+
# 会话隔离行为矩阵(隔离库探针,参考 issue65 修复报告 §四)
|
|
86
|
+
# registry 回验(publish 后)
|
|
87
|
+
python scripts/check_publish_artifact.py --registry 0.8.1
|
|
88
|
+
```
|
package/md_cg/mdcos.py
CHANGED
|
@@ -3231,7 +3231,7 @@ class MdCGOS(MdCG):
|
|
|
3231
3231
|
out.sort(key=lambda n: (-n["created_at"], str(n.get("id") or "")))
|
|
3232
3232
|
return out[:max(1, int(limit or 5))]
|
|
3233
3233
|
|
|
3234
|
-
# 生效条件:limit 经 max(1,min(int(limit or 5),50))、budget_tokens 经 max(200,int(budget_tokens or 1200)) 归一后逐段取数(include_state 为真才取 self_state;会话要点段、未解问题段与台账两段(goals/tasks)恒剔除 lifecycle.is_archived 条目——退役不参与默认注入;台账段取全量窗过滤后再截断 `_PACK_ROWS` 条,`tasks.active_total/done_total`
|
|
3234
|
+
# 生效条件:limit 经 max(1,min(int(limit or 5),50))、budget_tokens 经 max(200,int(budget_tokens or 1200)) 归一后逐段取数(include_state 为真才取 self_state;会话要点段、未解问题段与台账两段(goals/tasks)恒剔除 lifecycle.is_archived 条目——退役不参与默认注入;台账段取全量窗过滤后再截断 `_PACK_ROWS` 条,`tasks.active_total/done_total` 取剔除后的全窗计数;③ recent 段按会话过滤(issue #65):session 为真值且 str(session).strip() != "*" 时只保留 (r.get("meta") or {}).get("session") == session 的事件(严格相等;缺 meta.session 者丢弃——最坏空窗口),显式 "*" 不过滤(跨会话汇总),falsy 会话(None/"")退回全局窗口;会话要点段的 tags 过滤与台账段的不过滤口径不变),每段异常只把段名追加进 degraded,再由 while 循环按预算交替裁 recent/notes 尾部、任务段最后才裁并置 tasks_truncated。
|
|
3235
3235
|
def session_recall(self, session=None, limit=5, recent_limit=10,
|
|
3236
3236
|
budget_tokens=1200, include_state=True):
|
|
3237
3237
|
"""按需恢复:一次调用返回「可续接的上下文包」(替代 hook 自动注入)。
|
|
@@ -3244,6 +3244,15 @@ class MdCGOS(MdCG):
|
|
|
3244
3244
|
「还在做的」与「刚做完的」,不必先想到去查。任务属结构层、跨会话稳定,
|
|
3245
3245
|
故**不按 session 过滤**——工程台账跟着工程走,不跟着会话走。
|
|
3246
3246
|
|
|
3247
|
+
**会话口径(issue #65,2026-10-07 修)**:③ recent 段跟会话走——`session`
|
|
3248
|
+
为真值且非 `"*"` 时只回 `meta.session` 与本会话**严格相等**的事件(缺
|
|
3249
|
+
`meta.session` 者被丢弃:最坏是空窗口,绝不把别的会话的报文塞进本会话
|
|
3250
|
+
上下文——宿主把 recent 当用户输入处理,混入即静默污染,且可被压缩检查点
|
|
3251
|
+
记入);显式 `"*"` = 跨会话汇总(与 stg 面 `view_session` 的 `"*"` 同款
|
|
3252
|
+
语义);`session` 为 None/空串(falsy)时退回旧的全局窗口行为。口径分工:
|
|
3253
|
+
① 会话要点段本就按 `session:{sid}` tag 过滤;目标段与任务段是**有意跨
|
|
3254
|
+
会话**的工程面(见上段),不在此列。
|
|
3255
|
+
|
|
3247
3256
|
**退役口径(本包统一)**:本 op 是**注入面**(内容直接回给调用方/进上下文),
|
|
3248
3257
|
故四个取数段(notes / goals / tasks[active/done 两清单]/ unresolved;合计
|
|
3249
3258
|
五个清单)一律剔除
|
|
@@ -3307,6 +3316,20 @@ class MdCGOS(MdCG):
|
|
|
3307
3316
|
# ③ 近期事件(原始滚动窗口)
|
|
3308
3317
|
try:
|
|
3309
3318
|
evs = self.recent_events(limit=max(1, int(recent_limit or 10)))
|
|
3319
|
+
# issue #65(外部报告 by ducc239,对 v0.7.5 实测,本 workflow 修前复现):
|
|
3320
|
+
# 本段此前**不传会话**,而 `MdCGSecure.recent_events` 无 session 形参
|
|
3321
|
+
# (只有 private/secret 档做会话归属判断、默认档全放行)⇒ recent 恒为
|
|
3322
|
+
# 全进程窗口:多窗口/多会话并发时 A 的续接包里混入 B 的报文(宿主把
|
|
3323
|
+
# recent 当用户输入处理,可被压缩检查点记入——静默污染,不报错)。
|
|
3324
|
+
# 语义矩阵(与 `session_compact` 的 `if session:` 过滤同族对齐,那边已有正确形态):
|
|
3325
|
+
# 真值且非 "*" → 只回本会话(`meta.session` **严格相等**;缺
|
|
3326
|
+
# `meta.session` 的事件被丢弃——最坏空窗口,杜绝错块);
|
|
3327
|
+
# 显式 "*" → 不过滤(跨会话汇总合法用法,与 stg 面 `view_session`
|
|
3328
|
+
# 的 "*" 同款语义);
|
|
3329
|
+
# falsy(None/"")→ 退回旧行为(全局窗口,存量调用面零迁移)。
|
|
3330
|
+
# `strip()` 防空白包裹的 "*"(" * " 也按汇总处理)。
|
|
3331
|
+
if session and str(session).strip() != "*":
|
|
3332
|
+
evs = [r for r in evs if (r.get("meta") or {}).get("session") == session]
|
|
3310
3333
|
pack["recent"] = [{"role": r.get("role"),
|
|
3311
3334
|
"text": (r.get("text") or "")[:300],
|
|
3312
3335
|
"t": r.get("t")} for r in evs]
|
|
@@ -17,16 +17,31 @@
|
|
|
17
17
|
E 会话要点:session_note 缺省用 Principal.session;recall 带回会话自我切片
|
|
18
18
|
F 审计归因:_audit.jsonl 条目带 harness/unit
|
|
19
19
|
G MCP 入口:_self_state_call 缺省会话透传 + index 快捷反查
|
|
20
|
+
R65 issue #65:session_recall ③ 近期事件段的**会话隔离**(外部报告 by ducc239,
|
|
21
|
+
对 v0.7.5 实测;本 workflow 修前复现)——假会话 id 空窗口 / A 只回 A /
|
|
22
|
+
B 只回 B / "*" 与 None 退回全局 / 缺 meta.session 者被丢弃 / compact 回归 /
|
|
23
|
+
MCP 入口端到端 / 文档口径在位;外加**定点变异自证**模式。
|
|
20
24
|
|
|
21
|
-
运行:python -m md_cg.test_p45_session_identity
|
|
25
|
+
运行:python -m md_cg.test_p45_session_identity # 正常跑
|
|
26
|
+
python -m md_cg.test_p45_session_identity --self-proof # 变异自证
|
|
27
|
+
退出码:0 全绿 | 1 断言失败 | 2 ANCHOR-MISS(锚点漂移,fail-closed)
|
|
28
|
+
|
|
29
|
+
变异自证的基线源 = **运行中的实现源码**(`inspect.getsource` 就地变异、就地复原),
|
|
30
|
+
**不读 git**——把基线绑到某个提交,下一次改动即失效(本仓已有两次教训)。
|
|
22
31
|
"""
|
|
23
32
|
from __future__ import annotations
|
|
24
33
|
|
|
34
|
+
import contextlib
|
|
35
|
+
import inspect
|
|
36
|
+
import io
|
|
25
37
|
import json
|
|
26
38
|
import os
|
|
39
|
+
import sys
|
|
27
40
|
import tempfile
|
|
41
|
+
import textwrap
|
|
28
42
|
|
|
29
43
|
from .mdcos import MdCGOS, MdCGSecure
|
|
44
|
+
from . import mdcos as _mdcos
|
|
30
45
|
from .security import Principal
|
|
31
46
|
from . import self_state as ss
|
|
32
47
|
from . import mcp_server as ms
|
|
@@ -65,6 +80,14 @@ def _restore(old):
|
|
|
65
80
|
|
|
66
81
|
|
|
67
82
|
def main():
|
|
83
|
+
global PASS, FAIL
|
|
84
|
+
if "--self-proof" in sys.argv:
|
|
85
|
+
return _r65_self_proof()
|
|
86
|
+
_rc = _r65_anchor_preflight() # 锚点漂移 → ANCHOR-MISS + 退出码 2
|
|
87
|
+
if _rc:
|
|
88
|
+
return _rc
|
|
89
|
+
PASS = FAIL = 0 # 计数器复位:变异自证模式内会复用本函数重跑
|
|
90
|
+
del FAILS[:]
|
|
68
91
|
# ---------- A. Principal 归因字段 ----------
|
|
69
92
|
print("\n[A] Principal 归因字段(与授权正交)")
|
|
70
93
|
p0 = Principal(actor="a0")
|
|
@@ -207,6 +230,10 @@ def main():
|
|
|
207
230
|
check("G3 refresh 经 MCP 缺省登记会话",
|
|
208
231
|
bool(out2.get("ok")))
|
|
209
232
|
|
|
233
|
+
# ---------- R65. 近期事件段的会话隔离(issue #65) ----------
|
|
234
|
+
print("\n[R65] session_recall ③ 近期事件:按会话隔离(issue #65;修前=全进程窗口)")
|
|
235
|
+
_r65_group()
|
|
236
|
+
|
|
210
237
|
print("\n" + "=" * 68)
|
|
211
238
|
print(f"通过 {PASS} / 失败 {FAIL}")
|
|
212
239
|
if FAILS:
|
|
@@ -215,6 +242,226 @@ def main():
|
|
|
215
242
|
return FAIL
|
|
216
243
|
|
|
217
244
|
|
|
245
|
+
# ======================================================================
|
|
246
|
+
# R65(issue #65):session_recall ③ 近期事件段的会话隔离
|
|
247
|
+
# ======================================================================
|
|
248
|
+
# 缺陷(外部报告 by ducc239,对 v0.7.5 实测;本 workflow 修前复现):③ 段取
|
|
249
|
+
# `self.recent_events(limit=…)` 时**不传会话**,而 `MdCGSecure.recent_events`
|
|
250
|
+
# 无 session 形参(只有 private/secret 档判会话归属,默认档全放行)⇒ recent 恒为
|
|
251
|
+
# 全进程窗口:多窗口/多会话并发时 A 的续接包里混入 B 的报文(宿主把它当用户输入
|
|
252
|
+
# 处理,可被压缩检查点记入——静默污染)。同族既有正确形态(旁证):`session_compact`
|
|
253
|
+
# 的 `if session: evs = [...]`、`_session_notes` 的 `session:{sid}` tag 过滤、
|
|
254
|
+
# MCP `_session_call` 已传 `session=`——缺口只在 session_recall ③ 段一处。
|
|
255
|
+
R65_A = "session-AAA-0000"
|
|
256
|
+
R65_B = "session-BBBB-0000"
|
|
257
|
+
R65_AX = R65_A + "-long" # A 的**前缀延伸**会话:钉「严格相等」判据
|
|
258
|
+
R65_FAKE = "session-zzzz-nope-0000"
|
|
259
|
+
R65_BARE = "N0 无会话归属的裸事件"
|
|
260
|
+
R65_GLOBAL = ("A1", "A2", "AX", "B1", "N0") # 全局窗口(不过滤)读数
|
|
261
|
+
|
|
262
|
+
# 五条事件:A×2 / B×1 / 裸×1(缺 meta.session)/ A 前缀延伸×1。
|
|
263
|
+
# 裸事件经**基类** remember_event 直写注入——安全档写入会把 meta.session
|
|
264
|
+
# setdefault 成本进程会话(mdcos.py:5087),测不到「缺 meta.session」形态;而读侧
|
|
265
|
+
# 对无归属事件的丢弃正是本 issue 的契约要求(存量数据 / 非安全档写入形态)。
|
|
266
|
+
R65_EVENTS = (
|
|
267
|
+
("user", "A1 会话A的第一句话", {"session": R65_A}),
|
|
268
|
+
("assistant", "A2 会话A的第二句话", {"session": R65_A}),
|
|
269
|
+
("user", "B1 会话B的第一句话", {"session": R65_B}),
|
|
270
|
+
("user", R65_BARE, {}),
|
|
271
|
+
("user", "AX 会话A前缀延伸会话", {"session": R65_AX}),
|
|
272
|
+
)
|
|
273
|
+
|
|
274
|
+
# 真源文件快照(导入时取一次):变异自证把 `MdCGOS.session_recall` 换成 exec 出来的
|
|
275
|
+
# 临时函数,那种函数**没有源文件**(`getsource` 会抛 OSError)——故注释行检查必须锚在
|
|
276
|
+
# 导入时记下的真源路径上;docstring 检查则走 `__doc__`(变异体也带,M6 才能转红)。
|
|
277
|
+
_R65_FILE = inspect.getsourcefile(MdCGOS.session_recall)
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
def _r65_lib():
|
|
281
|
+
"""隔离库(系统临时目录合成库;不留仓内件)。"""
|
|
282
|
+
cg = MdCGOS(tempfile.mkdtemp(prefix="mdcg_p45_r65_"))
|
|
283
|
+
for role, text, meta in R65_EVENTS:
|
|
284
|
+
cg.remember_event(role, text, meta=meta)
|
|
285
|
+
return cg
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
def _r65_recent(cg, session):
|
|
289
|
+
"""续接包 recent 段的事件短标签(前 2 字),排序后返回(顺序不参与判据)。"""
|
|
290
|
+
pack = cg.session_recall(session=session, recent_limit=20,
|
|
291
|
+
budget_tokens=4000, include_state=False)
|
|
292
|
+
return sorted((r.get("text") or "")[:2] for r in (pack.get("recent") or []))
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
def _r65_cond_line():
|
|
296
|
+
"""`def session_recall` 上方紧邻的「生效条件:」注释行(本仓惯例:改函数须同步)。"""
|
|
297
|
+
lines = io.open(_R65_FILE, encoding="utf-8").read().splitlines()
|
|
298
|
+
for i, ln in enumerate(lines):
|
|
299
|
+
if ln.strip().startswith("def session_recall("):
|
|
300
|
+
j = i - 1
|
|
301
|
+
while j >= 0 and not lines[j].strip():
|
|
302
|
+
j -= 1
|
|
303
|
+
return lines[j] if j >= 0 else ""
|
|
304
|
+
return ""
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
def _r65_group():
|
|
308
|
+
"""R65 组断言(变异自证模式复用同一组,故不依赖模块级实例)。"""
|
|
309
|
+
cg = _r65_lib()
|
|
310
|
+
check("R65.1 假会话 id → recent 空窗口(修前=全库五条;杜绝错块)",
|
|
311
|
+
_r65_recent(cg, R65_FAKE) == [], _r65_recent(cg, R65_FAKE))
|
|
312
|
+
check("R65.2 会话 A → recent 恰为 A 的两条(严格相等:不含 B/裸事件/A 的前缀延伸会话)",
|
|
313
|
+
_r65_recent(cg, R65_A) == ["A1", "A2"], _r65_recent(cg, R65_A))
|
|
314
|
+
check("R65.3 会话 B → recent 恰为 B 的一条",
|
|
315
|
+
_r65_recent(cg, R65_B) == ["B1"], _r65_recent(cg, R65_B))
|
|
316
|
+
check('R65.4a session="*" → 不过滤(跨会话汇总合法用法)',
|
|
317
|
+
_r65_recent(cg, "*") == list(R65_GLOBAL), _r65_recent(cg, "*"))
|
|
318
|
+
check('R65.4b session=" * "(空白包裹)→ 仍按汇总处理(strip 判据)',
|
|
319
|
+
_r65_recent(cg, " * ") == list(R65_GLOBAL), _r65_recent(cg, " * "))
|
|
320
|
+
check("R65.5a session=None → 全局窗口(退回旧行为)",
|
|
321
|
+
_r65_recent(cg, None) == list(R65_GLOBAL), _r65_recent(cg, None))
|
|
322
|
+
check('R65.5b session=""(falsy)→ 全局窗口',
|
|
323
|
+
_r65_recent(cg, "") == list(R65_GLOBAL), _r65_recent(cg, ""))
|
|
324
|
+
check("R65.6a 指定会话下缺 meta.session 的事件被丢弃(不许漏;最坏空窗口)",
|
|
325
|
+
"N0" not in _r65_recent(cg, R65_A), _r65_recent(cg, R65_A))
|
|
326
|
+
check("R65.6b 不指定会话(None)时缺 meta.session 的事件照旧可见",
|
|
327
|
+
"N0" in _r65_recent(cg, None), _r65_recent(cg, None))
|
|
328
|
+
check("R65.7 回归:session_compact(session=A) 仍只压 A、compact(None) 照旧全局",
|
|
329
|
+
cg.session_compact(session=R65_A, limit=50)["events"] == 2
|
|
330
|
+
and cg.session_compact(session=None, limit=50)["events"] == len(R65_EVENTS),
|
|
331
|
+
(cg.session_compact(session=R65_A, limit=50)["events"],
|
|
332
|
+
cg.session_compact(session=None, limit=50)["events"]))
|
|
333
|
+
_doc = inspect.getdoc(MdCGOS.session_recall) or ""
|
|
334
|
+
check("R65.8 文档口径在位:docstring 声明「段跟会话走」=recent 跟会话(含『跨会话汇总』"
|
|
335
|
+
"例外口径)+ 紧邻「生效条件:」注释含过滤条款",
|
|
336
|
+
("段跟会话走" in _doc and "跨会话汇总" in _doc
|
|
337
|
+
and "recent 段按会话过滤" in _r65_cond_line()),
|
|
338
|
+
_r65_cond_line()[:48])
|
|
339
|
+
_mcp = MdCGSecure(tempfile.mkdtemp(prefix="mdcg_p45_r65_mcp_"),
|
|
340
|
+
principal=Principal(actor="r65_mcp", session=R65_A))
|
|
341
|
+
_mcp.remember_event("user", "A1 生产形态:本会话事件", meta={"session": R65_A})
|
|
342
|
+
_mcp.remember_event("user", "B1 生产形态:他会话事件", meta={"session": R65_B})
|
|
343
|
+
_hit = ms._session_call(_mcp, {"action": "recall", "session": R65_A,
|
|
344
|
+
"recent_limit": 20, "budget_tokens": 4000,
|
|
345
|
+
"include_state": False})
|
|
346
|
+
_none = ms._session_call(_mcp, {"action": "recall", "session": R65_FAKE,
|
|
347
|
+
"recent_limit": 20, "budget_tokens": 4000,
|
|
348
|
+
"include_state": False})
|
|
349
|
+
check("R65.9 MCP 入口(_session_call recall)端到端:本会话只回本会话、假 id 空窗口",
|
|
350
|
+
sorted((r.get("text") or "")[:2] for r in _hit.get("recent") or []) == ["A1"]
|
|
351
|
+
and (_none.get("recent") or []) == [],
|
|
352
|
+
(_hit.get("recent"), _none.get("recent")))
|
|
353
|
+
|
|
354
|
+
|
|
355
|
+
# ---------------------------------------------------------------- 定点变异自证
|
|
356
|
+
# 锚点在**去缩进后的实现源码**上定位(方法体在类体内缩进 4 格,编译前统一 dedent)。
|
|
357
|
+
# 每条变异声明**期望转红的断言项**;实跑红项与期望必须**恰好相等**——多红=断言语义
|
|
358
|
+
# 纠缠,少红=该判据空转(本仓教训:test_neg_condition_hits 的「整条命中档」曾 31 条
|
|
359
|
+
# 断言全绿而分支已删)。锚点漂移(命中次数 ≠1)→ ANCHOR-MISS + 退出码 2(fail-closed)。
|
|
360
|
+
_R65_A_BLOCK = (
|
|
361
|
+
' if session and str(session).strip() != "*":\n'
|
|
362
|
+
' evs = [r for r in evs if (r.get("meta") or {}).get("session") == session]\n')
|
|
363
|
+
_R65_A_COND = 'if session and str(session).strip() != "*":'
|
|
364
|
+
_R65_A_STRIP = 'str(session).strip() != "*"'
|
|
365
|
+
_R65_A_FILTER = ('evs = [r for r in evs '
|
|
366
|
+
'if (r.get("meta") or {}).get("session") == session]')
|
|
367
|
+
_R65_A_DOC = "段跟会话走"
|
|
368
|
+
_R65_ANCHORS = (_R65_A_BLOCK, _R65_A_COND, _R65_A_STRIP, _R65_A_FILTER, _R65_A_DOC)
|
|
369
|
+
|
|
370
|
+
_R65_MUTATIONS = (
|
|
371
|
+
("①删整段会话过滤(recent 回到全进程窗口)", _R65_A_BLOCK, "",
|
|
372
|
+
{"R65.1", "R65.2", "R65.3", "R65.6a", "R65.9"}),
|
|
373
|
+
("②删 \"*\" 例外(星号也过滤)", _R65_A_COND, "if session:",
|
|
374
|
+
{"R65.4a", "R65.4b"}),
|
|
375
|
+
("③去 strip(\" * \" 不再按汇总处理)", _R65_A_STRIP, 'str(session) != "*"',
|
|
376
|
+
{"R65.4b"}),
|
|
377
|
+
("④缺 meta.session 改 fail-open(放行)", _R65_A_FILTER,
|
|
378
|
+
('evs = [r for r in evs if not (r.get("meta") or {}).get("session") '
|
|
379
|
+
'or (r.get("meta") or {}).get("session") == session]'),
|
|
380
|
+
# 签名:放行无归属事件 ⇒ 三个「按会话」腿任一都混入裸事件(R65.1/2/3 与 R65.6a
|
|
381
|
+
# 同红);R65.9 的库内无裸事件,故它**不**红——与变异① 的签名差恰在这一项。
|
|
382
|
+
{"R65.1", "R65.2", "R65.3", "R65.6a"}),
|
|
383
|
+
("⑤去 falsy 守卫(None/\"\" 也过滤)", _R65_A_COND,
|
|
384
|
+
'if str(session).strip() != "*":', {"R65.5a", "R65.5b"}),
|
|
385
|
+
("⑥文档口径回退(docstring 反转)", _R65_A_DOC, "段不跟会话走", {"R65.8"}),
|
|
386
|
+
)
|
|
387
|
+
|
|
388
|
+
|
|
389
|
+
def _r65_anchor_preflight():
|
|
390
|
+
"""锚点自检:任一锚点在实现源码里命中次数 ≠1 → ANCHOR-MISS + 退出码 2(fail-closed)。"""
|
|
391
|
+
src = textwrap.dedent(inspect.getsource(MdCGOS.session_recall))
|
|
392
|
+
bad = [(a, src.count(a)) for a in _R65_ANCHORS if src.count(a) != 1]
|
|
393
|
+
if not bad:
|
|
394
|
+
return 0
|
|
395
|
+
for a, n in bad:
|
|
396
|
+
print(" ANCHOR-MISS 锚点漂移(命中 %d 次,期望恰好 1):%r" % (n, a[:70]))
|
|
397
|
+
print(" => 实现已漂移,变异表失效:退出码 2(fail-closed;请同步 _R65_MUTATIONS 锚点)")
|
|
398
|
+
return 2
|
|
399
|
+
|
|
400
|
+
|
|
401
|
+
def _r65_run():
|
|
402
|
+
"""跑 R65 组,返回(转红断言项集合, 通过数, 失败数)。"""
|
|
403
|
+
global PASS, FAIL
|
|
404
|
+
PASS = FAIL = 0
|
|
405
|
+
del FAILS[:]
|
|
406
|
+
_r65_group()
|
|
407
|
+
return ({n.split(" ", 1)[0] for n in FAILS}, PASS, FAIL)
|
|
408
|
+
|
|
409
|
+
|
|
410
|
+
def _r65_self_proof():
|
|
411
|
+
"""逐条变异 → 要求恰好命中期望红项;复原后重跑全套应当全绿。"""
|
|
412
|
+
rc = _r65_anchor_preflight()
|
|
413
|
+
if rc:
|
|
414
|
+
return rc
|
|
415
|
+
print("!! 定点变异自证:就地变异运行中的实现源码(不读 git),逐条要求**恰好**命中期望红项\n")
|
|
416
|
+
src = textwrap.dedent(inspect.getsource(MdCGOS.session_recall))
|
|
417
|
+
orig = MdCGOS.session_recall
|
|
418
|
+
bad = []
|
|
419
|
+
with contextlib.redirect_stdout(io.StringIO()):
|
|
420
|
+
base_red, _, _ = _r65_run()
|
|
421
|
+
print(" 未变异基线:红项 %d %s" % (len(base_red),
|
|
422
|
+
"(应为 0)" if base_red else ""))
|
|
423
|
+
if base_red:
|
|
424
|
+
bad.append("未变异基线即转红:%s" % sorted(base_red))
|
|
425
|
+
marks = "①②③④⑤⑥"
|
|
426
|
+
for i, (name, old, new, expect) in enumerate(_R65_MUTATIONS):
|
|
427
|
+
ns = dict(vars(_mdcos))
|
|
428
|
+
exec(compile(src.replace(old, new), "<r65-mutated-%d>" % i, "exec"), ns)
|
|
429
|
+
MdCGOS.session_recall = ns["session_recall"]
|
|
430
|
+
try:
|
|
431
|
+
with contextlib.redirect_stdout(io.StringIO()):
|
|
432
|
+
red, _, _ = _r65_run()
|
|
433
|
+
except Exception as exc: # noqa: BLE001
|
|
434
|
+
red = {"<变异体运行异常:%s>" % type(exc).__name__}
|
|
435
|
+
finally:
|
|
436
|
+
MdCGOS.session_recall = orig
|
|
437
|
+
hit = red == expect
|
|
438
|
+
if not hit:
|
|
439
|
+
bad.append("变异%s %s:红项 %s ≠ 期望 %s"
|
|
440
|
+
% (marks[i], name, sorted(red), sorted(expect)))
|
|
441
|
+
print(" 变异%s %-32s 红项 %d(期望 %d)%s"
|
|
442
|
+
% (marks[i], name, len(red), len(expect),
|
|
443
|
+
"PASS" if hit else "**FAIL** 实=%s 期=%s" % (sorted(red), sorted(expect))))
|
|
444
|
+
argv_bak = list(sys.argv)
|
|
445
|
+
sys.argv[:] = [a for a in argv_bak if a != "--self-proof"]
|
|
446
|
+
_buf = io.StringIO()
|
|
447
|
+
try:
|
|
448
|
+
with contextlib.redirect_stdout(_buf):
|
|
449
|
+
full_rc = main()
|
|
450
|
+
finally:
|
|
451
|
+
sys.argv[:] = argv_bak
|
|
452
|
+
print(" 复原后重跑全套(A–G + R65):退出码 %d %s"
|
|
453
|
+
% (full_rc, "(全绿)" if full_rc == 0 else "**非全绿**"))
|
|
454
|
+
if full_rc:
|
|
455
|
+
for _ln in _buf.getvalue().splitlines():
|
|
456
|
+
if "[FAIL]" in _ln:
|
|
457
|
+
print(" 复原重跑红:%s" % _ln.strip()[:120])
|
|
458
|
+
bad.append("复原后全套非全绿(rc=%d)" % full_rc)
|
|
459
|
+
print("\n变异自证:%s"
|
|
460
|
+
% ("PASS(六条腿逐条恰好命中期望红项;复原后全绿)" if not bad
|
|
461
|
+
else "FAIL —— " + ";".join(bad)))
|
|
462
|
+
return 0 if not bad else 1
|
|
463
|
+
|
|
464
|
+
|
|
218
465
|
if __name__ == "__main__":
|
|
219
|
-
|
|
220
|
-
sys.exit(
|
|
466
|
+
_rc = main()
|
|
467
|
+
sys.exit(_rc if _rc in (0, 2) else 1)
|
package/package.json
CHANGED
package/skills/plugin.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
|
|
3
3
|
"name": "lingshu-skills",
|
|
4
|
-
"version": "0.8.
|
|
4
|
+
"version": "0.8.1",
|
|
5
5
|
"description": "灵枢(AEIS)自我认知技能包——白箱条件化知识(Agent Skills 兼容导出)。本质是灵枢了解自身的工具:每个技能描述灵枢在什么条件下能做什么、怎么执行、克制什么(KCCS 四要素),由条件路由图精确路由,由灵枢 MCP 工具执行验证(物理基底)。",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "灵枢(AEIS)· CommonTrustProtocol"
|