## 范围与基线 架构审查项,只检查继承关系、接口归属和存储模块边界;不据此断言 SQL、隔离级别或并发实现存在缺陷。 基线:`476e64f04ed1d9c7dcab4a41c4f3cdb72faad6a7`。Refs #85;与 #65 的存量迁移验收、#69 的恢复验收是不同范围。 ## 结构证据 - [`core/contracts.py`](https://github.com/TokenNotIncluded/msg.lmm.best/blob/476e64f04ed1d9c7dcab4a41c4f3cdb72faad6a7/src/msg/core/contracts.py) 已定义 `MetadataSession`、`MetadataStore`,事务接口返回元数据会话契约。 - [`storage/postgres.py`](https://github.com/TokenNotIncluded/msg.lmm.best/blob/476e64f04ed1d9c7dcab4a41c4f3cdb72faad6a7/src/msg/storage/postgres.py) 导入 `msg.storage.sqlite.SqliteSession`,声明 `class PostgresSession(SqliteSession)`。 - [`storage/sqlite.py`](https://github.com/TokenNotIncluded/msg.lmm.best/blob/476e64f04ed1d9c7dcab4a41c4f3cdb72faad6a7/src/msg/storage/sqlite.py) 因此同时承载一个具体后端和生产 PostgreSQL 会话所继承的行为。 - PostgreSQL 模块的说明明确保留跨进程/主机的连接级 advisory lock 和会话连接约束。这些是需要继续明确表达的后端语义,不能在抽取公共代码时抹去。 类声明搜索索引当时指向父提交 `01e39345c755d6ec8f584534de20d9cd265ca922`;已分别读取父提交和本次基线的文件元数据,二者 `postgres.py` blob 均为 `5ac02cff5b7b048a84e4812c60502ffba889c9c1`,不是用旧实现推断新结构。 ## 架构问题 生产后端并未只依赖共同契约,而是继承另一个具体数据库后端。共享的元数据行为与 SQLite 适配职责没有清晰分开,导致“修改 SQLite 后端”也可能改变 PostgreSQL 的继承行为。 这不代表继承一定导致运行错误;问题在于所有权和替换边界不准确。维护者难以仅根据包名和类关系判断:哪些是共同数据访问规则,哪些是 SQLite 约束,哪些是 PostgreSQL 专有事务保证。用这种继承关系复用代码,也不等同于两个独立后端通过同一契约验收。 ## 建议的目标结构 保留 PostgreSQL 作为唯一生产权威数据库,也保留现有测试/辅助后端;不新增数据库服务、通用 ORM 或每个业务名词一套 Repository。 只将真正可共享的会话辅助行为放到后端中立的位置,采用小型共享基类或组合均可。PostgreSQL 与 SQLite 的具体适配器分别依赖公共契约/公共机制,不再让前者继承后者。连接、事务、锁、SQL 方言、约束和迁移仍归各自后端,不以“统一”为由削弱 PostgreSQL 保证。 ```text MetadataSession 契约 / \ PostgreSQL 适配器 SQLite/测试适配器 \ / 必要的共享机制 ``` ## 验收条件 - [ ] `PostgresSession` 不再继承具体 `SqliteSession`,且生产存储模块不导入 SQLite 适配模块作为其行为底座。 - [ ] 明确记录公共机制与后端专有职责;不是只把 `SqliteSession` 换个中性名字,却仍把 SQLite 连接/方言混在公共层。 - [ ] `MetadataStore`/`MetadataSession` 的实际依赖与声明一致,必要扩展显式记录,不用万能对象绕过边界。 - [ ] 同一组存储契约验收分别运行于测试后端和真实 PostgreSQL;后者另保留其事务、锁、唯一约束和并发回归。 - [ ] 不改变数据库 schema、历史 ID、账项、签名或 receipt;需要迁移的额外变化另行提案,不与这次类关系调整捆绑。 - [ ] 现有 LedgerAccount 迁移、恢复隔离及提交/回滚回归保持,不以 SQLite 通过代替 PostgreSQL 通过。 依据[权威设计](https://docs.google.com/document/d/1EM5Qr5qdg6tAFi2wvY0EBm6zxMj6DTBMc_dybU5qkz0/edit)第11、14、15章。保持模块化单体;无需全仓搬目录。本 issue 不授权生产迁移或恢复。
Activity
LIghtJUNction commentedon Sep 28, 2026
第二轮架构补充:除后端继承外,声明的存储接口也未覆盖真实依赖
基线仍为
476e64f04ed1d9c7dcab4a41c4f3cdb72faad6a7。这是本 issue「实际依赖与契约一致」验收项的具体证据,不另开重复 issue。结构证据
core/contracts.py中MetadataStore.transaction()返回MetadataSession;该 Protocol 声明资源、身份、证书、分片、事件等操作,但没有one或execute。core/models.py的OperationSpec.handler/requirements也把会话参数描述为MetadataSession。storage/sqlite.py和storage/postgres.py的具体会话却提供execute(self, sql, parameters=(), *, write=False)。plugins.money._balance、market.policy.load_policy、market.targets.validate_target、security.vault._open、extensions.keystore等对tx.one的使用,以及workers.maintenance对tx.execute的使用。可从这些模块的调用符号与会话方法声明核对;本轮没有检查查询条件、SQL 正确性或事务实现。架构含义
一个满足已声明 MetadataSession 的替代会话,仍未必满足这些调用者的真实需求。业务/安全/扩展层还隐含依赖数据库查询接口及表结构,因此只解除
PostgresSession(SqliteSession)的继承关系,不能独立解决存储抽象失真。这不意味着直接写 SQL 天生错误,也不证明现有会话会绕过只读/事务保护;缺少类型注解本身同样不是故障。问题是工程对外宣称的接口范围与实际依赖范围不同。
请在原验收中明确选择
可以保留 SQL 型模块化单体:显式声明受限查询会话的参数、返回类型、错误和事务所有权,限定哪些模块可依赖它。也可以只把关键跨域数据访问收敛到小型有类型接口。两种路线都比名义上只依赖 MetadataSession、实际使用额外方法更清楚。
不要求为每张表新增 Repository、不引入 ORM、不把全部业务方法塞进一个更大的万能 Session,也不把裸连接及 commit 权限公开给 handler。
建议补充检查:选取真实业务消费者,通过声明的接口类型完成装配/契约检查;存储层变化不能依靠
Any或放宽断言掩盖缺失方法。继续保留唯一事务提交者、真实 PostgreSQL、当前授权、账本及恢复回归。LIghtJUNction commentedon Sep 28, 2026
本会话接续处理本 issue,独立分支 fix/storage-session-20260929-2050(与#156/#160不共用源分支)。保留SQL型模块化单体:拆出真正不持有数据库连接/方言的共享会话逻辑;SQLite/PG各自管理连接、锁、迁移、事务及错误映射。同时补显式受限查询接口与结果读取接口,选择真实policy消费者验证声明,不新增逐表Repository/ORM,不改schema/账本历史。先提交双后端回归,所有执行放云端,包含拒绝只读写入、跨任务/关闭后的会话结果、嵌套回滚与现有补偿写栅栏。
test(storage): expose session backend and query contract drift
LIghtJUNction commentedon Sep 28, 2026
认领并实施于独立 PR #166,分支
fix/metadata-session-boundary-20260929;不重叠 #165 的执行内核/工具契约与 #155 恢复实现。已取得真实 red:Actions 36481798533,精确 head
7f8752e37fe5b5a8021f192b87e731caec2bbc2d/ tree686dbddc1bdfc52d97c127bdb7338f002cabf735。16 项中 4 failed / 12 passed / 0 error / 0 skip:4项失败均为本 issue 的依赖/归属问题;资源世代、事件/回执/审计/任务持久化、保存点与补偿、只读/跨任务/关闭保护、唯一约束回滚及占位符/字面量的两后端回归都已通过。下载产物10997260030的SHA-256与提供方一致(50ae1ff1b71684d60e0ff133cde41c6cb81658c0855aeb9c376fff110dc27e2e)。下一提交仅提取共同模型查询/会话约束到中立
storage.session.MetadataSessionBase,两驱动分别持有自己的连接和 execute;PostgreSQL advisory lock、事务/补偿、SQL/error适配及两边schema/migration保持不变。将对原共享方法与store类做逐方法结构一致性核对并上传证据。笔记:docs/METADATA_SESSION_PROGRESS.md。当前仍是回归先行,未声明修复完成。merge: verify executor and storage boundary repairs as one integration
test: retain shared regression coverage while consolidating duplicate…
merge: synchronize verified tool-runner main without dropping query c…
merge: retain supplemental metadata coverage and converge on one impl…
merge: verify client boundary against the single executor and session…
merge: retain the latest supplemental database regression gate
LIghtJUNction commentedon Sep 29, 2026
架构验收完成,按已合入源码关闭
唯一实现随PR #167于2026-09-29T08:58:23Z合入main 261c881 / tree 6171f6041ffd0addaf72eadd35acf562f478281f。#166的16个双后端补充用例和历史均保留,没有叠入第二份基类实现。
PostgresSession与SqliteSession独立继承storage.session.RelationalSession;生产PG导入会被测试环境主动禁止SQLite模块/驱动,当前仍可通过。docs/ISSUE_PROGRESS.md。core.query.QuerySession/QueryResult明确受限SQL参数/结果/错误/事务所有权;MetadataSession补齐真实域方法,无connection/commit/万能对象。实际market.policy.load_policy使用声明接口,通过真实已存政策、损坏digest拒绝、双后端嵌套回滚验证,不只检查一个假会话。test_session_boundary.py及test_metadata_session_boundary.py在真实PG和SQLite分别运行,含会话/结果跨任务与关闭保护、保存点补偿、资源世代/路径/分页、事件/审计/作业、约束冲突、占位符/字面量。PG原锁与并发回归保留。SQLite的政策表仅隔离fixture复用真实DDL,不把测试辅助后端说成完整生产市场等价实现。#65真实旧库迁移、#69恢复、#85总体验收仍独立开放。此关闭不表示生产部署或生产迁移已执行。