OpenProgram / SessionStore / implementation handoff

Session 注册表时间语义与并发一致性

基线:e831d0f0。本页只规范 meta.json、内存 _indexindex.json 的时间和并发合同;实现状态与证据见第 07 节。

确定方案。 updated_at 表示最后一次会话内容追加时间,不表示任意元数据持久化时间。内存注册表的读、改、快照由独立锁保护;磁盘原子写串行化,但不在注册表锁内执行文件 I/O。延迟 flush 只能清除自己快照之前的 dirty 状态,不能覆盖并发更新。

01 · 已验证缺口与不变量

位置当前行为目标不变量
_persist_meta每次写 meta.json 都赋值 time.time()改名、置顶、已读、分支标记、HEAD 持久化不得改变会话内容排序时间;append 路径显式推进。
_index更新和 list_sessions() 复制未使用同一把锁。读者获得行字典的点时快照;并发创建、更新、删除不产生迭代异常或撕裂字段。
deferred flushtimer 受锁保护,但快照序列化和其他直接保存路径未形成统一协议。旧快照不得晚于新快照落盘;写入期间的新 mutation 必须保留 dirty 并由后续 flush 保存。

02 · 参考机制与取舍

现有 SessionMemoryIndex

节点和 session meta 已有自己的内部锁。采用其“锁内更新、锁外文件 I/O”边界,不增加事务框架。

stdlib threading

采用一把 registry state lock 加一把 write lock。前者保护 dict、dirty、timer;后者只保证磁盘快照按取得顺序写入。

atomic_write_text

继续负责单次文件替换的原子性;不新增数据库、文件锁或第三方依赖。

采用:显式内容时钟、行级浅拷贝快照、串行原子写。修改:_persist_meta 不再隐式更新时间。拒绝:对所有 SessionStore 操作加全局锁、每次 append 同步写 registry、把 updated_at 拆成新字段并迁移全部消费者。

03 · 目标状态流

1
append message/node 显式生成一个时间值,并写入 session meta 与 registry entry。
2
rename/pin/read/branch/head 等路径只持久化已有 updated_at,不自行推进。
3
registry mutation 在 state lock 内完成;list_sessions 在同一锁内复制每行后再过滤和排序。
4
保存者先取得 write lock,再在 state lock 内捕获 JSON 快照并清除该快照覆盖的 dirty,随后释放 state lock 执行原子写。
5
若写入期间发生新 mutation,它重新设置 dirty;timer 或退出 flush 保存后续快照。

04 · 失败、并发与兼容

05 · 明确排除

06 · 验收矩阵

公共入口场景可观察断言
update_session创建两会话后改名、置顶、标已读目标会话的 meta/index updated_at 不变,列表顺序不变。
append_message追加一条内容meta/index 同时推进,列表按新时间排序。
list_sessions调用方修改返回行后续列表结果不受影响。
deferred flush旧快照写入期间并发更新后续 flush 后 index.json 包含新字段,不被旧保存清除 dirty。
concurrent registry access并发 update/list/flush无迭代异常,JSON 始终可解析,最终内存与磁盘一致。

07 · 实现文件与证据

文件职责
openprogram/store/session/session_store.py时间语义、registry state/write 锁、快照与延迟 flush。
openprogram/store/session/session_node_writer.pynode append 的单一 activity timestamp 与 meta/index 同步。
tests/unit/test_session_index_consistency.py排序时间、返回快照和并发写入的公共回归。

已实现并完成独立审查。 实现提交:9fef648c09480e0fc50b5180。规格审查与质量审查均通过;最终 gate 证据记录在任务台账。