OpenProgram / SessionStore / 实施交接

存储一致性

本页规范 meta.json、内存 _index 与 index.json 的时间和并发合同,以及 merged heads 和 store 锁范围。各部分保留独立的验收边界。

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

注册表时间与不变量

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

参考机制与取舍

现有 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 拆成新字段并迁移全部消费者。

目标状态流

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 保存后续快照。

失败、并发与兼容

明确排除

验收矩阵

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

缓存快照更新

缓存重建和首次加载在读取历史与元数据之前获取磁盘指纹,重建后的索引只记录这个已获取的指纹。读取期间的原子替换会改变磁盘指纹,下一次公共读取因此再次重建。旧快照不得使用较新的磁盘指纹标记为已同步。动态 DAG 上下文和终止状态沿用现有存储与锁。

验收使用两个真实 SessionStore 实例:读取旧快照之后、标记同步之前完成历史或元数据写入;随后 get_nodes 或 get_session 能看到新值。覆盖已有缓存和首次加载,取消重试保留原有时限并读取真实取消状态。

Windows 会话目录删除

显式删除会话、过期会话清理和容量清理共用平台删除适配器。普通 Git 对象因 Windows 只读属性导致删除失败时,清除该属性并仅重试该次删除一次。其他权限失败保持调用方原有的尽力处理行为;不修改 ACL,也不跟随链接改变目标权限。缺失历史的清理条件和归档保留规则保持不变。

验收通过公开 SessionStore reopen 和 delete_session 入口使用只读 Git 对象。原生 Windows 单元回归检查目录确实消失,而非仅从索引移除。实现遵循标准 shutil 只读文件删除示例,使用兼容 Python 3.11 的错误回调与只读属性检查。

适配器和公开回归已通过独立规格与质量审查。原生验收由 Windows 单元 CI 执行,本地模拟不能替代。

Merged heads 原子更新

mark_merged(session_id, head_ids) 是集合并集操作。并发 merge 可以来自子 agent 完成、task runner 和 Web 分支操作;两个调用必须保留双方写入,不能由后写者覆盖先写者。

状态合同

属性要求
原子性读取现有列表、去重追加、写回 idx.meta 在同一个 idx._lock 临界区完成。
顺序保留既有顺序;每个调用按输入顺序追加此前不存在的非空、strip 后 ID。
幂等重复 ID 不产生重复项;没有新增值时不写磁盘。
持久化锁内只更新内存;_persist_meta 在锁外执行,避免文件 I/O 持有 index 状态锁。并发调用最终至少有一个持久化快照包含完整并集。
本节约束 mark_merged 的 read-modify-write。它不扩展为通用 transaction API,不修改 branch naming、DAG 节点删除或 session index flush 协议。

验收

Store 锁范围

SessionStore._lock 仅保护会话位置映射和 LRU 缓存结构。_open、位置索引持久化和 delete_session 不得在该锁内执行目录扫描、原子写、fsync 或递归删除,避免一个会话的慢 I/O 阻塞其他会话。

并发合同

资源锁要求
_locations、_sessions 和 LRU 顺序_lock仅在读取、修改或复制这些内存结构时持有;不允许在锁内进行文件系统 I/O。
单个 session_id 的首次加载、重建、删除和缓存失效每会话 RLock同一会话串行,不同会话可并行。不创建通用 transaction 层。
locations.json 发布专用写锁专用写锁串行化快照获取和发布;在全局状态锁内更新并复制快照,释放状态锁后落盘,防止旧快照后写覆盖新快照。
SessionMemoryIndex 持久化现有 _persist_lock → idx._lock保持现有锁序和 stale rebuild 语义;不在 idx._lock 内进行 I/O。
本节仅约束 store 全局锁的 I/O 范围。不改 SessionStore 文件格式、LRU 容量、删除语义、跨进程 stale 检测或 Windows 行为。

验收

实现状态与证据

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

注册表时间与快照合同已实现并完成独立审查。 规格审查与质量审查均通过;最终 gate 证据记录在任务台账。

原子 merged heads 与 store 锁范围在此集中维护设计合同和验收要求;文档合并本身不新增它们的实现或平台验收结论。