固化 DirectProject 聊天真相源契约
- 主规范新增首屏锚点、生命周期锚点必进 bootstrap、事件自足与不提供单点快照四条契约 - 新增 ADR 记录单一事实源取舍、删除清单与待验证的 id 空间风险 - 新增里程碑与实施计划,CONTEXT.md 补项目对话历史/运行态事件/聊天投影词条 - 目标文档合同与文档索引同步
This commit is contained in:
@@ -98,6 +98,10 @@ readHistory(threadId, { beforeItemId?, limit }) -> {
|
||||
|
||||
`notify` 只负责唤醒,不携带事件、cursor 或持久化状态。前端收到通知后调用 `consume`;通知可合并、重复或丢失,事件完整性由 `consume` 保证。
|
||||
|
||||
因为唤醒本身允许丢失,前端在「已判定存在活动回合」期间以低频 `consume` tick(1–2 秒)兜底;`consume` 自带 cursor,重复 tick 不会重复返回事件。
|
||||
|
||||
首屏历史不通过"读取整份对话"的命令获取:`subscribe` 返回的 `lastCompletedItemId` 就是首屏锚点,前端据此调用 `readHistory` 取最近的切片,再按滚动或按钮继续向前分页。系统不提供返回整份对话历史的命令。
|
||||
|
||||
### 事件和顺序
|
||||
|
||||
Thread 内所有公开事件共用一个单调递增 seq;seq 允许跳号,前端不要求连续。事件 envelope 至少包含:
|
||||
@@ -116,6 +120,12 @@ Thread 内所有公开事件共用一个单调递增 seq;seq 允许跳号,
|
||||
|
||||
一个 thread 同时最多有一个 active turn;一个 turn 内允许多个并发 item。`turn.completed` 必须在该 turn 的完成 item 均成功持久化后进入队列,前端据此结束运行态;不能用“不存在 unfinished item”猜测 turn 是否完成。
|
||||
|
||||
前端 reducer 的活动回合判定只有一条:state 中存在「出现过 `turn.started` 且未出现对应 `turn.completed`」的 `turnId` 时,该 turn 才是活动回合,界面才允许显示忙碌态。`subscribe` bootstrap 中没有这样的 `turnId`,就表示当前没有活动回合;Thread Manager 队列随进程消失,因此进程重启后历史里留下的半截回合一律按已结束渲染,前端不发明中断态,也不从历史条目反推忙碌态。
|
||||
|
||||
生命周期锚点独立于 replay 队列保存:`turn.started` / `turn.completed` 事件即使已被队列前缀回收,`subscribe` 仍必须把最新的一条作为 bootstrap 事件返回。因此进程内任意时刻新建订阅,都能判定最新回合是运行中还是已结束,不依赖"未完成 item 恰好还在队列里"。
|
||||
|
||||
`item.started` 与 `item.completed` 必须携带与历史切片同形的完整条目(经同一套脱敏、截断、路径归一),不得只给 item 类型或空 payload。前端不得依赖"按 `itemId` 单点取快照"补齐正文:Rust 不提供 `getItemSnapshot(itemId)`,未完成条目的正文随事件下发,已完成条目一律通过历史读取。
|
||||
|
||||
### 队列、subscriber 和回收
|
||||
|
||||
每个 thread 一个 Vec-based append-only replay queue,使用逻辑 head 偏移清理前缀,不做中间删除。完成 item 的事件在持久化成功后才可进入普通 replay 回收流程;unfinished item 的事件必须保留到 item 完成,不能被普通上限截断。
|
||||
|
||||
Reference in New Issue
Block a user