9.1 KiB
AGENTS.md — ChuSQL 开发契约
给 AI agent 的项目规则。约束优先于便利:任何"暂时放宽"都必须先在此文件里改数字,并说明理由,否则视为违约。
进度与待办的唯一事实来源是 projectplan.txt;本文件只管"什么算做完"。
1. 硬约束(CONSTRAINTS)
违反任一条 = 改动不算完成,不许提交、不许进 projectplan.txt 的 ✅。
| # | 约束 | 当前值 | 校验方式 |
|---|---|---|---|
| C1 | Haskell 测试全绿 | 381 条(engine 200 + web 181) | cd chusql-engine && stack test、cd chusql-web && stack test |
| C2 | Rust 测试全绿 | 78 条 | cd chusql-storage && cargo test |
| C3 | Haskell 编译零警告 | -Wall 下 0 warning |
stack build 输出无 warning:(engine 与 web 两个包都要看) |
| C4 | 不引入抑制手段 | 禁止 {-# OPTIONS_GHC -Wno-... #-}、#[allow(...)]、@ts-ignore、--no-verify、跳过/删除测试、注释掉断言 |
看 diff |
| C5 | 不吞错误 | Rust 拿不到 unwrap()/expect() 当控制流;Haskell 不用 error/head 兜底。能返回错误的走 Result / Either |
看 diff + clippy 提示 |
| C6 | 索引语义不得回退 | "索引列必须唯一"是决策 14,不是实现细节;放宽它必须同时改 projectplan.txt 的已知问题 6/9 |
看 diff |
| C7 | 性能结论必须同轮交叉测 | 机器漂移 15~25%(已知问题 4);小于 2 倍的差异不当结论,改前改后必须同轮前后跑 | 基准记录 |
| C8 | 已实测的功能不许只留"应该能跑" | 新能力要么有测试断言,要么在 projectplan.txt 标 ⬜ 未做 |
看 projectplan.txt |
| C9 | 测试数下降 = 阻塞 | 新增测试可以增数;数字变小必须解释哪条被移除及原因 | 前后对比 |
已知的历史违约模式(别重复):
- P1 遗留:
Spec.hs的 "INSERT ... via IPC" 用例只断言了结果,没断言"没走 scan" —— 需求被"测试绿了"掩盖。补法是检查服务端 debug 日志里不出现scan。 - 若哪天要"为了跑通先注释掉断言" —— 停手,改成把这条写进
projectplan.txt的 ⬜。
2. 改动规则
- 改
chusql-engine源码后,跑全链路基准前必须先stack build --force-dirty,否则基准可能链到旧引擎(plan 第 146 行)。 - 表存在性判据以
catalog为准(不是文件长度),空表也是合法表;改这条要先搜它所有调用点(决策 7、8)。 - 索引元数据只放一处(数据字典)。优化器只把"单列等值条件"标成"可点查",有没有索引由存储层回答(
no_index→ 上层退回全表扫描)。不许在优化器里缓存一份索引表(决策 13)。 - 等价改造不许改结果。典型:等值连接换哈希表,输出顺序必须和嵌套循环一致(左表序为主、右表序为辅,同键下右行保序);认不出等值键或某侧空表时退回嵌套循环(决策 9)。
- 存储接口分
schema与snapshot:语义检查与优化只走schema,不许再拉全库行(决策 10)。 - 测试的位置约定:Haskell 引擎测试全部在
chusql-engine/test/Spec.hs;Web 在chusql-web/test/Spec.hs(web 是独立包,不能反向依赖 engine 的测试);Rust 在chusql-storage/tests/*_test.rs,另有src/log.rs里 3 条单元测试。新增测试放同一处,不另起目录。 - 只留根目录一个 README:性能基准的跑法/结果/踩坑都写进
README.md;设计长文写docs/ENGINE_INTRO.md、docs/STORAGE_INTRO.md,CLI 与接口说明写docs/COMMANDS.md。 - 改任何决策或已知问题,同一轮内更新
projectplan.txt。
3. 已知正确性缺口(最高优先级)
- 重放不幂等(已知问题 1):断电卡在"数据已落盘"与"日志清空"之间时,重启会把同一操作再作用一遍,
INSERT多一行。这是当前唯一的正确性缺口,不是功能缺失。 做 P7 完整版 WAL 时,验收标准 = 给每次操作记 LSN、重放前比对是否已作用过,并有一条"中途断电重放两次、行数不变"的测试。
4. Skill 路由表
已安装 7 个相关 skill(源:addyosmani/agent-skills,全局位于 ~/.agents/skills)。按阶段加载,不要全部加载。
| 阶段 | 先加载 | 用途 |
|---|---|---|
| 任何阶段开工 | constraint-driven-development |
对照本文件 C1–C9,检查本轮有没有偷偷降标准 |
| 写代码 / 修 bug(全程) | test-driven-development |
先红后绿;C1/C2 的 200/181/78 是底线 |
| 编译失败、测试失败、行为不符预期、IPC/FFI 异常 | debugging-and-error-recovery |
根因定位,禁止"试一下看看"式修改 |
| P3 Web(✅ 已完成,改动 Web 时仍照此走) | spec-driven-development → api-and-interface-design → security-and-hardening |
先出 spec 再定接口,最后过输入校验与 Session 安全 |
| P3 启动前(无 spec 时) | spec-driven-development |
Web 涉及 5 块能力,必须先拆能力图 |
| P4 CLI | api-and-interface-design |
与 Web 共用 Haskell 后端,接口一次定稳 |
| P5 FFI(C ABI + 跨语言所有权) | api-and-interface-design → debugging-and-error-recovery |
ABI 契约 + 内存所有权问题定位 |
| P2 遗留 A/B(字节键、重复键、范围扫描) | test-driven-development → performance-optimization |
重写比较器与分裂节点,必须有前后同轮数字 |
| P6 SQL 功能补齐 | test-driven-development |
每条语法/语义能力都要有用例 |
| P7 代价优化 / 统计信息被"用"上 | performance-optimization |
遵 C7;已知问题 3(池淘汰 O(池容量))绝对值 2.8 µs/次,先证明值得改 |
| P7 完整版 WAL(含重放幂等) | debugging-and-error-recovery + 对抗式复核 |
见上面第 3 节,正确性问题 |
明确不用(本项目场景不匹配,别加载):
browser-testing-with-devtools(需 chrome-devtools MCP,本机没配;Web 的端到端用
stack test 里那条真 HTTP 用例顶着)、
deprecation-and-migration、observability-and-instrumentation、shipping-and-launch(无生产部署)、
planning-and-task-breakdown(路线图已在 projectplan.txt)、
git-workflow-and-versioning / code-review-and-quality / refactor / code-simplification(与既有习惯重叠,只增流程噪音)。
5. 每轮收尾清单
cd chusql-engine && stack test→ 200 条全绿cd chusql-web && stack test→ 181 条全绿(含一条真起 Rust 存储进程的端到端;static/未构建时 14 条构建产物契约测试记 pending)cd chusql-storage && cargo test→ 78 条全绿stack build输出 0 warning(-Wall,engine 与 web 两个包)- diff 里没有 C4 列的抑制手段
- 动过性能路径 → 同轮交叉测,数字进
README.md表格 - 动过决策/已知问题 →
projectplan.txt同一轮更新 - 新能力的测试数只增不减(C9)
6. 常用命令
# Haskell 引擎测试
cd chusql-engine && stack test
# Web 测试(端到端那条会自己 cargo build --bin chusql-storage 并起真进程)
cd chusql-web && stack test
# Rust 测试
cd chusql-storage && cargo test
# 一键把三个模块都起来(Rust 存储 + Haskell Web + 浏览器)
# .\script\chusql.ps1 web -Port 9000 临时端口(关掉即失效)
# .\script\chusql.ps1 config set port 9000 存成设置(script\chusql.settings.json)
# .\script\chusql.ps1 config / cli / help 看设置 / CLI 占位(会起 web)/ 帮助
# (每个开关都能换成 CHUSQL_* 环境变量;清单见 README 的配置表)
# 手动分开起(一条命令连 Rust 存储进程一起拉)
cd chusql-storage && cargo build --release
cd chusql-web && stack run chusql-web -- --storage ../chusql-storage/target/release/chusql-storage.exe
# 浏览器打开 http://127.0.0.1:7777/ ;
# 端口被占或被系统保留时换一个:--port 7778
# (Windows 查保留段:netsh interface ipv4 show excludedportrange protocol=tcp)
# 全链路基准(Haskell 引擎 + Rust 存储进程一起起)
cd chusql-storage && cargo build --release
cd benchmark/fullchain && stack run chusql-fullchain -- 1000 1000
# ⚠️ 改过 chusql-engine 源码 → 先 stack build --force-dirty
# 分层基准
cd benchmark/chusql-engine && stack run # 查询层(内存数据,隔离优化器)
cd benchmark/chusql-storage && cargo bench --offline # 存储层微基准
上一轮评估:2026-09-25(P3 ⑰ 全部完成:按 DataGrip 截图把界面整个重做——主工具栏 + "Database Explorer" 工具窗(标题栏窗口动作/小工具栏/连接→库→schema→Database Objects→表→列的树)+ 标签页 + 数据编辑器工具栏 + 带行号槽与类型字形的网格 + 状态栏;图标换成 IDEA 风格线稿,配色换 IDEA 深浅两套;顺手做实了标签页、行号槽、单元格游标与方向键、工具栏加减行、DDL 只读查看、树过滤、折叠全部、工具窗三个窗口动作与下拉菜单)。下一项:P2 遗留 A/B(复合键 + 字节键 → 非唯一索引)。