Skip to content

Commit 472cf7c

Browse files
Kasper JungeRalphify
authored andcommitted
refactor: use tuple for _StreamResult.stdout_lines to honour frozen contract
frozen=True on a dataclass signals immutability, but list is mutable — callers can silently mutate the field even though frozen prevents reassignment. Switching to tuple[str, …] makes _StreamResult truly immutable and properly hashable. Also removes a stale unused import of _IterationPanel from the TUI snapshot harness. Co-authored-by: Ralphify <noreply@ralphify.co>
1 parent e4da20a commit 472cf7c

2 files changed

Lines changed: 13 additions & 16 deletions

File tree

scripts/tui_dev/snapshot.py

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,9 @@
4343
from ralphify._console_emitter import ( # noqa: E402
4444
ConsoleEmitter,
4545
_FullscreenPeek,
46-
_IterationPanel,
4746
_IterationSpinner,
47+
_LivePanelBase,
48+
_SinglePanelNavigator,
4849
)
4950
from ralphify._events import ( # noqa: E402
5051
Event,
@@ -62,18 +63,14 @@ class _SnapshotConsoleEmitter(ConsoleEmitter):
6263
6364
In snapshot mode we want a single, frozen rendering of the iteration
6465
panel — not a Live refresh loop that writes the panel to the record
65-
console multiple times. Overriding ``_start_live_unlocked`` creates
66-
the panel/spinner but leaves ``_live=None``, so ``_on_agent_activity``
67-
still routes to ``panel.apply()`` without triggering any draw.
66+
console multiple times. Overriding ``_start_compact_live_unlocked``
67+
leaves ``_live=None`` so ``_on_agent_activity`` still routes to
68+
``panel.apply()`` without triggering any draw.
6869
"""
6970

70-
def _start_live_unlocked(self) -> None: # type: ignore[override]
71-
if self._structured_agent:
72-
self._iteration_panel = _IterationPanel()
73-
self._iteration_spinner = None
74-
else:
75-
self._iteration_panel = None
76-
self._iteration_spinner = _IterationSpinner()
71+
def _start_compact_live_unlocked( # type: ignore[override]
72+
self, renderable: _LivePanelBase
73+
) -> None:
7774
self._live = None
7875

7976

@@ -290,7 +287,7 @@ def _snapshot_fullscreen_peek() -> None:
290287
panel = emitter._iteration_panel
291288
assert panel is not None
292289

293-
view = _FullscreenPeek(panel)
290+
view = _FullscreenPeek(_SinglePanelNavigator(panel), 1)
294291
view._console_height = 40
295292
console.print(view)
296293
_save(console, "15_fullscreen_peek")

src/ralphify/_agent.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,7 @@ class AgentResult(ProcessResult):
237237
class _StreamResult:
238238
"""Accumulated output from reading the agent's JSON stream."""
239239

240-
stdout_lines: list[str]
240+
stdout_lines: tuple[str, ...]
241241
result_text: str | None
242242
timed_out: bool
243243

@@ -347,14 +347,14 @@ def _read_agent_stream(
347347
except queue.Empty:
348348
# Deadline expired while waiting for a line.
349349
return _StreamResult(
350-
stdout_lines=stdout_lines,
350+
stdout_lines=tuple(stdout_lines),
351351
result_text=result_text,
352352
timed_out=True,
353353
)
354354

355355
if line is None: # EOF sentinel from reader thread
356356
return _StreamResult(
357-
stdout_lines=stdout_lines,
357+
stdout_lines=tuple(stdout_lines),
358358
result_text=result_text,
359359
timed_out=False,
360360
)
@@ -390,7 +390,7 @@ def _read_agent_stream(
390390
# past the deadline.
391391
if deadline is not None and time.monotonic() > deadline:
392392
return _StreamResult(
393-
stdout_lines=stdout_lines,
393+
stdout_lines=tuple(stdout_lines),
394394
result_text=result_text,
395395
timed_out=True,
396396
)

0 commit comments

Comments
 (0)