Skip to content

Commit 57999e3

Browse files
Kasper Jungeclaude
authored andcommitted
refactor: add explicit timed_out field to AgentResult for consistency with other result types
AgentResult was the only result type using an implicit convention (returncode is None) to signal timeout. CheckResult, ContextResult, RunResult, and _StreamResult all have explicit timed_out fields. This converts AgentResult from NamedTuple to dataclass with a timed_out: bool field, making the timeout condition self-documenting and the engine code more readable. Also fixes stale _discover_and_filter_enabled() references in codebase-map.md (the actual function is _discover_enabled_primitives()). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 7184437 commit 57999e3

4 files changed

Lines changed: 21 additions & 7 deletions

File tree

β€Ždocs/contributing/codebase-map.mdβ€Ž

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ ralph run
7171

7272
### The three primitives and the `Primitive` protocol
7373

74-
All three primitive types follow the same pattern: a directory under `.ralphify/` with a marker markdown file containing YAML frontmatter. Each type's dataclass (`Check`, `Context`, `Ralph`) satisfies the `Primitive` protocol defined in `_discovery.py`, which requires `name` and `enabled` properties. This enables type-safe generic functions for discovery, filtering, merging, and display β€” the engine's `_discover_and_filter_enabled()` helper uses the protocol to handle all three types through a single code path.
74+
All three primitive types follow the same pattern: a directory under `.ralphify/` with a marker markdown file containing YAML frontmatter. Each type's dataclass (`Check`, `Context`, `Ralph`) satisfies the `Primitive` protocol defined in `_discovery.py`, which requires `name` and `enabled` properties. This enables type-safe generic functions for discovery, filtering, merging, and display β€” the engine's `_discover_enabled_primitives()` helper uses the protocol to handle all three types through a single code path.
7575

7676
| Primitive | Marker file | Runs | Injects into prompt |
7777
|---|---|---|---|
@@ -136,7 +136,7 @@ You need to:
136136

137137
1. Create a new module (like `ralphs.py`) with a dataclass that satisfies the `Primitive` protocol (`name` and `enabled` properties), plus discover and resolve functions
138138
2. Add a scaffold template in `_templates.py` and a `new` subcommand in `cli.py`
139-
3. Wire it into `engine.py:run_loop()` β€” add it to `EnabledPrimitives` and use `_discover_and_filter_enabled()`
139+
3. Wire it into `engine.py:run_loop()` β€” add it to `EnabledPrimitives` and use `_discover_enabled_primitives()`
140140
4. Add tests
141141
5. Update `docs/primitives.md`
142142

β€Žsrc/ralphify/_agent.pyβ€Ž

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,20 +20,28 @@
2020
import sys
2121
import time
2222
from collections.abc import Callable
23+
from dataclasses import dataclass
2324
from datetime import datetime
2425
from pathlib import Path
2526
from typing import IO, NamedTuple
2627

2728
from ralphify._output import collect_output
2829

2930

30-
class AgentResult(NamedTuple):
31-
"""Result of running the agent subprocess."""
31+
@dataclass
32+
class AgentResult:
33+
"""Result of running the agent subprocess.
3234
33-
returncode: int | None # None means timed out
35+
*returncode* is the process exit code, or ``None`` when the process
36+
timed out. *timed_out* makes the timeout condition explicit β€” prefer
37+
checking ``timed_out`` over ``returncode is None``.
38+
"""
39+
40+
returncode: int | None
3441
elapsed: float
3542
log_file: Path | None
3643
result_text: str | None = None
44+
timed_out: bool = False
3745

3846

3947
class _StreamResult(NamedTuple):
@@ -147,9 +155,11 @@ def _run_agent_streaming(
147155
proc.kill()
148156
proc.wait()
149157
returncode = None
158+
timed_out = True
150159
else:
151160
proc.wait()
152161
returncode = proc.returncode
162+
timed_out = False
153163

154164
stderr_data = proc.stderr.read()
155165
finally:
@@ -166,6 +176,7 @@ def _run_agent_streaming(
166176
elapsed=time.monotonic() - start,
167177
log_file=log_file,
168178
result_text=stream.result_text,
179+
timed_out=timed_out,
169180
)
170181

171182

@@ -188,6 +199,7 @@ def _run_agent_blocking(
188199
start = time.monotonic()
189200
log_file: Path | None = None
190201
returncode: int | None = None
202+
timed_out = False
191203

192204
try:
193205
result = subprocess.run(
@@ -205,13 +217,15 @@ def _run_agent_blocking(
205217
sys.stderr.write(result.stderr)
206218
returncode = result.returncode
207219
except subprocess.TimeoutExpired as e:
220+
timed_out = True
208221
if log_path_dir:
209222
log_file = _write_log(log_path_dir, iteration, e.stdout, e.stderr)
210223

211224
return AgentResult(
212225
returncode=returncode,
213226
elapsed=time.monotonic() - start,
214227
log_file=log_file,
228+
timed_out=timed_out,
215229
)
216230

217231

β€Žsrc/ralphify/_events.pyβ€Ž

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ class EventType(Enum):
6666
ITERATION_COMPLETED = "iteration_completed"
6767
# Data: same as ITERATION_COMPLETED
6868
ITERATION_FAILED = "iteration_failed"
69-
# Data: same as ITERATION_COMPLETED (returncode is None)
69+
# Data: same as ITERATION_COMPLETED (returncode is None, timed_out is True)
7070
ITERATION_TIMED_OUT = "iteration_timed_out"
7171

7272
# ── Checks ──────────────────────────────────────────────────

β€Žsrc/ralphify/engine.pyβ€Ž

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,7 @@ def _run_agent_phase(
184184

185185
duration = format_duration(agent.elapsed)
186186

187-
if agent.returncode is None:
187+
if agent.timed_out:
188188
state.mark_timed_out()
189189
event_type = EventType.ITERATION_TIMED_OUT
190190
state_detail = f"timed out after {duration}"

0 commit comments

Comments
Β (0)