Skip to content

Commit c877dae

Browse files
test: tighten real-session fixture tests per CodeRabbit review
1 parent 5487fe8 commit c877dae

7 files changed

Lines changed: 129 additions & 65 deletions

scripts/gen_real_session_fixtures.py

Lines changed: 76 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
"""One-off generator for Tuesday real_session_*.jsonl fixtures. Run from repo root:
22
python scripts/gen_real_session_fixtures.py
3+
4+
Each entry includes top-level ``sessionId`` (real Claude Code JSONL shape). The parser
5+
currently ignores it and uses the filename for ``session_id``.
36
"""
47
from __future__ import annotations
58

@@ -10,16 +13,31 @@
1013
CWD = "/sanitized/project/path"
1114
TS = "2026-05-26T10:{:02d}:00Z"
1215

16+
# Sanitized sessionId values (one per fixture file).
17+
SESSION_IDS = {
18+
"minimal": "session-sanitized-minimal",
19+
"all_tool_types": "session-sanitized-all-tool-types",
20+
"nested_tools": "session-sanitized-nested-tools",
21+
"unknown_fields": "session-sanitized-unknown-fields",
22+
"malformed_lines": "session-sanitized-malformed-lines",
23+
}
24+
1325

1426
def _line(obj: dict) -> str:
1527
return json.dumps(obj, ensure_ascii=False) + "\n"
1628

1729

30+
def _stamp(entry: dict, session_id: str) -> dict:
31+
entry["sessionId"] = session_id
32+
return entry
33+
34+
1835
def _user(
1936
minute: int,
2037
text: str = "",
2138
tool_result: dict | None = None,
2239
*,
40+
session_id: str,
2341
sidechain: bool = False,
2442
extra: dict | None = None,
2543
) -> dict:
@@ -35,33 +53,51 @@ def _user(
3553
entry["isSidechain"] = True
3654
if extra:
3755
entry.update(extra)
38-
return entry
56+
return _stamp(entry, session_id)
3957

4058

41-
def _assistant(minute: int, content: list, model: str = "claude-sanitized") -> dict:
42-
return {
43-
"type": "assistant",
44-
"timestamp": TS.format(minute),
45-
"message": {
46-
"model": model,
47-
"content": content,
48-
"usage": {"input_tokens": 10, "output_tokens": 5},
59+
def _assistant(
60+
minute: int,
61+
content: list,
62+
*,
63+
session_id: str,
64+
model: str = "claude-sanitized",
65+
) -> dict:
66+
return _stamp(
67+
{
68+
"type": "assistant",
69+
"timestamp": TS.format(minute),
70+
"message": {
71+
"model": model,
72+
"content": content,
73+
"usage": {"input_tokens": 10, "output_tokens": 5},
74+
},
4975
},
50-
}
76+
session_id,
77+
)
5178

5279

5380
def write_minimal() -> None:
81+
sid = SESSION_IDS["minimal"]
5482
lines = [
55-
_user(0, "Sanitized minimal real-shaped session opener"),
56-
_assistant(1, [{"type": "text", "text": "Acknowledged."}]),
57-
_user(2, tool_result={"stdout": "sanitized output\n", "stderr": "", "exitCode": 0}),
83+
_user(0, "Sanitized minimal real-shaped session opener", session_id=sid),
84+
_assistant(1, [{"type": "text", "text": "Acknowledged."}], session_id=sid),
85+
_user(
86+
2,
87+
tool_result={"stdout": "sanitized output\n", "stderr": "", "exitCode": 0},
88+
session_id=sid,
89+
),
5890
]
5991
path = os.path.join(FIXTURES, "real_session_minimal.jsonl")
6092
with open(path, "w", encoding="utf-8", newline="\n") as f:
6193
f.writelines(_line(x) for x in lines)
6294

6395

6496
def write_all_tool_types() -> None:
97+
# 16 tool-result user entries: 15 to cover each dispatch predicate once, plus 1
98+
# overlap blob (agentId + totalDurationMs + message) for task_message-beats-
99+
# task_completed dispatch-order regression. Total lines: 2 opener + 16 = 18 messages.
100+
sid = SESSION_IDS["all_tool_types"]
65101
tool_results = [
66102
{"stdout": "ok\n", "stderr": "", "exitCode": 0},
67103
{"filePath": f"{CWD}/a.py", "structuredPatch": "@@ sanitized"},
@@ -105,22 +141,24 @@ def write_all_tool_types() -> None:
105141
},
106142
]
107143
lines = [
108-
_user(0, "Exercise all fifteen tool-result dispatch predicates"),
144+
_user(0, "Exercise all fifteen tool-result dispatch predicates", session_id=sid),
109145
_assistant(
110146
1,
111147
[{"type": "tool_use", "id": "tu-1", "name": "Bash", "input": {"command": "echo ok"}}],
148+
session_id=sid,
112149
),
113150
]
114151
for i, tr in enumerate(tool_results):
115-
lines.append(_user(2 + i, tool_result=tr))
152+
lines.append(_user(2 + i, tool_result=tr, session_id=sid))
116153
path = os.path.join(FIXTURES, "real_session_all_tool_types.jsonl")
117154
with open(path, "w", encoding="utf-8", newline="\n") as f:
118155
f.writelines(_line(x) for x in lines)
119156

120157

121158
def write_nested_tools() -> None:
159+
sid = SESSION_IDS["nested_tools"]
122160
lines = [
123-
_user(0, "Parent turn with nested subagent tool activity"),
161+
_user(0, "Parent turn with nested subagent tool activity", session_id=sid),
124162
_assistant(
125163
1,
126164
[
@@ -131,20 +169,25 @@ def write_nested_tools() -> None:
131169
"input": {"description": "sanitized subagent task"},
132170
}
133171
],
172+
session_id=sid,
134173
),
135174
_user(
136175
2,
137176
tool_result={"stdout": "sidechain output\n", "stderr": "", "exitCode": 0},
177+
session_id=sid,
138178
sidechain=True,
139179
),
140-
{
141-
"type": "progress",
142-
"timestamp": TS.format(3),
143-
"toolUseID": "child-tool-1",
144-
"parentToolUseID": "parent-tool-1",
145-
"isSidechain": True,
146-
"data": {"type": "bash_progress", "output": "sanitized streaming chunk"},
147-
},
180+
_stamp(
181+
{
182+
"type": "progress",
183+
"timestamp": TS.format(3),
184+
"toolUseID": "child-tool-1",
185+
"parentToolUseID": "parent-tool-1",
186+
"isSidechain": True,
187+
"data": {"type": "bash_progress", "output": "sanitized streaming chunk"},
188+
},
189+
sid,
190+
),
148191
_assistant(
149192
4,
150193
[
@@ -155,6 +198,7 @@ def write_nested_tools() -> None:
155198
"input": {"file_path": f"{CWD}/nested.txt"},
156199
}
157200
],
201+
session_id=sid,
158202
),
159203
]
160204
path = os.path.join(FIXTURES, "real_session_nested_tools.jsonl")
@@ -163,13 +207,15 @@ def write_nested_tools() -> None:
163207

164208

165209
def write_unknown_fields() -> None:
210+
sid = SESSION_IDS["unknown_fields"]
166211
lines = [
167212
_user(
168213
0,
169214
"Forward-compat entry with unknown top-level keys",
215+
session_id=sid,
170216
extra={"_futureSchemaVersion": 2, "experimentalFlag": True},
171217
),
172-
_assistant(1, [{"type": "text", "text": "Handled unknown fields."}]),
218+
_assistant(1, [{"type": "text", "text": "Handled unknown fields."}], session_id=sid),
173219
_user(
174220
2,
175221
tool_result={
@@ -178,6 +224,7 @@ def write_unknown_fields() -> None:
178224
"exitCode": 0,
179225
"_unknownToolMeta": {"vendor": "sanitized"},
180226
},
227+
session_id=sid,
181228
),
182229
]
183230
path = os.path.join(FIXTURES, "real_session_unknown_fields.jsonl")
@@ -186,9 +233,10 @@ def write_unknown_fields() -> None:
186233

187234

188235
def write_malformed_lines() -> None:
236+
sid = SESSION_IDS["malformed_lines"]
189237
valid = [
190-
_user(0, "Valid line before malformed section"),
191-
_assistant(1, [{"type": "text", "text": "Recovered after bad lines."}]),
238+
_user(0, "Valid line before malformed section", session_id=sid),
239+
_assistant(1, [{"type": "text", "text": "Recovered after bad lines."}], session_id=sid),
192240
]
193241
path = os.path.join(FIXTURES, "real_session_malformed_lines.jsonl")
194242
with open(path, "w", encoding="utf-8", newline="\n") as f:
@@ -204,6 +252,7 @@ def write_malformed_lines() -> None:
204252
3,
205253
"Valid line after malformed section",
206254
tool_result={"stderr": "warn only", "exitCode": 1},
255+
session_id=sid,
207256
)
208257
)
209258
)
Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
1-
{"type": "user", "timestamp": "2026-05-26T10:00:00Z", "cwd": "/sanitized/project/path", "message": {"content": [{"type": "text", "text": "Exercise all fifteen tool-result dispatch predicates"}]}}
2-
{"type": "assistant", "timestamp": "2026-05-26T10:01:00Z", "message": {"model": "claude-sanitized", "content": [{"type": "tool_use", "id": "tu-1", "name": "Bash", "input": {"command": "echo ok"}}], "usage": {"input_tokens": 10, "output_tokens": 5}}}
3-
{"type": "user", "timestamp": "2026-05-26T10:02:00Z", "cwd": "/sanitized/project/path", "message": {"content": []}, "toolUseResult": {"stdout": "ok\n", "stderr": "", "exitCode": 0}}
4-
{"type": "user", "timestamp": "2026-05-26T10:03:00Z", "cwd": "/sanitized/project/path", "message": {"content": []}, "toolUseResult": {"filePath": "/sanitized/project/path/a.py", "structuredPatch": "@@ sanitized"}}
5-
{"type": "user", "timestamp": "2026-05-26T10:04:00Z", "cwd": "/sanitized/project/path", "message": {"content": []}, "toolUseResult": {"filePath": "/sanitized/project/path/b.txt", "content": "sanitized body"}}
6-
{"type": "user", "timestamp": "2026-05-26T10:05:00Z", "cwd": "/sanitized/project/path", "message": {"content": []}, "toolUseResult": {"filenames": ["sanitized.py"], "numFiles": 1, "truncated": false}}
7-
{"type": "user", "timestamp": "2026-05-26T10:06:00Z", "cwd": "/sanitized/project/path", "message": {"content": []}, "toolUseResult": {"mode": "content", "numFiles": 1, "numLines": 2, "content": "sanitized match"}}
8-
{"type": "user", "timestamp": "2026-05-26T10:07:00Z", "cwd": "/sanitized/project/path", "message": {"content": []}, "toolUseResult": {"file": {"filePath": "/sanitized/project/path/readme.md", "numLines": 3, "content": "sanitized read"}}}
9-
{"type": "user", "timestamp": "2026-05-26T10:08:00Z", "cwd": "/sanitized/project/path", "message": {"content": []}, "toolUseResult": {"query": "sanitized query", "results": [{"url": "https://example.com/a"}]}}
10-
{"type": "user", "timestamp": "2026-05-26T10:09:00Z", "cwd": "/sanitized/project/path", "message": {"content": []}, "toolUseResult": {"url": "https://example.com/doc", "code": 200, "durationMs": 40}}
11-
{"type": "user", "timestamp": "2026-05-26T10:10:00Z", "cwd": "/sanitized/project/path", "message": {"content": []}, "toolUseResult": {"task_id": "task-sanitized-msg", "task_type": "sub"}}
12-
{"type": "user", "timestamp": "2026-05-26T10:11:00Z", "cwd": "/sanitized/project/path", "message": {"content": []}, "toolUseResult": {"retrieval_status": "found", "task": {"task_id": "task-sanitized-ret", "description": "REDACTED"}}}
13-
{"type": "user", "timestamp": "2026-05-26T10:12:00Z", "cwd": "/sanitized/project/path", "message": {"content": []}, "toolUseResult": {"agentId": "agent-sanitized-done", "totalDurationMs": 1000, "status": "completed"}}
14-
{"type": "user", "timestamp": "2026-05-26T10:13:00Z", "cwd": "/sanitized/project/path", "message": {"content": []}, "toolUseResult": {"agentId": "agent-sanitized-async", "isAsync": true, "status": "running", "description": "REDACTED background task"}}
15-
{"type": "user", "timestamp": "2026-05-26T10:14:00Z", "cwd": "/sanitized/project/path", "message": {"content": []}, "toolUseResult": {"newTodos": [{"id": "1", "content": "sanitized todo"}]}}
16-
{"type": "user", "timestamp": "2026-05-26T10:15:00Z", "cwd": "/sanitized/project/path", "message": {"content": []}, "toolUseResult": {"questions": [{"id": "q1"}], "answers": {"q1": "sanitized answer"}}}
17-
{"type": "user", "timestamp": "2026-05-26T10:16:00Z", "cwd": "/sanitized/project/path", "message": {"content": []}, "toolUseResult": {"plan": [], "filePath": "/sanitized/project/path/plan.md"}}
18-
{"type": "user", "timestamp": "2026-05-26T10:17:00Z", "cwd": "/sanitized/project/path", "message": {"content": []}, "toolUseResult": {"agentId": "agent-sanitized-overlap", "totalDurationMs": 500, "status": "completed", "message": "status update sanitized"}}
1+
{"type": "user", "timestamp": "2026-05-26T10:00:00Z", "cwd": "/sanitized/project/path", "message": {"content": [{"type": "text", "text": "Exercise all fifteen tool-result dispatch predicates"}]}, "sessionId": "session-sanitized-all-tool-types"}
2+
{"type": "assistant", "timestamp": "2026-05-26T10:01:00Z", "message": {"model": "claude-sanitized", "content": [{"type": "tool_use", "id": "tu-1", "name": "Bash", "input": {"command": "echo ok"}}], "usage": {"input_tokens": 10, "output_tokens": 5}}, "sessionId": "session-sanitized-all-tool-types"}
3+
{"type": "user", "timestamp": "2026-05-26T10:02:00Z", "cwd": "/sanitized/project/path", "message": {"content": []}, "toolUseResult": {"stdout": "ok\n", "stderr": "", "exitCode": 0}, "sessionId": "session-sanitized-all-tool-types"}
4+
{"type": "user", "timestamp": "2026-05-26T10:03:00Z", "cwd": "/sanitized/project/path", "message": {"content": []}, "toolUseResult": {"filePath": "/sanitized/project/path/a.py", "structuredPatch": "@@ sanitized"}, "sessionId": "session-sanitized-all-tool-types"}
5+
{"type": "user", "timestamp": "2026-05-26T10:04:00Z", "cwd": "/sanitized/project/path", "message": {"content": []}, "toolUseResult": {"filePath": "/sanitized/project/path/b.txt", "content": "sanitized body"}, "sessionId": "session-sanitized-all-tool-types"}
6+
{"type": "user", "timestamp": "2026-05-26T10:05:00Z", "cwd": "/sanitized/project/path", "message": {"content": []}, "toolUseResult": {"filenames": ["sanitized.py"], "numFiles": 1, "truncated": false}, "sessionId": "session-sanitized-all-tool-types"}
7+
{"type": "user", "timestamp": "2026-05-26T10:06:00Z", "cwd": "/sanitized/project/path", "message": {"content": []}, "toolUseResult": {"mode": "content", "numFiles": 1, "numLines": 2, "content": "sanitized match"}, "sessionId": "session-sanitized-all-tool-types"}
8+
{"type": "user", "timestamp": "2026-05-26T10:07:00Z", "cwd": "/sanitized/project/path", "message": {"content": []}, "toolUseResult": {"file": {"filePath": "/sanitized/project/path/readme.md", "numLines": 3, "content": "sanitized read"}}, "sessionId": "session-sanitized-all-tool-types"}
9+
{"type": "user", "timestamp": "2026-05-26T10:08:00Z", "cwd": "/sanitized/project/path", "message": {"content": []}, "toolUseResult": {"query": "sanitized query", "results": [{"url": "https://example.com/a"}]}, "sessionId": "session-sanitized-all-tool-types"}
10+
{"type": "user", "timestamp": "2026-05-26T10:09:00Z", "cwd": "/sanitized/project/path", "message": {"content": []}, "toolUseResult": {"url": "https://example.com/doc", "code": 200, "durationMs": 40}, "sessionId": "session-sanitized-all-tool-types"}
11+
{"type": "user", "timestamp": "2026-05-26T10:10:00Z", "cwd": "/sanitized/project/path", "message": {"content": []}, "toolUseResult": {"task_id": "task-sanitized-msg", "task_type": "sub"}, "sessionId": "session-sanitized-all-tool-types"}
12+
{"type": "user", "timestamp": "2026-05-26T10:11:00Z", "cwd": "/sanitized/project/path", "message": {"content": []}, "toolUseResult": {"retrieval_status": "found", "task": {"task_id": "task-sanitized-ret", "description": "REDACTED"}}, "sessionId": "session-sanitized-all-tool-types"}
13+
{"type": "user", "timestamp": "2026-05-26T10:12:00Z", "cwd": "/sanitized/project/path", "message": {"content": []}, "toolUseResult": {"agentId": "agent-sanitized-done", "totalDurationMs": 1000, "status": "completed"}, "sessionId": "session-sanitized-all-tool-types"}
14+
{"type": "user", "timestamp": "2026-05-26T10:13:00Z", "cwd": "/sanitized/project/path", "message": {"content": []}, "toolUseResult": {"agentId": "agent-sanitized-async", "isAsync": true, "status": "running", "description": "REDACTED background task"}, "sessionId": "session-sanitized-all-tool-types"}
15+
{"type": "user", "timestamp": "2026-05-26T10:14:00Z", "cwd": "/sanitized/project/path", "message": {"content": []}, "toolUseResult": {"newTodos": [{"id": "1", "content": "sanitized todo"}]}, "sessionId": "session-sanitized-all-tool-types"}
16+
{"type": "user", "timestamp": "2026-05-26T10:15:00Z", "cwd": "/sanitized/project/path", "message": {"content": []}, "toolUseResult": {"questions": [{"id": "q1"}], "answers": {"q1": "sanitized answer"}}, "sessionId": "session-sanitized-all-tool-types"}
17+
{"type": "user", "timestamp": "2026-05-26T10:16:00Z", "cwd": "/sanitized/project/path", "message": {"content": []}, "toolUseResult": {"plan": [], "filePath": "/sanitized/project/path/plan.md"}, "sessionId": "session-sanitized-all-tool-types"}
18+
{"type": "user", "timestamp": "2026-05-26T10:17:00Z", "cwd": "/sanitized/project/path", "message": {"content": []}, "toolUseResult": {"agentId": "agent-sanitized-overlap", "totalDurationMs": 500, "status": "completed", "message": "status update sanitized"}, "sessionId": "session-sanitized-all-tool-types"}
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
{"type": "user", "timestamp": "2026-05-26T10:00:00Z", "cwd": "/sanitized/project/path", "message": {"content": [{"type": "text", "text": "Valid line before malformed section"}]}}
2-
{"type": "assistant", "timestamp": "2026-05-26T10:01:00Z", "message": {"model": "claude-sanitized", "content": [{"type": "text", "text": "Recovered after bad lines."}], "usage": {"input_tokens": 10, "output_tokens": 5}}}
1+
{"type": "user", "timestamp": "2026-05-26T10:00:00Z", "cwd": "/sanitized/project/path", "message": {"content": [{"type": "text", "text": "Valid line before malformed section"}]}, "sessionId": "session-sanitized-malformed-lines"}
2+
{"type": "assistant", "timestamp": "2026-05-26T10:01:00Z", "message": {"model": "claude-sanitized", "content": [{"type": "text", "text": "Recovered after bad lines."}], "usage": {"input_tokens": 10, "output_tokens": 5}}, "sessionId": "session-sanitized-malformed-lines"}
33

44
{not valid json
55
{"type": "user", "timestamp": "2026-05-26T10:02:00Z", "message": {"content":
6-
{"type": "user", "timestamp": "2026-05-26T10:03:00Z", "cwd": "/sanitized/project/path", "message": {"content": [{"type": "text", "text": "Valid line after malformed section"}]}, "toolUseResult": {"stderr": "warn only", "exitCode": 1}}
6+
{"type": "user", "timestamp": "2026-05-26T10:03:00Z", "cwd": "/sanitized/project/path", "message": {"content": [{"type": "text", "text": "Valid line after malformed section"}]}, "toolUseResult": {"stderr": "warn only", "exitCode": 1}, "sessionId": "session-sanitized-malformed-lines"}
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
{"type": "user", "timestamp": "2026-05-26T10:00:00Z", "cwd": "/sanitized/project/path", "message": {"content": [{"type": "text", "text": "Sanitized minimal real-shaped session opener"}]}}
2-
{"type": "assistant", "timestamp": "2026-05-26T10:01:00Z", "message": {"model": "claude-sanitized", "content": [{"type": "text", "text": "Acknowledged."}], "usage": {"input_tokens": 10, "output_tokens": 5}}}
3-
{"type": "user", "timestamp": "2026-05-26T10:02:00Z", "cwd": "/sanitized/project/path", "message": {"content": []}, "toolUseResult": {"stdout": "sanitized output\n", "stderr": "", "exitCode": 0}}
1+
{"type": "user", "timestamp": "2026-05-26T10:00:00Z", "cwd": "/sanitized/project/path", "message": {"content": [{"type": "text", "text": "Sanitized minimal real-shaped session opener"}]}, "sessionId": "session-sanitized-minimal"}
2+
{"type": "assistant", "timestamp": "2026-05-26T10:01:00Z", "message": {"model": "claude-sanitized", "content": [{"type": "text", "text": "Acknowledged."}], "usage": {"input_tokens": 10, "output_tokens": 5}}, "sessionId": "session-sanitized-minimal"}
3+
{"type": "user", "timestamp": "2026-05-26T10:02:00Z", "cwd": "/sanitized/project/path", "message": {"content": []}, "toolUseResult": {"stdout": "sanitized output\n", "stderr": "", "exitCode": 0}, "sessionId": "session-sanitized-minimal"}

0 commit comments

Comments
 (0)