Skip to content

Commit c5c38d3

Browse files
Kasper Jungeclaude
authored andcommitted
refactor: use EventType enums instead of strings for Store event dispatch
The persistence Store used .value strings as handler dict keys and for comparisons, inconsistent with ConsoleEmitter which uses EventType enums directly. This made the dispatch fragile — renaming an enum value would silently break handler lookup and the check_passed string comparison on line 226. Now both emitters use the same type-safe pattern. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 26914ac commit c5c38d3

File tree

1 file changed

+29
-23
lines changed

1 file changed

+29
-23
lines changed

src/ralphify/ui/persistence.py

Lines changed: 29 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -68,10 +68,10 @@
6868
# Maps iteration-end event types to their database status values.
6969
# Explicit mapping avoids deriving status from event names via string
7070
# manipulation, which would break silently if event names changed.
71-
_ITERATION_STATUS: dict[str, str] = {
72-
EventType.ITERATION_COMPLETED.value: "completed",
73-
EventType.ITERATION_FAILED.value: "failed",
74-
EventType.ITERATION_TIMED_OUT.value: "timed_out",
71+
_ITERATION_STATUS: dict[EventType, str] = {
72+
EventType.ITERATION_COMPLETED: "completed",
73+
EventType.ITERATION_FAILED: "failed",
74+
EventType.ITERATION_TIMED_OUT: "timed_out",
7575
}
7676

7777

@@ -81,15 +81,15 @@ class Store:
8181
def __init__(self, db_path: Path | str | None = None) -> None:
8282
self._db_path = Path(db_path) if db_path else DEFAULT_DB_PATH
8383
self._db: aiosqlite.Connection | None = None
84-
self._event_handlers: dict[str, Callable[..., Any]] = {
85-
EventType.RUN_STARTED.value: self._on_run_started,
86-
EventType.RUN_STOPPED.value: self._on_run_stopped,
87-
EventType.ITERATION_STARTED.value: self._on_iteration_started,
88-
EventType.ITERATION_COMPLETED.value: self._on_iteration_ended,
89-
EventType.ITERATION_FAILED.value: self._on_iteration_ended,
90-
EventType.ITERATION_TIMED_OUT.value: self._on_iteration_ended,
91-
EventType.CHECK_PASSED.value: self._on_check_result,
92-
EventType.CHECK_FAILED.value: self._on_check_result,
84+
self._event_handlers: dict[EventType, Callable[..., Any]] = {
85+
EventType.RUN_STARTED: self._on_run_started,
86+
EventType.RUN_STOPPED: self._on_run_stopped,
87+
EventType.ITERATION_STARTED: self._on_iteration_started,
88+
EventType.ITERATION_COMPLETED: self._on_iteration_ended,
89+
EventType.ITERATION_FAILED: self._on_iteration_ended,
90+
EventType.ITERATION_TIMED_OUT: self._on_iteration_ended,
91+
EventType.CHECK_PASSED: self._on_check_result,
92+
EventType.CHECK_FAILED: self._on_check_result,
9393
}
9494

9595
async def init(self) -> None:
@@ -127,25 +127,31 @@ async def save_event(self, event: dict[str, Any]) -> None:
127127
"""
128128
db = self._conn
129129
run_id = event["run_id"]
130-
event_type = event["type"]
130+
event_type_str = event["type"]
131131
data = event.get("data", {})
132132
timestamp = event["timestamp"]
133133

134-
# Append to events table
134+
# Append to events table (store the string value for SQL portability)
135135
await db.execute(
136136
"INSERT INTO events (run_id, event_type, data, timestamp) VALUES (?, ?, ?, ?)",
137-
(run_id, event_type, json.dumps(data), timestamp),
137+
(run_id, event_type_str, json.dumps(data), timestamp),
138138
)
139139

140-
# Upsert materialized views via dispatch
140+
# Convert to enum for type-safe handler dispatch
141+
try:
142+
event_type = EventType(event_type_str)
143+
except ValueError:
144+
await db.commit()
145+
return
146+
141147
handler = self._event_handlers.get(event_type)
142148
if handler:
143149
await handler(event_type, run_id, data, timestamp)
144150

145151
await db.commit()
146152

147153
async def _on_run_started(
148-
self, event_type: str, run_id: str, data: dict[str, Any], timestamp: str,
154+
self, event_type: EventType, run_id: str, data: dict[str, Any], timestamp: str,
149155
) -> None:
150156
db = self._conn
151157
await db.execute(
@@ -159,7 +165,7 @@ async def _on_run_started(
159165
)
160166

161167
async def _on_run_stopped(
162-
self, event_type: str, run_id: str, data: dict[str, Any], timestamp: str,
168+
self, event_type: EventType, run_id: str, data: dict[str, Any], timestamp: str,
163169
) -> None:
164170
db = self._conn
165171
await db.execute(
@@ -176,7 +182,7 @@ async def _on_run_stopped(
176182
)
177183

178184
async def _on_iteration_started(
179-
self, event_type: str, run_id: str, data: dict[str, Any], timestamp: str,
185+
self, event_type: EventType, run_id: str, data: dict[str, Any], timestamp: str,
180186
) -> None:
181187
db = self._conn
182188
iteration = data.get("iteration", 0)
@@ -191,7 +197,7 @@ async def _on_iteration_started(
191197
)
192198

193199
async def _on_iteration_ended(
194-
self, event_type: str, run_id: str, data: dict[str, Any], timestamp: str,
200+
self, event_type: EventType, run_id: str, data: dict[str, Any], timestamp: str,
195201
) -> None:
196202
db = self._conn
197203
iteration = data.get("iteration", 0)
@@ -211,7 +217,7 @@ async def _on_iteration_ended(
211217
)
212218

213219
async def _on_check_result(
214-
self, event_type: str, run_id: str, data: dict[str, Any], timestamp: str,
220+
self, event_type: EventType, run_id: str, data: dict[str, Any], timestamp: str,
215221
) -> None:
216222
db = self._conn
217223
iteration = data.get("iteration", 0)
@@ -223,7 +229,7 @@ async def _on_check_result(
223229
run_id,
224230
iteration,
225231
data.get("check_name", ""),
226-
1 if event_type == "check_passed" else 0,
232+
1 if event_type == EventType.CHECK_PASSED else 0,
227233
data.get("exit_code"),
228234
1 if data.get("timed_out") else 0,
229235
),

0 commit comments

Comments
 (0)