# ChuSQL 当前设计决策
进度见 progress.txt，限制见 known-issues.txt，用户操作见 docs/。

## 架构与接口

### 文档与后续方向

- 用户文档按主题记录于 docs/commands.md、docs/types.md、docs/architecture.md 和 docs/install.md；只读范式入口已提供。

  后续完善绑定后的中间表示（bound IR）与 DOMAIN，扩展物化资源控制、递归查询，并按实测减少行版本发布与在线整理的开销；规划不代表现有能力。
- 范式迁移首版保留来源，拒绝 NULL、重复行与依赖不保持；发布目标须验证约束、完整重建并使用独立原子结构 WAL 组，不能复用禁止 DDL 的会话事务拼接执行。
- 发布真实性规划采用 TUF（用于验证软件更新元数据的框架）与 Ed25519 签名；初始 root 信任元数据必须可信取得，离线验证不证明最新版本。操作系统原生代码签名另行接入；这些签名能力尚未实现。

### 组件边界与分层

- Web / CLI → server TCP → Haskell 引擎 → FFI → Rust 存储；server 独占数据目录，Haskell 库静态链接，存储为 cdylib。
- 引擎按 AST → 语义检查 → RelOp → 优化 → 执行分层；语义与优化只读 schema，表存在性以 catalog 为准，索引由存储管理。
- JSON 使用显式稳定标签，FFI 提供 version / open / close / request / maintenance / free / last_error；Web 不本地执行 SQL。

### 类型语义的职责

- 兼容值与类型语义集中于 Core.Model，运行时类型与版本化编码集中于 Core.Runtime，函数注册与绑定集中于 Engine.Runtime；旧运算符保留 Engine.Builtin，错误分类集中于 Engine.Error。
- 比较与赋值兼容性分开，整数与整值浮点统一相等和哈希，SQL NULL 比较走三值逻辑；DML 的 CHECK 随写计划一次绑定，结构变更对新 schema 重新绑定。

### 写入求值与约束

- 写表达式和 CHECK 复用查询的列槽位绑定及行布局整理，避免按物理返回顺序取错值；UPDATE 在执行前按 schema 一次绑定目标槽位及列定义，按槽位校验、转换并覆盖单个值。目标不存在或歧义明确失败，不保留缺失类型时直接写入的兜底；保留逐项赋值、全部赋值后的 CHECK 及未命中行不求值。
- INSERT 目标清单拒绝重复列，合法目标在本次执行中一次映射到 VALUES 槽位；行构造保留 schema 顺序及原输入表达式求值顺序，默认值、自增、显式 NULL 和整批校验沿用既有语义。缺少已绑定输入值明确失败，不把它作为省略列处理。
- UPDATE 在全部行求值及 CHECK 成功后、调用存储写入前，复用 INSERT 的 NOT NULL 和 UNIQUE / PRIMARY KEY 检查；唯一性以最终整表结果判断，多个 NULL 不冲突。约束失败不发布部分行修改，事务快照保留并允许修正后继续提交。
- NOT NULL 和 UNIQUE / PRIMARY KEY 检查使用 schema 位置绑定列，缺值或位置不符明确失败，不用 mapMaybe 跳过缺列。

  INSERT 的已有行与经引擎重写 schema 的 ALTER 输入行在约束边界按原 schema 整理；保留列级错误顺序、NULL 去重规则和数值规范化比较。

### 写计划与结构守卫

- 写语句及已有表 DDL 使用仅保存 schema 的 BoundWrite，复用查询的依赖验证规则；收集目标、嵌套查询和传递 DOMAIN，新增列或改类型包含新声明 DOMAIN。

  先以只读 validate_write 确认写守卫能力，再发送实际写入；存储按库名排序取门，在目标库独占门及其他依赖共享门内复查后执行，不改变单次请求之间的数据隔离语义。
- BoundWrite 的 DML 动作保存 INSERT 输入槽位、UPDATE 赋值目标、DELETE 布局和预绑定 CHECK / 非空 / 唯一性约束，执行期不再重新解析或按名称绑定目标；自动编号仍从当前行集求起点，唯一性仍按最终行集校验，保持逐项赋值及未命中行不求值。
- CHECK 定义与执行共用布尔条件、函数和槽位绑定；聚合及子查询拒绝。结构变更先按新 schema 校验保留约束、默认值及键能力，空表不跳过；CHECK 文本未采用独立列身份，改名不自动重写，失效引用明确拒绝。

## 查询与优化

### 优化与成本选择

- 优化保持结果语义和可观察行序；相关外层列不能被投影删除，LEFT JOIN 只下推安全的左侧谓词。
- schema 携带行数、distinct、索引和直方图，缺统计时保守退回原计划。范围成本使用 16 桶等宽直方图，无直方图时按四分之一估算。
- 内连接从各起点贪心选序，仅在严格更省且行序不可观察处重排；统计不足或不安全条件时不改写。

### 名称与作用域

- 表 / 列限定名在语义层按作用域及唯一候选解析，别名优先；会话将当前库限定 DDL 转为裸名，拒绝跨库目标，库名随请求传递。
- 相关子查询的语义列环境按词法层逐层查找，保留最近层的列及类型，不把不同外层并成一张表；本层歧义不向外回退。JOIN ON 和外部引用收集使用同一外层环境，来源检查失败明确传播。执行期仍使用既有扁平外层行布局。
- SELECT 在引擎入口直接进入 BoundQuery，避免规范化引用再次作为原始来源名解析；二段限定名被最近层同名来源遮蔽，该来源缺列时不回退。裸列仍按列可见性逐层查找，已解析引用的类型推导不重新解释来源遮蔽。

### 列槽位与重绑定

- 绑定后的子查询保存父布局中的局部或外层列槽位，取值与复用缓存键共享该绑定；诊断名称相同不表示位置相同。重绑定不把外层引用转为局部引用，布局失配和引用清单失配明确返回错误；原始关系执行 API 保留未绑定 Subquery 的名称入口。
- 已绑定列重绑定时保留仍匹配名称的有效位置，原位置不匹配仅允许唯一同名列重定位；歧义或负位置拒绝。外层槽位保留相对位置并更新局部宽度；独立列身份未实现，结构改写仍须在绑定前完成，不能凭同名槽位识别重排后的不同列。
- 相关子查询保留已解析外层限定名；求值绑定补回单表物理列的限定名，绑定去重与扫描投影使用同一列值，库限定引用仍校验 schema 来源。

### 子查询执行与复用

- JOIN 条件按子查询外层引用的值缓存替换后的表达式；首次遇到行对时才求值，每个行对仍计算普通条件，保留输出顺序、空输入和错误返回。
- 半连接仅重写独立 IN 或单表唯一等值相关 EXISTS 的单独过滤谓词；键按两侧布局分别绑定，输出只保留原左行。独立 IN 沿用物化身份，NOT IN 感知右侧 NULL，反 EXISTS 保留空值键；大数混合比较退回原比较语义。

  复杂条件、可能报错的相关投影和相关聚合不重写，空左侧不执行右侧，保持完整右侧求值；空集 IN 为假、NOT IN 为真（含 NULL 左值），非空集合保持三值逻辑。
- 每次查询执行用独立状态物化成功行集，子查询按已解析定义和具名外层绑定值共享，原始名称调用保守包含整个外层行；派生表按执行子树和外层行缓存，先共享再应用别名。各消费者独立处理标量基数与三值逻辑，失败仍明确返回，保持既有求值顺序；不跨执行保留，也不提供整条查询快照或磁盘溢出。

## 存储提交前校验

- WAL 提交组落盘后、首次应用前进入 recovery_required；任何应用、刷盘或检查点错误都保持该状态，完整落盘与截断成功后才解除。
- 普通提交组在分配对象编号及写 WAL 前，使用 HeapTable::encode_row 检查全部载荷行；单写、批写、整表替换、事务及结构重写共用此边界，避免可预见的行大小错误在提交后触发恢复封闭。账号专用 WAL 路径保留原有账号行预校验；不改变页格式与已有恢复顺序。
- 索引键容量共用 DiskBTree::check_key，存储提交预检复用堆表实际键编码；结构替换只检查保留的内置 id 索引，删列重写排除已移除索引。建索引在写 WAL 前扫描目标列校验；堆表插入、建索引和整表替换在修改数据前检查键长，不用截断键或跳过错误降级。
- 新插入 id 的冲突预检复用 row_key，在同库写门内按 WAL 操作顺序维护键计数；未涉及的现存键按需查询 id 索引，不为单次插入全表扫描。

  删除键在一条操作内去重且每键只减一，保留旧重复 id 的首行删除规则；整表替换重建预检基线，沿用其兼容重复 id 的行为。预检不改变恢复重放与原有页格式。
- 目录预演覆盖普通写入可能触发的扩列及结构重写的载荷行，列推导与版本递增共用 Catalog::ensure_columns；只在结构操作或输入可能扩列时克隆目录。

  按行及操作顺序累计变化，在写 WAL 前拒绝版本耗尽，保留同一行多列只递增一次及未定型 NULL 不扩列的规则；已有列的数据写入不因版本达到上限而被拒绝。
- Request::valid_columns 检查所有写入载荷的顶层字段名及事务内行，存储表名与列名字符集共用 valid_storage_name；保持已有非空 ASCII 字母、数字及下划线规则。

  只在请求边界约束新载荷，不递归检查值内部键，不对旧目录、堆行及 WAL 重放加新名称限制，也不静默改名或丢字段。

## 查询来源与排序

- FROM 是来源树，派生表必须带别名且不能引用外层，输出取裸列名，重名报错；未定型 NULL 保留独立查询推导类型，不能声明为存储类型。
- ORDER BY 优先匹配唯一输出别名，再查来源列；只将排序表达式提前计算，非排序投影保持在 LIMIT 后求值。CTE 列清单同步重命名内层排序别名。
- ORDER BY 输出序号保留到计划生成，不转换为诊断名称；普通查询选定对应投影表达式，DISTINCT 按去重结果槽位排序，重复别名不丢失位置。名称排序仍拒绝歧义，序号不作为相关子查询自由列，排序键遵守类型能力、分组及 LIMIT 求值时序。
- 派生表从透明算子与 schema 统计估算行数，缺统计或表达式可能报错时不重排；投影下推保留计算项，避免删除错误。内存存储提供实时行数及封顶去重统计。

### 数值与子查询语义

- DECIMAL(p,s) 使用有限浮点、ties-to-even 舍入和舍入后溢出检查，要求 1 <= p <= 308、0 <= s <= p；Haskell 与 Rust 写入入口统一边界，日志记录规范化值。
- CTE 解析期展开为派生表，只接受 SELECT。相同执行子树在一次查询中物化复用；不同投影或优化后的子树仍分别执行，不支持递归或显式物化语法。
- 比较及 ANY / ALL 使用独立 AST 节点，量词复用三值逻辑，空集 ANY=false、ALL=true。
- 子查询在 Filter / Compute 中按外层绑定分组复用；适用的独立 IN 与简单等值相关 EXISTS 使用半连接或反连接，复杂条件保留原路径。扫描按页分片，至少 1000 行、最多 8 线程，结果保序，safe FFI 释放执行能力。

## 会话、事务与存储

### 会话快照与差分

- 每连接独立会话及工作库；短锁取得库句柄后执行，每库读写门控制可见性，表锁保护页，在用库不强行删除。
- BEGIN 物化当前库快照，事务内走内存执行，COMMIT 提交相对基线差分，ROLLBACK 丢弃快照。
- COMMIT 先跳过数据未修改的表，整数 id 用 IntMap / IntSet 查找差异与冲突；保留原行顺序及重复 id 的首行查找语义，冲突检查和提交操作共享一次差分。
- SQL 数据语句和提交使用共享全局门与共享库门；同库最终校验和发布使用单独的短提交门。自动提交也走事务计划、行锁和版本复查，保持失败不部分发布。权限与 DDL 全局独占，原始管理员存储写入通过版本变化参与冲突检查。
- 有整数 id 时按键提交及检查写写冲突，无整数 id 时整表比对；未修改表不回写，冲突保留事务并返回 serialization_failure。
- COMMIT 在短提交门内预演实际事务操作，并复用引擎的领域 / CHECK / 非空 / 唯一性规则校验合并最终行集，避免不同整数主键绕过业务唯一约束；校验失败不写 WAL，事务保留。内存预演与存储一致，每个去重删除键只删除首个旧重复 id 行。
- 可选 BEGIN [TRANSACTION] ISOLATION LEVEL SERIALIZABLE / START TRANSACTION ISOLATION LEVEL SERIALIZABLE 保留整库快照，在写事务提交时复查读写语句全部表的 schema 和行集，包含子查询与幻行；按表保守拒绝冲突，默认 BEGIN 的不同行差分合并不变。保存点恢复语句列表，也恢复读依赖；只读事务不因数据变化拒绝提交。
- 保存点保存不可变快照引用，同名取最近一层，回退 / 释放丢弃之后的点，结束清空；事务命令由会话层执行。

### 日志与恢复

- WAL 先记录操作及提交标记，再应用、刷数据与 catalog、写检查点、截断；仅重放已提交且超过检查点的记录。
- WAL 帧保留长度、LSN、类型码、表名及 JSON payload，兼容旧记录；恢复按内容、schema 和文件存在性判断幂等。
- 检查点按库 LSN，前缀截断用同目录临时文件原子替换，未提交尾部直接截短；失败保留 WAL 并进入 recovery_required。
- 检查点采用同目录临时文件替换，长度必须为 8 字节；完整 WAL 帧损坏明确报错，尾部不完整帧保留撕裂处理。刷盘或检查点失败后封闭请求，重启重放已提交组。

### 统计与页面整理

- distinct 在封顶前按值引用计数维护，列封顶不清除其他列状态；直方图独立累计与递减，桶重映射导致删除目标为空时扣最近非空桶以保持总量。rebuild_stats 先扫描数值范围，再按固定边界计数，消除重建过程的桶重映射偏差并保持输入顺序无关。
- 表 / 索引 DDL 进 WAL，删表不重取已移除句柄；库建删不进 WAL，删除以墓碑目录收敛。
- 自动页内 compact 保持槽号及索引位置，死空间达到四分之一时触发；显式 compact 经 WAL 按原扫描顺序打包存活行，回收跨页空洞和隐藏列字节，并重建索引位置。

## 身份、权限与引导

### 对象编号

- 对象标识使用数据库编号与库内表编号组合；数据库编号从 system catalog 的对象高水位分配，表编号从本库高水位分配，删除不降低计数器。对象解析只读取 catalog。
- 建表编号在 WAL 组写入前分配，随建表记录提交；目录应用时推进高水位，恢复使用日志编号。只有建表组复制操作列表，普通数据提交沿用借用列表。

### 身份与角色

- 用户是可登录角色，共用名称空间；identities 保存属性、时间、稳定编号及版本，users 只存编号和口令哈希，编号不复用。
- 最高权限按身份属性判断，不按名称、不继承；默认用户 LOGIN、角色 NOLOGIN，两者 NOSUPERUSER / ENABLED。保护最后一个启用的登录管理员。
- 仅旧格式迁移按管理员参数识别初始最高权限；旧账号保留信息，角色补编号，重名拒绝；身份、凭据及删除清理用同一 Accounts WAL。
- server 判定权限，目录封装于 Server.Catalog；角色成员取向上闭包，写入拒绝环。GRANT role TO member 表示 member 继承 role 权限。

### 对象授权与会话失效

- members 保持身份成员目录；对象授权存于受保护 __system_object_acl，使用稳定对象与身份编号，保存所有者、授予者和 grant option。CONNECT / CREATE 限数据库，四项数据权限限表或单库表范围；ALL 按作用域展开。
- PUBLIC 默认只提供业务数据库 CONNECT，可逐库撤销；CREATE 建表登记创建者所有权，数据库所有权不隐含他人表的数据权限。启用角色可继承所有权，不继承 SUPERUSER。
- 授权链从独立管理来源及所有权展开固定点；撤销清理无来源下游，保留独立来源，循环不能自持。禁用保留所有权和管理来源授权，停止权限贡献并清理失效下游。
- 旧名称授权仅迁移一次到现存稳定对象；旧全局 * 固定为当时现存表，缺失对象或身份清理，旧记录无授予者时使用管理来源。
- 权限快照按字符分块存储，比较旧快照后以同一 WAL 组替换；跨页及 Unicode 不受单行上限限制。普通存储入口隐藏并拒绝访问权限快照表。
- 服务端共享公平读写锁排序查询与权限变更；事务提交复查身份、权限和稳定对象。建表前写入创建者声明，启动时依据现存对象恢复所有权或清理未完成声明。
- 口令 / 属性推进 revision，权限变更预先展开受影响身份；TCP 只标记连接失效，下一请求回 unauthorized 后关闭，避免 Windows 阻塞句柄异步中断；登记在 finally 清理。

### 系统目录与引导

- 系统表统一 __system_ 前缀并分层保护，内部目录按白名单访问；旧名按文件先、catalog 后迁移，崩溃后可重复收敛。
- 安装先 bootstrap：建立 system、默认 root（LOGIN / SUPERUSER / ENABLED）、18 个内置名称，再起服务；引导请求不开放 TCP。
- 清单编入引导并与解析器测试比对；system.__system_types 保存名称、基础表示、参数能力、编号。重复引导保留口令并补齐类型，冲突或身份降权失败；类型建表与写入用同组 WAL。

### 本机 sudo 认证

- sudo 认证用独立 allow_sudo_auth 属性和显式 sudo_auth_user 映射；默认关闭，只有 SUPERUSER 能管理，不继承、不隐含权限。目录管理者不得管理启用该属性的身份。
- 选择操作系统保护的随机本机凭据与 TCP 一次性 HMAC 挑战，以复用三平台既有会话协议；Unix 只信任有效 UID 0，Windows 只信任提升 Administrators 令牌。双方校验内核回环地址，不接受客户端自报 OS 用户或 SUDO_USER。
- 本机凭据只存放于系统保护的端口专属目录，原生入口校验提升身份；实例重启重新生成，现存目录拒绝覆盖。此方案要求服务端也提升运行，以保证创建凭据时的操作系统保护边界。
- 挑战绑定账号、连接及实例，60 秒过期并在每次尝试时消费；证明常量时间比对，凭据不经网络发送。成功登录复用身份版本、登录时间及连接失效机制，审计不含凭据或证明。
- sudo 属性由 Accounts WAL 持久化，旧目录缺列触发身份快照重写，旧记录缺属性默认 false，已有的错误类型明确拒绝。

## 范式分析

- 范式检查采用阿姆斯特朗属性闭包与子集枚举，候选键声明转为决定全关系的依赖；完整覆盖声明与领域原子性分别控制可证明结论，不从样本推断。二元 BCNF 建议按违反依赖无损拆分，投影依赖之并用于验证依赖保持。

## 自定义类型

### DOMAIN 定义与变更

- DOMAIN 命名基础类型，列编码 domain(名称,基础类型) 保留引用，值及索引沿用底层语义。领域目录的 base 列保存类型、可空性与默认值，check_名称 列保存具名条件，旧的仅基础类型定义继续可读；未改变值、页或 WAL 编码版本。
- DOMAIN CHECK 在独立 VALUE 槽位绑定，拒绝聚合及子查询，TRUE / UNKNOWN 通过。领域继承全部 CHECK 和非空限制，默认值按列、最近领域、基础领域选取；继承属性只在绑定时合成，不写回列声明。
- ALTER DOMAIN 在库读门内取完整定义及数据快照，引擎校验传递依赖、默认值和已有行；库身份与 WAL 检查点组成精确令牌，在写门内复查后走 ReplaceSchema WAL。

  并发提交返回 serialization_failure；复用已有恢复与结构版本，不引入通用结构事务。创建依赖领域的表 / 领域也在发布点验证结构守卫；事务提交复查修改表及传递领域依赖。
- 各库 __system_domain_<名称> 空目录表的 base 列保存定义，复用 catalog / DDL WAL；普通 SQL 隐藏并保护目录。
- 嵌套只引用当前库已有类型，语义层从 schema 递归解析并拒绝循环，存储写锁内逐层校验；删除检查类型间及间接列依赖。
- 类型 DDL 要求管理员，事务内禁止。DOMAIN 已支持默认值、非空与 CHECK 约束；暂不引入自定义函数 / 运算符。

### 运行时类型与表达式

- 运行时类型使用稳定构造器编号与结构化 TypeId，逻辑类型与 PhysicalType 分离；递归值使用版本 1 标签编码，读取时校验类型、形状及载荷。
- TypedExpression / TypedPlan 仅使用 schema 绑定，SQL BoundQuery 通过既有 RelOp 执行并按输出位置验证；输出类型保留嵌套 DOMAIN 名称，基础日期、时间戳及 Blob 保留兼容类型，函数结果不继承参数领域。
- TypedExpression 列引用保存输入 schema 槽位与诊断名称；独立求值要求行顺序匹配，TypedPlan 扫描边界校验并按 schema 排序，每层投影绑定到子计划输出。

  SQL RelOp 在优化后绑定表达式列槽位，投影单向转为 Compute，排序/分组转为 BoundSort/BoundAggregate，存储结果按算子布局排列；子查询局部列在前、外层绑定在后。

  绑定算子不参与后续投影下推；独立作用域身份仍待迁移。
- SQL 局部引用使用 ColumnSlot，外层引用使用 OuterColumnSlot 的相对位置及局部布局宽度。重新绑定外层引用只查询外层布局，避免同名局部捕获；语义输入拒绝执行槽位。外层行仍扁平化，多级词法作用域/来源编号另行迁移。

### 查询依赖的结构守卫

- BoundQuery 执行前重新取得 schema 并比较依赖对象身份、schema 版本及列定义，依赖覆盖全部嵌套子查询及来源列的传递 DOMAIN 定义。

  IPC 将依赖守卫限定在本次执行环境；扫描、分片、点查及范围读均在相关库读锁内复查全部依赖后读取，跨库读锁按库名排序，持有库引用防止删除。数据、统计和无关对象不参与比较；旧目录缺失版本时仍比较列定义，已知身份或版本丢失则拒绝。

  守卫响应必须明确确认，旧二进制忽略字段时拒绝；未实现守卫的后端明确报错，纯内存视图可直接执行。守卫不改变既有多请求查询的数据隔离语义。
- 内建函数先解析为稳定 FunctionId 并验证签名，再执行；SQL NULL 保留三值语义。运行时描述保存 Eq / Ord / Hash 能力，List、Maybe、Tuple 根据参数递归派生，DOMAIN 沿用基础类型。SQL 比较和量词按运算要求能力，GROUP BY / DISTINCT 同时要求 Eq 和 Hash。
- 复合值排序使用类型身份与结构的字典序；哈希按构造器与子值递归计算，统一浮点正负零，不依赖显示文本。类型和值编码仍为版本 1；哈希只用于内存执行，不需重建持久化索引。SQL NULL 与 Maybe Nothing 分开，空容器也保留完整身份。
- 复合值的 Eq / Ord / Hash 不等于索引键能力；运行时索引及 UNIQUE / PRIMARY KEY 继续拒绝，索引连接仅对现有整数或文本键探测，其余走扫描 / 哈希回退。
- SQL 字面量生成返回明确错误，先验证运行时值与有限浮点；复合值在 Web 使用 SQL 修改。

## 配置、构建与发行

### Web 编辑与受限管理

- Web 编辑在首个业务表加载时建立快照，在提交 / 回退时结束；既有大库基准 BEGIN 269.806 ms、COMMIT 376.345 ms，不按每次单元格编辑重建整库快照。
- Web 登录分别建立普通 API / SQL 和编辑连接，页面 / 数据库标识绑定编辑事务，SQL 事务另绑定查询标签；同 Cookie 请求串行执行。
- 编辑集保留在浏览器，提交从固定保存点重放；失败回退整个编辑集，冲突回滚并重新加载，成功回执去重；系统身份修改不进入数据事务。
- system_catalog_manager 只管理普通身份，不继承、不隐含业务访问权限；存储写锁内再次拒绝特权目标及特权属性修改。

### 离线维护与配置

- bootstrap 维护使用独占数据目录锁，在副本中修复并验证后保留原目录备份再发布；repair 不重放 WAL、不补初始化数据，原有数据文件必须字节一致。
- listen_host 是监听配置，static 固定为静态目录，管理员名由 --user 指定；data_dir 存数据，log_files 存日志，相对路径以启动目录解释。
- resources/settings.toml.linux 与 resources/settings.toml.windows 入库并打包，安装时填入平台路径并生成 settings.toml；UI 设置样本不入库、不进包。配置路径以 docs/config.md 为准。

### 安装与门禁

- 预编译包优先，server / 存储必装，Web / CLI 可选；Windows zip、Linux/macOS tar.gz。包内模板也放 resources/，按确切产物名打包并拒绝未知工件。
- 本地解包安装不联网，管理员名同时传 bootstrap 和 server；安装冒烟隔离配置并恢复用户 PATH，卸载可保留数据 / 配置。
- 门禁先刷新 release cdylib，再并行 Rust、串行 Haskell、注释三条车道；Server / Web 先刷新各工程的本地依赖，任一步失败即失败。
- 局部改动只测试受影响模块；较大改动由 agent 执行全量门禁。日志 / 临时文件在 logs/，必要时 -Full；门禁及打包强制 UTF-8 以避免 GBK 编码失败。

### 口令输入与诊断

- CLI 终端隐藏口令，管道兼容 LF / CRLF / 无行尾，保留空格与内部回车，提示写 stderr。
- CLI 禁用逐行自动历史，只记录完整 SQL 与元命令；含 IDENTIFIED / PASSWORD（忽略大小写）的输入整体排除，避免多行口令或执行失败留下片段。解析错误也隐藏此类原文，筛选保守包含字符串和标识符中的同名文本，已有历史不自动删除。
- TCP 与 Web 使用同一解析诊断脱敏路径；Web 回归校验整个 HTTP 错误响应不含口令。内置 SQL 控制台记录结果或诊断，不将 SQL 原文写入日志，外部代理和采集需自行配置。

### 对象及结构版本编码

- BoundQuery 保存来源对象身份及列定义；DOMAIN 依赖从来源列递归展开，执行前复查。库 / 对象编号在 TableInfo 中输出十进制字符串，读取兼容旧数值，零对象编号表示无有效身份。
- 表级 schema_version 由 catalog 持久化，新表从 1 开始，旧目录缺失版本保留 0；结构变化递增，新增结构 WAL 记录保存确定目标版本，恢复不重新分配。

  线上使用精确十进制字符串并兼容数值输入，0 或缺失表示未知版本；IPC schema / 快照与内存 Server 保留版本，内存 DDL 同样递增并拒绝耗尽。
- 注释以 AGENTS.md 为准；原因写本文件，限制写 known-issues.txt，进度及计数只以 progress.txt 为准。

### 容器部署

- 同一运行镜像包含数据库、引导、CLI 和 Web，Compose 将数据库与 Web 分为单进程服务；Web 不挂载数据库目录。运行 UID/GID 为 10001，命名卷保存数据、日志及编辑器偏好。
- 每次 server 启动复用既有幂等 bootstrap，口令从文件经 stdin 输入，不使用文件标记判断系统目录状态；失败阻止启动，不自动 repair / reset。已有口令不随 secret 文件修改而重设。
- Rust 与 Haskell 使用固定 digest 的 Bookworm 构建阶段，运行层沿用 Bookworm。BASE_REGISTRY 只替换下载地址，保持 digest 校验；Debian 与 Cargo 镜像源保留签名及锁文件校验。源码构建单任务且限制 GHC 堆，WSL 使用 1 核 / 3 GiB、无额外交换空间的 Buildx 或异机构建导入。
- Compose 通过 CHUSQL_IMAGE 固定验收镜像，服务默认限制 1 核 / 512 MiB、128 个进程或线程且无额外交换空间。不同 UID 的旧数据卷须停机备份后显式调整所有者，不由服务启动脚本自动递归改权限。

## 博客业务接入

- 业务用户与数据库身份分离，角色使用标量 CHECK；账号完整 Argon2id PHC、发布授权、文章权威状态、评论、互动、审计及迁移标记持久化，可重建文章索引独立。正文与资源留在 Git 文件，接入验收见 docs/blog-database.md。
- 标量索引键上限为 min(128, floor((page_size - 11) / (order - 1)) - 12)，保留按旧 64 字节预算校验配置的规则，避免现有合法页 / 分支数组合失效。旧索引头可读取，首次写长键更新能力字段；页、槽位、WAL 与键排序编码不变，提交前及堆直接写入共用配置上限。

## 存储并发与页恢复

- 全局公平门排序权限和 DDL；SQL 数据准备共享库门，最终约束校验与 WAL 发布串行，以保留连续已应用提交前缀。行锁等待位于库门外，持有内部行编号及旧、新唯一值锁；固定名称顺序用于跨库门，等待图用于跨请求行锁。环中的新等待者中止，锁等待总上限 5 秒。保存点回滚恢复计划和读依赖，严格持锁到事务结束。
- 行版本保存在独立的 mvcc.json，以 WAL 提交 LSN 为可见序号，使用不复用内部行编号、起止序号、内容版本和逻辑行序。内容版本与位置变化分开，避免其他行的删除或排序造成伪写冲突。发布顺序是逻辑 WAL、数据与索引、版本目录、库检查点；重放后幂等发布版本。版本超前于可恢复 WAL 时拒绝打开，不冒充已完成恢复。
- 选择独立版本文件以保留现有页与键格式，代价是整库扫描和版本文件写放大。SQL 仍使用物化工作行集；默认快照隔离，SERIALIZABLE 写事务按表保守复查读依赖。版本保留到最老活跃快照，结束事务回收内存，后续发布回收磁盘版本。database_snapshot 的单版本缓存继续供普通后端读取，不承担事务存活登记。
- 保留现有页内布局，以 .page-wal 记录 CPW1 全页镜像、写序号与校验和，以版本 1 的 .pages 保存页 LSN。镜像先同步，数据文件再同步，页元数据经临时文件原子替换并同步目录，最后清理镜像；库 WAL 检查点在这些步骤之后推进。
- 同一逻辑提交可多次刷新同一页，镜像按独立写序号恢复，不仅按页 LSN 比较；截断也写入重置标记。缓存淘汰先记录并写回当前脏页批次，避免反复记录未修改的缓存页。日志错误使当前页句柄进入恢复封闭，保留材料而不靠析构重试。
- 已提交逻辑组重放前丢弃受影响表的派生索引，再依据修复后的堆重建；整表载荷直接重写，避免残缺根或部分行集阻断幂等恢复。账号变更统一走带提交标记和检查点的组，保留旧账号日志解码 / 恢复。
- 显式 compact 在独立文件代次复制堆与索引，最终库写门内追平并发变更，保留切换时逻辑行序。影子文件同步后用 Migrate WAL 发布 catalog.file_generation，对象身份与结构版本不变。旧快照阻止退役文件删除；启动恢复后清理孤立代次。保留有序前缀、重写其余后缀避免重排可观察行序，代价是变化较多时切换窗口变长。启动隐藏列整理保留离线 Compact 日志兼容。
