Skip to content

Commit 727590e

Browse files
AlexStocksOmX
andcommitted
docs(planning): define RESP allocation guardrails
Record the approved parser resource-limit design, TDD execution steps, and the transition from merged PR #388 to the RESP-LIMITS-001 implementation card. Constraint: This commit changes planning and recovery-facing documentation only; runtime behavior remains unchanged. Confidence: High; the scope follows exact main source inspection and Redis 8.8.1 parser evidence. Scope-risk: Low; only task-specific design, plan, STATE, and KANBAN files are included. Tested: git diff --cached --check; placeholder scan; Requirement and task identifier scan. Not-tested: Runtime tests are deferred to the implementation commit. Co-authored-by: OmX <omx@oh-my-codex.dev>
1 parent cbc2895 commit 727590e

4 files changed

Lines changed: 248 additions & 24 deletions

File tree

.planning/KANBAN.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
# Kiwi Kanban
22

3-
> 更新日期:2026-07-30
3+
> 更新日期:2026-07-31
44
>
55
> 当前里程碑:M1 Redis 8.8.1 Cache OFF compatibility foundation
66
>
7-
> 当前 task:PR `#388` implementation(`feat/rocksdb-build-accel-and-prd`);已验证远端 Base `0f8d96238860a5c29a5582e461e4cdeb974431b3`、Head `b47cb1eebe098e2d4d2d784020dc283a8d026d28`
7+
> 当前 task:`RESP-LIMITS-001` implementation(`codex/fix-resp-parser-limits`);基线 `main` at `cbc28958f261ae049d67a8b4a9d904d794b37726`
88
>
99
> 当前运行模式:Cache OFF
1010
>
@@ -14,7 +14,7 @@
1414

1515
| ID | 工作项 | Requirement / Gate 关系 | 当前状态 |
1616
|---|---|---|---|
17-
| `PR388-001` | C/C++ sccache 构建接入、跨平台回归探针、PRD/用户故事及 review 一致性修复 | `REQ-STABILITY-003``REQ-WORK-001``REQ-WORK-003``REQ-WORK-005`;跨平台构建修复只维护 G6 CI 的可执行性与证据条件,不构成 Gate 通过证据 | 本地针对性验证通过并形成提交;远端发布状态和新 Head CI 结果以 PR/checks/threads 实时查询为准 |
17+
| `RESP-LIMITS-001` | 限制未认证 RESP Array/Map/Set/Push 声明长度和初始预分配 | `REQ-COMPAT-002``REQ-COMPAT-006``REQ-WORK-003` | 设计已批准;独立 worktree 与 recovery checkpoint 已建立;正在执行 parser TDD |
1818

1919
## Ready for a separate implementation task
2020

@@ -37,6 +37,7 @@
3737
| ID | 工作项 | Evidence | 状态 |
3838
|---|---|---|---|
3939
| `M1-001-T1` | Redis 8.8.1 exact compatibility manifest | PR `#372`;final Head `6a692bc195f96327296296977a100af301deaf01`;merge commit `9e91707d774ad367d682e23677dcef79ecb14338` | 已合并到 `main` |
40+
| `PR388-001` | C/C++ sccache 构建接入、跨平台回归探针、PRD/用户故事及 review 一致性修复 | PR `#388`;final Head `1ee8c916a55d03d02a250ed95af83712fa14a742`;2026-07-30 merged | 已合并到 `main` |
4041

4142
## Frozen unaccepted drafts
4243

@@ -89,7 +90,7 @@
8990

9091
## WIP 与授权规则
9192

92-
- 同一时间只允许一个 implementation card 处于 In Progress;当前为 `PR388-001`,planning/docs task 不得隐式持有 implementation card。
93+
- 同一时间只允许一个 implementation card 处于 In Progress;当前为 `RESP-LIMITS-001`,planning/docs task 不得隐式持有 implementation card。
9394
- 规划批准不授权修改 source、tests、build scripts 或 CI,也不授权 stage/commit/push 实现文件。
9495
- 从 planning 转 implementation 必须创建新 Codex task、TaskId、worktree、dirty allowlist 和 recovery checkpoint;不能只把 recovery mode 从 `planning` 改成 `implementation`
9596
- 冻结草稿不得覆盖、续写、清理或回退;后续实施只读参考并重新审计。

.planning/STATE.md

Lines changed: 24 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,46 @@
11
# Kiwi 当前状态
22

3-
> 更新时间:2026-07-30
3+
> 更新时间:2026-07-31
44
>
55
> 当前 task 类型:implementation
66
>
7-
> 当前 PR:`#388``feat/rocksdb-build-accel-and-prd`
7+
> 当前 PR:待创建(`codex/fix-resp-parser-limits`
88
>
9-
> 已验证远端快照:Base `main` at `0f8d96238860a5c29a5582e461e4cdeb974431b3`;Head `b47cb1eebe098e2d4d2d784020dc283a8d026d28`
9+
> 实现基线:`main` at `cbc28958f261ae049d67a8b4a9d904d794b37726`
1010
>
11-
> 状态:PR `#388` 在 2026-07-30 复检时为 OPEN;本轮 review fix 已形成提交,发布状态、checks 和 review threads 必须实时查询
11+
> 状态:设计已批准,独立 worktree 与恢复 checkpoint 已建立;正在执行 parser TDD
1212
>
13-
> 当前范围:修复 C/C++ sccache 的 Windows 编译器回归和重复 `target` 缓存,补充自动回归探针,并统一版本化 PRD、用户故事与项目状态
13+
> 当前范围:拒绝超出 Redis 8.8.1 整数边界的 RESP 聚合长度,并限制 Array/Map/Set/Push 的初始预分配
1414
>
15-
> Requirement 边界:`REQ-STABILITY-003``REQ-WORK-001``REQ-WORK-003``REQ-WORK-005`;跨平台构建修复只维护 G6 CI 的可执行性与证据条件,不构成任何稳定性 Gate 已通过的证据
15+
> Requirement 边界:`REQ-COMPAT-002``REQ-COMPAT-006``REQ-WORK-003`
1616
1717
## 当前目标
1818

19-
PR `#388` 是与 PR `#383` 规划任务分离的 implementation task。其既有范围是在 CI 与 `scripts/dev.sh` 中为 RocksDB C/C++ 编译接入 sccache,并新增由权威规划文档综合出的 PRD 与用户故事。本轮 review fix 处理 Windows compiler wrapper 回归、重复缓存、自动回归探针、文档 Requirement 映射、Hot Tier 授权边界和当前项目状态问题
19+
本 task 修复 Issue #395 B1 中已经由源码确认的未认证 RESP 聚合类型无界预分配问题。客户端声明长度不得直接控制 `Vec` 的初始容量;超出 Redis 8.8.1 `INT_MAX` 边界的声明返回协议错误,合法声明的初始容量最多为 1024
2020

21-
本 task 不实现 Redis Oracle provenance,不接受旧六文件 Oracle 草稿,也不启动 Embedded Redis Hot Tier。PR `#388` 的合并、CI 通过或 M7 Ready 均不能替代 Oracle implementation task 或 Hot Tier implementation task 的单独授权
21+
本 task 不处理实际流入的超大 bulk 或连接累计 buffer 限额,不修改 PR #402 的文档,不处理 Issue #395 的其他条目,也不实现 Redis Oracle provenance 或 Embedded Redis Hot Tier
2222

2323
## 当前授权边界
2424

2525
允许:
2626

27-
- 在本轮 review fix 中修改 `.github/workflows/ci.yml``scripts/dev.sh``scripts/tests/test-dev-sccache-env.sh``docs/prd.md``docs/personas-and-user-stories.md``.planning/STATE.md``.planning/KANBAN.md`
28-
- 执行 Markdown、链接、Requirement/Decision ID、一致性和 Git diff 等只读检查
29-
- 记录已经发生并可验证的 PR/Base/Head 历史;瞬时 checks、threadsdirty ownership 继续实时查询
27+
- 修改 `src/resp/src/parse.rs`、本 task 的设计/计划`.planning/STATE.md``.planning/KANBAN.md`
28+
- 运行 resp crate 单测、Clippy、格式检查、Git diff 检查及与 changed surface 对应的验证
29+
- commit、push 并创建以 `main` 为 base 的独立 PR;发布后实时查询 checks 和 review threads
3030

3131
禁止:
3232

33-
- 修改 Cargo、生产源码、Oracle provenance 实现、Hot Tier 实现或其他未授权路径
33+
- 修改 Cargo、网络/认证逻辑、其他生产源码、PR #402 文档、Oracle provenance Hot Tier 实现
3434
- 在旧 `redis-8.8.1-stability-foundation` worktree 继续、暂存、提交、push、清理或回退六文件实现草稿。
3535
- 把旧草稿的绿色测试、审查或真实构建准备表述为方案 A 已实现。
3636
- 扩大到 Embedded Redis Hot Tier、Redis fork、动态库、loader、Cache ON 或组合发行实现。
37-
- 在未获授权时 commit、push、merge、rebase、Resolve 或回复 GitHub review thread。
37+
- merge、rebase、Resolve 或回复 GitHub review thread。
3838

3939
## 已确认决定
4040

4141
- PR `#383` 已于 2026-07-28 合并:final Head `42c16bef899385bd2e1b1e16e2e0202d4a614590`,merge commit `58030e1331655546ea4547a9a94efc493534ef7d`;它只完成 Oracle 方案 A 的规划闭环。
42-
- PR `#388` 是独立 implementation task,不得继承或隐式扩大 PR `#383` 的 Oracle 实施授权。
42+
- PR `#388` 已于 2026-07-30 合并;本 task 是从最新 `main` 创建的独立 implementation task。
43+
- RESP 聚合长度上限采用 Redis 8.8.1 的 `INT_MAX`,初始预分配上限采用 1024。
4344
- `D011`:Redis Oracle required provenance 采用 verifier fresh-checkout independent rebuild 和 exact binary hash equality。
4445
- `D012`:规划 task 与实施 task 分离;规划批准不授权源码实现,提前产生的实现草稿冻结。
4546
- Redis 8.8.1 tag `8.8.1` / commit `77b6c308396c9700672390a210143a8496fb4b10` 是唯一兼容和 Oracle 基线。
@@ -60,11 +61,11 @@ PR `#388` 是与 PR `#383` 规划任务分离的 implementation task。其既有
6061
- 规划状态:方案 A 的 15 路径 planning-only Diff 已由 PR `#383` 合并;final Head `42c16bef899385bd2e1b1e16e2e0202d4a614590`,merge commit `58030e1331655546ea4547a9a94efc493534ef7d`
6162
- 实施状态:未在已接受边界中开始。
6263
- 实施入口:`docs/superpowers/plans/2026-07-28-redis-8.8.1-trusted-oracle-provenance.md`
63-
- 启动条件:另开专用于 Oracle provenance 的 implementation task,从包含本规划的 clean commit 创建新 worktree,保存新 TaskId 和 recovery checkpoint;当前 PR `#388` 不满足或替代该条件。
64+
- 启动条件:另开专用于 Oracle provenance 的 implementation task,从包含本规划的 clean commit 创建新 worktree,保存新 TaskId 和 recovery checkpoint;当前 RESP parser task 不满足或替代该条件。
6465

6566
## 冻结实现草稿
6667

67-
相关但不属于当前 PR `#388` dirty ownership:
68+
相关但不属于当前 RESP parser task dirty ownership:
6869

6970
```text
7071
Worktree:
@@ -98,7 +99,9 @@ D:\test\github\kiwi\.worktrees\redis-8.8.1-stability-foundation\.codex\recovery\
9899
- PR `#372` 的合并状态、final Head 和 `main` merge commit 已实时确认。
99100
- PR `#383` 已合并;其规划提交只涉及 15 个 planning/docs 路径,并从 Task 1 合并后的 `main` 重放,未带入 Cargo、脚本、CI 或实现路径。
100101
- 旧六文件草稿 worktree 已只读核对,并继续冻结。
101-
- PR `#388` 的远端审查快照为 Base `0f8d96238860a5c29a5582e461e4cdeb974431b3`、Head `b47cb1eebe098e2d4d2d784020dc283a8d026d28`;本轮 review fix 已形成提交,当前发布状态须通过 GitHub 实时查询。
102+
- PR `#388` 已于 2026-07-30 合并,final Head `1ee8c916a55d03d02a250ed95af83712fa14a742`
103+
- 本 task 已确认 `RespParse` 在认证前可达;Array/Map/Set/Push 均把未受信任的 `i64` 长度直接传给 `Vec::with_capacity`
104+
- Redis 8.8.1 exact tag 的 multibulk parser 拒绝大于 `INT_MAX` 的声明值,并把初始 argv 分配限制为 1024。
102105
- `wsl.exe --cd /mnt/d/test/github/review/kiwi-pr-388/source -- bash scripts/tests/test-dev-sccache-env.sh`:7 个 Windows/Unix/compiler 场景全部 PASS。
103106
- WSL Python/PyYAML 解析 `.github/workflows/ci.yml`:8 个 job 可解析,手写 `actions/cache``target` owner 为 0,compiler regression probe 恰有 1 个 CI step。
104107
- 文档一致性探针:59 个使用中的 `REQ-*`、4 个 `D*` 均能在权威文件解析,Markdown 表格结构通过。
@@ -109,8 +112,9 @@ PR `#383` 的结果只证明 Oracle 规划闭环,不证明方案 A 已实现
109112

110113
## 下一条安全动作
111114

112-
1. 对本轮七个 task-owned 路径执行 shell 回归、workflow YAML、Markdown、引用、Requirement/Decision ID、Git diff 和路径边界检查。
113-
2. 本轮 push 已获得单独授权;发布后重新查询新 Head 的 checks。Resolve 或回复 review thread 仍须对应的单独授权,不得把已发布提交表述为 CI 已验证内容。
115+
1. 先运行超限 frame 回归并保留预期红灯,再实现统一容量 helper。
116+
2. 运行 `cargo test -p resp`、目标 Clippy、`cargo fmt --check``git diff --check`
117+
3. 本轮 push 和创建独立 PR 已获得授权;发布后重新查询新 Head 的 checks。不得把未完成的 CI 表述为通过。
114118
3. PR `#383` 的规划历史保持不变,旧六文件 Oracle 草稿继续冻结。
115119
4. 只有用户另开 Oracle provenance implementation task 后,才从包含方案 A 的 clean `main` 创建新 worktree、TaskId 和 recovery checkpoint,并先执行真实 Redis 双 checkout reproducibility 门禁。
116120
5. Hot Tier 继续 Frozen;Gate PASS 后仍须用户明确批准一个单独的 implementation task。
@@ -127,7 +131,7 @@ Get-Content -Raw docs\personas-and-user-stories.md
127131
if (Test-Path .codex\recovery\ACTIVE.md) { Get-Content -Raw .codex\recovery\ACTIVE.md }
128132
git status --porcelain=v2 --branch --untracked-files=all
129133
git diff --cached --name-only
130-
gh pr view 388 -R arana-db/kiwi --json state,baseRefOid,headRefOid,statusCheckRollup,reviewDecision
134+
gh pr list --repo arana-db/kiwi --head codex/fix-resp-parser-limits --json number,state,baseRefName,headRefOid,statusCheckRollup,reviewDecision
131135
```
132136

133137
如果 branch、HEAD、task type 或 dirty ownership 与 recovery 记录不同,先报告差异,不得自动 checkout、restore、reset、stash、clean 或覆盖文件。
Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
# RESP 聚合类型资源限额实现计划
2+
3+
> **面向 AI 代理的工作者:** 必需子技能:使用 superpowers:subagent-driven-development(推荐)或 superpowers:executing-plans 逐任务实现此计划。步骤使用复选框(`- [ ]`)语法来跟踪进度。
4+
5+
**目标:** 阻止未认证客户端通过 RESP 聚合类型声明长度触发无界初始分配或 capacity-overflow panic。
6+
7+
**架构:**`resp` crate 内增加一个统一的聚合容量校验 helper,按 Redis 8.8.1 的 `INT_MAX` 边界拒绝超限声明,并把初始容量限制为 1024。四种聚合解析器共享该 helper,网络层继续复用现有协议错误关闭连接路径。
8+
9+
**技术栈:** Rust 2024、`nom` streaming parser、`bytes`、Cargo tests、Clippy
10+
11+
---
12+
13+
## 文件结构
14+
15+
- 修改:`src/resp/src/parse.rs`,包含聚合长度校验、Array/Map/Set/Push 调用点和回归测试。
16+
- 创建:`docs/superpowers/specs/2026-07-31-resp-parser-resource-limits-design.md`,冻结问题、边界与验收合同。
17+
- 创建:`docs/superpowers/plans/2026-07-31-resp-parser-resource-limits.md`,记录 TDD 实施步骤。
18+
- 修改:`.planning/STATE.md`,记录当前实现任务、授权边界和验证结果。
19+
- 修改:`.planning/KANBAN.md`,把已合并 PR #388 归档并登记唯一进行中的 `RESP-LIMITS-001`
20+
21+
### 任务 1:建立会安全失败的超限回归测试
22+
23+
**文件:**
24+
- 修改:`src/resp/src/parse.rs``tests` 模块
25+
26+
- [ ] **步骤 1:编写失败测试**
27+
28+
```rust
29+
#[test]
30+
fn test_reject_oversized_aggregate_lengths_without_panicking() {
31+
for frame in [
32+
"*9223372036854775807\r\n",
33+
"%9223372036854775807\r\n",
34+
"~9223372036854775807\r\n",
35+
">9223372036854775807\r\n",
36+
] {
37+
let mut parser = RespParse::new(RespVersion::RESP3);
38+
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
39+
parser.parse(Bytes::copy_from_slice(frame.as_bytes()))
40+
}));
41+
42+
assert!(matches!(result, Ok(RespParseResult::Error(_))), "{frame:?}");
43+
}
44+
}
45+
```
46+
47+
- [ ] **步骤 2:运行测试验证失败**
48+
49+
运行:`cargo test -p resp test_reject_oversized_aggregate_lengths_without_panicking -- --exact --nocapture`
50+
51+
预期:FAIL;现有实现发生 `capacity overflow``catch_unwind` 返回 `Err`,断言失败。
52+
53+
### 任务 2:实现统一的有界初始容量
54+
55+
**文件:**
56+
- 修改:`src/resp/src/parse.rs` 的常量、helper 和四个聚合解析函数
57+
58+
- [ ] **步骤 1:增加常量和 helper**
59+
60+
```rust
61+
const MAX_AGGREGATE_LENGTH: i64 = i32::MAX as i64;
62+
const MAX_PREALLOCATED_AGGREGATE_LENGTH: usize = 1024;
63+
64+
fn aggregate_capacity(
65+
input: &[u8],
66+
len: i64,
67+
) -> Result<usize, nom::Err<nom::error::Error<&[u8]>>> {
68+
if len > MAX_AGGREGATE_LENGTH {
69+
return Err(nom::Err::Failure(nom::error::Error::new(
70+
input,
71+
nom::error::ErrorKind::Verify,
72+
)));
73+
}
74+
75+
usize::try_from(len)
76+
.map(|len| len.min(MAX_PREALLOCATED_AGGREGATE_LENGTH))
77+
.map_err(|_| {
78+
nom::Err::Failure(nom::error::Error::new(
79+
input,
80+
nom::error::ErrorKind::Verify,
81+
))
82+
})
83+
}
84+
```
85+
86+
- [ ] **步骤 2:在四个调用点使用 helper**
87+
88+
各解析函数完成负数语义处理后调用 `Self::aggregate_capacity(input, len)?`,并把返回值传给 `Vec::with_capacity`。Map 的声明值表示 pair 数,不做乘二预分配。
89+
90+
- [ ] **步骤 3:运行红灯测试并确认转绿**
91+
92+
运行:`cargo test -p resp test_reject_oversized_aggregate_lengths_without_panicking -- --exact --nocapture`
93+
94+
预期:PASS;四种 frame 均返回 `RespParseResult::Error`,没有 panic。
95+
96+
### 任务 3:覆盖最大合法边界并验证兼容行为
97+
98+
**文件:**
99+
- 修改:`src/resp/src/parse.rs``tests` 模块
100+
101+
- [ ] **步骤 1:增加最大合法边界测试**
102+
103+
```rust
104+
#[test]
105+
fn test_maximum_aggregate_lengths_do_not_preallocate_declared_size() {
106+
for frame in [
107+
"*2147483647\r\n",
108+
"%2147483647\r\n",
109+
"~2147483647\r\n",
110+
">2147483647\r\n",
111+
] {
112+
let mut parser = RespParse::new(RespVersion::RESP3);
113+
assert_eq!(
114+
parser.parse(Bytes::copy_from_slice(frame.as_bytes())),
115+
RespParseResult::Incomplete,
116+
"{frame:?}"
117+
);
118+
}
119+
}
120+
```
121+
122+
- [ ] **步骤 2:运行 resp crate 完整测试**
123+
124+
运行:`cargo test -p resp`
125+
126+
预期:全部 PASS,现有正常、空、null、分片和 RESP3 聚合解析行为不变。
127+
128+
- [ ] **步骤 3:运行静态检查**
129+
130+
运行:`cargo clippy -p resp --all-targets -- -D warnings -D clippy::unwrap_used``cargo fmt --check``git diff --check`
131+
132+
预期:三条命令退出码均为 0,无 warning、格式或 whitespace error。
133+
134+
### 任务 4:同步项目状态并交付 PR
135+
136+
**文件:**
137+
- 修改:`.planning/STATE.md`
138+
- 修改:`.planning/KANBAN.md`
139+
140+
- [ ] **步骤 1:记录验证证据**
141+
142+
把任务 3 的 exact 命令、环境、结果和最终 commit 写入 STATE/KANBAN;不得把尚未完成的 GitHub checks 写成通过。
143+
144+
- [ ] **步骤 2:检查范围**
145+
146+
运行:`git status --short``git diff --stat origin/main...HEAD``git diff origin/main...HEAD`
147+
148+
预期:只有本计划“文件结构”列出的五个路径;无 Cargo、网络、认证、#402 文档或 Hot Tier 改动。
149+
150+
- [ ] **步骤 3:提交并推送**
151+
152+
提交信息:`fix(resp): bound aggregate parser allocations`
153+
154+
- [ ] **步骤 4:创建独立 PR**
155+
156+
PR base 必须是 `main`,正文引用 Issue #395 和 PR #402,但不得使用 `Closes #395`,因为本 PR 只处理其中 B1。

0 commit comments

Comments
 (0)