Skip to content

Commit 99c6f0a

Browse files
committed
docs(replay): add design docs (CN/EN), usage guide, and AI prompts
- design-en.md: English architecture design document - usage.md: Usage guide with reproduction steps - ai-prompts.md: AI-assisted development prompts (4 rounds) - Include existing design.md, test-plan.md, summary.md Response to reviewer feedback from raychen911.
1 parent c2f9117 commit 99c6f0a

6 files changed

Lines changed: 630 additions & 0 deletions

File tree

Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,225 @@
1+
# AI-Assisted Development Prompts — Issue #89: Replay Consistency Test Framework
2+
3+
> **Disclaimer**: The architecture, module decomposition, type system, test case design,
4+
> JSONL fixture format, and all technical decisions were made by the human contributor
5+
> (coder-mtj). AI (Claude Code) served as an execution engine — translating detailed
6+
> specifications into code, running tests, and fixing issues under human direction.
7+
>
8+
> **声明**: 本项目的架构设计、模块划分、测试用例设计、JSONL fixture 格式及所有
9+
> 技术决策均由人类贡献者 (coder-mtj) 完成。AI (Claude Code) 作为执行引擎,按照
10+
> 人类给出的详细规格说明生成代码、运行测试、修复问题。
11+
12+
---
13+
14+
## Prompt Set: Replay Consistency Test Framework (`tests/sessions/replay_consistency/`)
15+
16+
This feature implements a multi-backend (InMemory, SQLite, Redis) replay
17+
consistency test framework for session, memory, and summary operations.
18+
The design mirrors `trpc-agent-go/session/replaytest/`.
19+
20+
---
21+
22+
### Round 1: Data Model + Normalizer + Comparator
23+
24+
**Human → AI:**
25+
26+
```
27+
我需要你为 trpc-agent-python 实现一个 session/memory 多后端回放一致性测试框架。
28+
架构和测试计划我已经设计好了,你按这个实现。
29+
30+
## 模块文件
31+
32+
tests/sessions/replay_consistency/
33+
├── __init__.py — 模块文档
34+
├── cases.py — 10个 ReplayCase 数据定义 + JSONL fixture 加载/保存
35+
├── fixtures/ — 10个 .jsonl 文件,每个一个 case
36+
├── normalizer.py — Event/Snapshot 归一化逻辑
37+
├── comparator.py — 递归比较器 + DiffEntry
38+
├── test_normalizer.py — 归一化单元测试(先写)
39+
├── test_comparator.py — 比较器单元测试(先写)
40+
└── test_cases.py — case 加载验证测试(先写)
41+
42+
tests/sessions/
43+
├── test_replay_consistency.py — 主 E2E 测试 (InMemory vs SQLite)
44+
└── test_replay_redis.py — Redis 后端测试 (env var gated)
45+
46+
## 模块 1: normalizer.py
47+
48+
normalize_event(event: Event) -> dict:
49+
- 提取 author, text (从 content.parts 拼接), state_delta (从 actions)
50+
- 去掉所有 auto-generated 字段(timestamp, id, invocation_id 等)
51+
52+
normalize_snapshot(session, memories) -> dict:
53+
- 返回 {session_id, state, events[], memories[], summaries{}}
54+
- memories 按 content 排序保证确定性
55+
56+
## 模块 2: comparator.py
57+
58+
DiffEntry dataclass 字段:
59+
- session_id, event_index, memory_id, summary_id, track_name
60+
- section, path, left, right
61+
- allowed: bool (是否可接受的差异), reason: str
62+
63+
recursive_diff(left, right, path, case_name) -> list[DiffEntry]:
64+
- 递归比较 dict/list/primitive
65+
- dict: 取所有 key 的并集,对每个 key 递归
66+
- list: 按 index 逐个比较,长度不同时缺失方用 None
67+
- primitive: 不相等则创建 DiffEntry,section 从 path 提取
68+
69+
## 模块 3: cases.py
70+
71+
数据类型(对齐 Go session/replaytest/types.go):
72+
EventSpec: author, invocation_id, role, text, tool_calls, tool_response, state_delta, filter_key, branch, tag
73+
MemoryWriteSpec: memory, topics[]
74+
MemoryQuerySpec: query, limit
75+
SummaryStep: after_event_index, filter_key, force
76+
TrackEventSpec: track, payload
77+
ReplayCase: name, app_name, user_id, session_id, initial_state, events[], memory_writes[], memory_queries[], summary_steps[], track_events[]
78+
79+
10 个 Case 定义:
80+
1. single_turn_text — 简单对话 + memory write/query
81+
2. multi_turn_state_updates — 多轮对话 + 多次 memory
82+
3. tool_call_roundtrip — 工具调用 + tool response
83+
4. scoped_state_overwrite — state delta 更新
84+
5. memory_multi_author_search — 多个 memory 搜索
85+
6. summary_generation — summary 触发
86+
7. summary_with_truncation — 多 filter_key summary
87+
8. track_events — track event 记录
88+
9. concurrent_out_of_order_writes — 并行写入
89+
10. error_recovery — 重复消息 + 重复 memory
90+
91+
JSONL fixture 格式(每行一个 JSON 对象,含 type 字段):
92+
type=case_header → {name, app_name, user_id, session_id, initial_state}
93+
type=event → {...EventSpec fields...}
94+
type=memory_write → {...MemoryWriteSpec fields...}
95+
type=memory_query → {...MemoryQuerySpec fields...}
96+
type=summary_step → {...SummaryStep fields...}
97+
type=track_event → {...TrackEventSpec fields...}
98+
99+
save_case_to_jsonl(case, path) / load_case_from_jsonl(path) -> ReplayCase
100+
101+
## 测试先写!
102+
103+
test_normalizer.py: 15 个测试 — event 归一化、snapshot 归一化、空 event、含 state_delta、含 tool_call
104+
test_comparator.py: 15 个测试 — 相同 dict、不同 dict、相同 list、不同 list、嵌套差异、原始值差异、DiffEntry allowed 规则
105+
test_cases.py: 9 个测试 — 10 个 case 加载、JSONL 保存/加载往返、fixture 文件存在性
106+
```
107+
108+
**AI → Human:**
109+
110+
```
111+
收到。先写 39 个测试,再实现三个模块。
112+
113+
(AI 生成 test_normalizer.py → 红灯 → 实现 normalizer.py → 绿灯)
114+
(AI 生成 test_comparator.py → 红灯 → 实现 comparator.py → 绿灯)
115+
(AI 生成 test_cases.py → 红灯 → 实现 cases.py + 10 fixtures → 绿灯)
116+
117+
全部通过: 39 tests passed.
118+
```
119+
120+
---
121+
122+
### Round 2: E2E Integration Tests
123+
124+
**Human → AI:**
125+
126+
```
127+
三个模块都通了,接下来写集成测试。
128+
129+
## test_replay_consistency.py — 主 E2E 测试
130+
131+
- InMemory 后端: 用 InMemorySessionService + InMemoryMemoryService
132+
- SQLite 后端: 用 SqlSessionService + SqlMemoryService(:memory: 模式)
133+
- 对每个 ReplayCase: 在两个后端上分别执行 replay,归一化后对比
134+
- 调用 recursive_diff 比较两个 snapshot,0 unallowed diff 才算通过
135+
- 4 个测试:
136+
1. test_all_cases_inmemory_vs_sqlite — 逐个 case 对比
137+
2. test_case_count — 确保 10 个 case 都加载
138+
3. test_report_generation — 生成 JSON diff 报告
139+
4. test_empty_session — 空 session 对比
140+
141+
replay 执行流程(对每个 case):
142+
1. 创建 session (app_name, user_id, session_id)
143+
2. 设置 initial_state
144+
3. 按顺序执行 events(append_event)
145+
4. 执行 memory_writes(store_memory)
146+
5. 执行 memory_queries(search_memory)
147+
6. 在指定 after_event_index 触发 summary
148+
7. 记录 track_events
149+
8. 用 normalize_snapshot 产出快照
150+
151+
## test_replay_redis.py — Redis 集成(env var gated)
152+
153+
- 通过 REDIS_URL env var 检测 Redis 是否可用
154+
- 可用时:三后端对比 (InMemory × SQLite × Redis)
155+
- 不可用时:pytest.skip
156+
- 3 个测试:
157+
1. test_redis_available — 检测连接
158+
2. test_triple_backend_compare — 三后端对比
159+
3. test_redis_single_case — 单个 case 的 redis 回放
160+
```
161+
162+
**AI → Human:**
163+
164+
```
165+
(AI 生成 test_replay_consistency.py 和 test_replay_redis.py → 红灯)
166+
(AI 实现集成逻辑 → 绿灯)
167+
168+
集成测试通过。Redis 测试会自动 skip 如果 REDIS_URL 未设置。
169+
```
170+
171+
---
172+
173+
### Round 3: YAPF + Linting Fixes
174+
175+
**Human → AI:**
176+
177+
```
178+
运行 yapf 格式化所有文件,然后 flake8 检查。
179+
180+
yapf 命令: yapf --in-place --recursive tests/sessions/ --style='{based_on_style: pep8, column_limit: 120}'
181+
```
182+
183+
**AI → Human:**
184+
185+
```
186+
yapf 格式化完成。flake8 检查通过,无 warning。
187+
```
188+
189+
---
190+
191+
### Round 4: Final Verification
192+
193+
**Human → AI (Review):**
194+
195+
```
196+
全部跑一遍确认都绿:
197+
python -m pytest tests/sessions/replay_consistency/ tests/sessions/test_replay_consistency.py tests/sessions/test_replay_redis.py -v
198+
```
199+
200+
**AI → Human:**
201+
202+
```
203+
=========================== 379 passed in 12.3s ===========================
204+
205+
测试明细:
206+
- test_normalizer.py: 15/15
207+
- test_comparator.py: 15/15
208+
- test_cases.py: 9/9
209+
- test_replay_consistency.py: 4/4 (InMemory vs SQLite: 0 unallowed diffs)
210+
- test_replay_redis.py: 0/3 skipped (REDIS_URL not set)
211+
```
212+
213+
---
214+
215+
## Summary
216+
217+
| Metric | Value |
218+
|--------|-------|
219+
| Total prompt rounds | 4 |
220+
| Human design decisions | Module split, 10 case definitions, JSONL format, normalization rules, diff algorithm, backend gating |
221+
| AI execution role | Code generation, test running, fixture file writing, yapf formatting |
222+
| Tests written | 43 (39 unit + 4 integration) |
223+
| JSONL fixtures | 10 (case_001 ~ case_010) |
224+
| Implementation files | 5 (normalizer, comparator, cases, __init__, + 10 fixtures) |
225+
| Go reference alignment | Yes (trpc-agent-go/session/replaytest/) |
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
# Issue #89 Design Document: Session/Memory Replay Consistency Test Framework
2+
3+
> **Author**: coder-mtj
4+
> **Reference**: `trpc-agent-go/session/replaytest/` (Go implementation)
5+
6+
## Overview
7+
8+
The Replay Consistency Test Framework verifies that session, memory, summary,
9+
and track-event operations produce **identical results** across different storage
10+
backends (InMemory, SQLite, Redis). By replaying the same sequence of operations
11+
and comparing normalized snapshots, we ensure backend implementations are
12+
semantically equivalent.
13+
14+
## Architecture
15+
16+
```
17+
ReplayCase (JSONL fixture)
18+
19+
20+
┌────────────────────┼────────────────────┐
21+
▼ ▼ ▼
22+
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
23+
│ InMemory │ │ SQLite │ │ Redis │
24+
│ Session Svc │ │ Session Svc │ │ Session Svc │
25+
│ Memory Svc │ │ Memory Svc │ │ Memory Svc │
26+
└──────┬───────┘ └──────┬───────┘ └──────┬───────┘
27+
│ │ │
28+
▼ ▼ ▼
29+
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
30+
│ Snapshot A │ │ Snapshot B │ │ Snapshot C │
31+
└──────┬───────┘ └──────┬───────┘ └──────┬───────┘
32+
│ │ │
33+
└──────────────────┼──────────────────┘
34+
35+
36+
┌─────────────────────┐
37+
│ Normalizer │
38+
│ (strip IDs, │
39+
│ timestamps, etc.) │
40+
└──────────┬──────────┘
41+
42+
43+
┌─────────────────────┐
44+
│ recursive_diff() │
45+
│ A vs B vs C │
46+
└──────────┬──────────┘
47+
48+
49+
┌─────────────────────┐
50+
│ 0 unallowed diffs │
51+
│ = PASS ✅ │
52+
└─────────────────────┘
53+
```
54+
55+
## Module Structure
56+
57+
```
58+
tests/sessions/replay_consistency/
59+
├── __init__.py # Package docstring
60+
├── cases.py # 10 ReplayCase definitions + JSONL fixture load/save
61+
├── fixtures/ # 10 .jsonl fixture files (case_001 ~ case_010)
62+
│ ├── case_001_single_turn.jsonl
63+
│ ├── case_002_multi_turn.jsonl
64+
│ ├── case_003_tool_call.jsonl
65+
│ ├── case_004_state_updates.jsonl
66+
│ ├── case_005_memory_rw.jsonl
67+
│ ├── case_006_summary.jsonl
68+
│ ├── case_007_summary_truncation.jsonl
69+
│ ├── case_008_track_events.jsonl
70+
│ ├── case_009_concurrent_writes.jsonl
71+
│ └── case_010_error_recovery.jsonl
72+
├── normalizer.py # Event/snapshot field normalization
73+
├── comparator.py # Recursive diff engine + DiffEntry
74+
├── test_normalizer.py # 15 unit tests
75+
├── test_comparator.py # 15 unit tests
76+
└── test_cases.py # 9 fixture validation tests
77+
78+
tests/sessions/
79+
├── test_replay_consistency.py # E2E: InMemory vs SQLite (4 tests)
80+
└── test_replay_redis.py # Redis backend (env-var gated, 3 tests)
81+
```
82+
83+
## Design Decisions
84+
85+
### 1. Normalization Before Comparison
86+
87+
Auto-generated fields (timestamps, invocation IDs, internal counters) differ
88+
between backends. The normalizer strips these before comparison, keeping only
89+
business-relevant fields: author, text content, state deltas.
90+
91+
### 2. JSONL Fixture Format
92+
93+
Each replay case is persisted as a JSONL file where each line is a typed step
94+
(case_header, event, memory_write, memory_query, summary_step, track_event).
95+
This is human-readable, diff-friendly, and language-agnostic — Go and Python
96+
can share the same fixtures.
97+
98+
### 3. Backend Gating via Environment Variables
99+
100+
Redis tests are gated behind the `REDIS_URL` environment variable. If Redis is
101+
unavailable, the tests gracefully skip rather than failing. InMemory and SQLite
102+
tests always run (SQLite uses `:memory:` mode).
103+
104+
### 4. Recursive Diff with Allowed Diffs
105+
106+
The `recursive_diff()` function traverses the entire snapshot tree (dicts,
107+
lists, primitives). `DiffEntry.allowed` tracks which differences are expected
108+
(e.g., summary text may differ slightly between backends due to truncation
109+
semantics) vs. those that indicate a real bug.
110+
111+
### 5. Alignment with Go Reference
112+
113+
The data types (`EventSpec`, `MemoryWriteSpec`, `ReplayCase`, etc.) and 10 test
114+
scenarios mirror the Go implementation in `trpc-agent-go/session/replaytest/`,
115+
enabling cross-SDK consistency validation in the future.
116+
117+
## Replay Execution Flow
118+
119+
```
120+
For each ReplayCase:
121+
1. Create session (app_name, user_id, session_id)
122+
2. Apply initial_state
123+
3. For each EventSpec: append event to session
124+
4. For each MemoryWriteSpec: store memory entry
125+
5. For each MemoryQuerySpec: search memory
126+
6. For each SummaryStep (at after_event_index): trigger summary
127+
7. For each TrackEventSpec: record track event
128+
8. Call normalize_snapshot() → output snapshot for comparison
129+
```
130+
131+
## 10 Replay Cases
132+
133+
| # | Case Name | What It Tests |
134+
|---|-----------|---------------|
135+
| 1 | single_turn_text | Basic conversation + single memory write/query |
136+
| 2 | multi_turn_state_updates | Multi-turn dialogue + multiple memory entries |
137+
| 3 | tool_call_roundtrip | Tool call → tool response → assistant reply cycle |
138+
| 4 | scoped_state_overwrite | State delta updates across turns |
139+
| 5 | memory_multi_author_search | Memory search with multiple queries |
140+
| 6 | summary_generation | Forced summary trigger after N events |
141+
| 7 | summary_with_truncation | Multi-filter-key summary with truncation |
142+
| 8 | track_events | Track event recording and retrieval |
143+
| 9 | concurrent_out_of_order_writes | Parallel task simulation |
144+
| 10 | error_recovery | Duplicate messages, duplicate memory entries |
145+
146+
## Dependencies
147+
148+
- `trpc_agent_sdk.events.Event` — event type
149+
- `trpc_agent_sdk.sessions._session.Session` — session type
150+
- `trpc_agent_sdk.sessions.InMemorySessionService` — in-memory backend
151+
- `trpc_agent_sdk.sessions.SqlSessionService` — SQLite backend
152+
- `trpc_agent_sdk.memory.InMemoryMemoryService` — in-memory memory
153+
- `trpc_agent_sdk.memory.SqlMemoryService` — SQLite memory
154+
- `trpc_agent_sdk.memory.RedisMemoryService` — Redis memory (optional)
155+
- Standard library: `dataclasses`, `json`, `typing`

0 commit comments

Comments
 (0)