Summary
Two related schema-hygiene issues in result.json that compound:
A. Field duplication between top-level and agent_result
result.json writes three fields twice — once at top level and once inside the nested agent_result block:
{
"n_tool_calls": 9, ←
"n_skill_invocations": 0, ← duplicated
"n_prompts": 1, ←
"agent_result": {
"n_tool_calls": 9, ← duplicated
"n_skill_invocations": 0, ← duplicated
"n_prompts": 1 ← duplicated
}
}
Source: src/benchflow/rollout.py:514-520. Consumers must pick one location; whichever they pick goes stale on the next refactor.
B. started_at / finished_at are not ISO 8601
{
"started_at": "2026-05-24 21:48:26.264901", ← str(datetime), space sep, no TZ
"finished_at": "2026-05-24 21:50:04.087940", ← same
"idle_timeout_info": {
"last_activity_at": "2026-05-25T04:51:45.254841+00:00" ← ISO 8601 + TZ
}
}
result.json mixes formats within the same blob. The top-level fields come from rollout.py:540-541 (str(result.started_at)) but rewards.jsonl (rollout.py:262, 282) uses .isoformat() — same project, two parse paths.
Repro
J=$(ls -d /tmp/v05-jobs/test-include/20*/weighted-gdp-calc__*/ | head -1)
# A: duplication
jq '{
top_calls: .n_tool_calls,
nested_calls: .agent_result.n_tool_calls,
top_prompts: .n_prompts,
nested_prompts: .agent_result.n_prompts
}' "$J/result.json"
# B: timestamp drift
jq -r '.started_at, .finished_at' "$J/result.json" # space-separated
jq -r '.ts' "$J/rewards.jsonl" # ISO 8601
jq -r '.idle_timeout_info.last_activity_at' "$J/result.json" # ISO 8601 + TZ
Severity
P2.
- Duplication is bloat + latent staleness risk; not a current bug, but a refactor footgun.
- Timestamp drift forces every consumer (dashboard, check_results, downstream analysis) to branch on format. Already burned #491 (dashboard advisory drift) territory.
Proposed fix
A
Pick one canonical location. The flat top-level fields are likely the older shape; agent_result is the typed-model form. Either:
- Keep
agent_result and remove the top-level duplicates (preferred — agent_result is the typed shape that downstream consumers should use).
- Or keep flat top-level and remove from
agent_result.
Either way, this is a one-breaking-change bullet.
B
Replace str(result.started_at) with result.started_at.isoformat() (TZ-aware datetime needs astimezone(UTC).isoformat() if it isn't already). Audit rollout.py for other str(some_datetime) calls — a quick grep:
grep -n "str(.*started_at\|str(.*finished_at\|str(.*at\b" src/benchflow/rollout.py
Add a unit test that asserts every timestamp emitted by rollout.py parses with datetime.fromisoformat() and is TZ-aware.
Found via
v0.5 e2e validation §2 (threejs-to-obj) and §3 (data-to-d3) at 5a7a326.
Summary
Two related schema-hygiene issues in
result.jsonthat compound:A. Field duplication between top-level and
agent_resultresult.jsonwrites three fields twice — once at top level and once inside the nestedagent_resultblock:{ "n_tool_calls": 9, ← "n_skill_invocations": 0, ← duplicated "n_prompts": 1, ← "agent_result": { "n_tool_calls": 9, ← duplicated "n_skill_invocations": 0, ← duplicated "n_prompts": 1 ← duplicated } }Source:
src/benchflow/rollout.py:514-520. Consumers must pick one location; whichever they pick goes stale on the next refactor.B.
started_at/finished_atare not ISO 8601{ "started_at": "2026-05-24 21:48:26.264901", ← str(datetime), space sep, no TZ "finished_at": "2026-05-24 21:50:04.087940", ← same "idle_timeout_info": { "last_activity_at": "2026-05-25T04:51:45.254841+00:00" ← ISO 8601 + TZ } }result.jsonmixes formats within the same blob. The top-level fields come fromrollout.py:540-541(str(result.started_at)) butrewards.jsonl(rollout.py:262, 282) uses.isoformat()— same project, two parse paths.Repro
Severity
P2.
Proposed fix
A
Pick one canonical location. The flat top-level fields are likely the older shape;
agent_resultis the typed-model form. Either:agent_resultand remove the top-level duplicates (preferred —agent_resultis the typed shape that downstream consumers should use).agent_result.Either way, this is a one-breaking-change bullet.
B
Replace
str(result.started_at)withresult.started_at.isoformat()(TZ-aware datetime needsastimezone(UTC).isoformat()if it isn't already). Auditrollout.pyfor otherstr(some_datetime)calls — a quick grep:grep -n "str(.*started_at\|str(.*finished_at\|str(.*at\b" src/benchflow/rollout.pyAdd a unit test that asserts every timestamp emitted by
rollout.pyparses withdatetime.fromisoformat()and is TZ-aware.Found via
v0.5 e2e validation §2 (threejs-to-obj) and §3 (data-to-d3) at
5a7a326.