feat(observability): expose immutable runtime snapshots - #105
Conversation
🤖 CodeAnt AI — Review Status
|
Thanks for using CodeAnt! 🎉We're free for open-source projects. if you're enjoying it, help us grow by sharing. Share on X · |
| @classmethod | ||
| def read(cls, *, session_id: str, path: Path) -> RunTrace: | ||
| """Read a session's trace without introducing another trace store.""" | ||
| events = tuple(read_events(path)) |
There was a problem hiding this comment.
Suggestion: The aggregate is only shallowly immutable: each TraceEvent exposes its original mutable attributes mapping, so callers can mutate trace.events[0].attributes despite the frozen RunTrace contract. Copy and freeze the attribute mappings when constructing the aggregate, or make TraceEvent deeply immutable. [possible bug]
Severity Level: Major ⚠️
- ⚠️ Public `RunTrace` snapshots can change after construction.
- ⚠️ Query and serialization results can observe caller mutations.
- ⚠️ Event data no longer represents a stable point-in-time view.Prompt for AI Agent 🤖
This is a comment left during a code review.
**Path:** src/dream/observability/_run_trace.py
**Line:** 27:27
**Comment:**
*Possible Bug: The aggregate is only shallowly immutable: each `TraceEvent` exposes its original mutable `attributes` mapping, so callers can mutate `trace.events[0].attributes` despite the frozen `RunTrace` contract. Copy and freeze the attribute mappings when constructing the aggregate, or make `TraceEvent` deeply immutable.
Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fixThere was a problem hiding this comment.
Fixed in 9df8677. RunTrace now rebuilds each event with recursively frozen JSON value objects; the regression test covers nested objects and arrays.
Greptile SummaryThe PR adds a public
Confidence Score: 3/5The PR should not merge until snapshots and run traces enforce the immutable point-in-time behavior promised by their public contracts. Both new read models freeze only their outer containers, allowing nested metadata, tool-input, and trace-attribute collections to be modified after capture. Files Needing Attention: src/dream/session.py, src/dream/services/session_store.py, src/dream/observability/_run_trace.py
|
| Filename | Overview |
|---|---|
| src/dream/services/session_store.py | Converts durable record collections to tuples and preserves codec behavior, but shallow conversion leaves nested JSON collections mutable. |
| src/dream/session.py | Builds tuple-backed snapshots while retaining references to nested mutable metadata values. |
| src/dream/observability/_run_trace.py | Adds session-validated trace aggregation, but returned events still expose mutable attribute dictionaries. |
| src/dream/harness.py | Converts tuple-backed snapshot metadata into a fresh dictionary before restoring session options. |
| tests/test_runtime_contracts.py | Covers outer tuple and frozen-dataclass behavior but not nested collection immutability. |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart LR
A[Live session metadata and tool inputs] --> B[Session.snapshot]
B --> C[Tuple-backed SessionSnapshot]
C --> D[File-store serialization]
E[JSONL trace file] --> F[read_events]
F --> G[Tuple-backed RunTrace]
C -. nested mutable values retained .-> A
G -. mutable event attributes exposed .-> H[Control-plane consumer]
Prompt To Fix All With AI
### Issue 1
src/dream/session.py:284
**Nested snapshot values remain mutable**
When metadata or tool input contains a nested list or dictionary, snapshot construction retains that object by reference inside the new tuples, causing a captured snapshot to change after creation and potentially serialize state from after the capture point.
### Issue 2
src/dream/observability/_run_trace.py:27
**Trace attributes remain mutable**
When a consumer modifies `trace.events[n].attributes` or one of its nested collections, `RunTrace.read()` has exposed the parser's mutable dictionary unchanged, causing the contents of the advertised immutable aggregate to change after it was read.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.Reviews (1): Last reviewed commit: "feat(observability): expose immutable ru..." | Re-trigger Greptile
| max_turns=self._effective_max_turns(), | ||
| working_dir=self._working_dir(), | ||
| metadata=metadata, | ||
| metadata=tuple(metadata.items()), |
There was a problem hiding this comment.
Nested snapshot values remain mutable
When metadata or tool input contains a nested list or dictionary, snapshot construction retains that object by reference inside the new tuples, causing a captured snapshot to change after creation and potentially serialize state from after the capture point.
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/dream/session.py
Line: 284
Comment:
**Nested snapshot values remain mutable**
When metadata or tool input contains a nested list or dictionary, snapshot construction retains that object by reference inside the new tuples, causing a captured snapshot to change after creation and potentially serialize state from after the capture point.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.There was a problem hiding this comment.
Fixed in 9df8677. Session metadata and tool inputs are recursively captured as immutable JSON value objects, including persistence round trips.
| @classmethod | ||
| def read(cls, *, session_id: str, path: Path) -> RunTrace: | ||
| """Read a session's trace without introducing another trace store.""" | ||
| events = tuple(read_events(path)) |
There was a problem hiding this comment.
Trace attributes remain mutable
When a consumer modifies trace.events[n].attributes or one of its nested collections, RunTrace.read() has exposed the parser's mutable dictionary unchanged, causing the contents of the advertised immutable aggregate to change after it was read.
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/dream/observability/_run_trace.py
Line: 27
Comment:
**Trace attributes remain mutable**
When a consumer modifies `trace.events[n].attributes` or one of its nested collections, `RunTrace.read()` has exposed the parser's mutable dictionary unchanged, causing the contents of the advertised immutable aggregate to change after it was read.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.There was a problem hiding this comment.
Fixed in 9df8677. Trace attributes are recursively frozen on read and covered by a nested-attribute regression test.
| tool_use_id=_require_str(data, "tool_use_id"), | ||
| tool_name=_require_str(data, "tool_name"), | ||
| input=json_dict_from_mapping(input_raw), | ||
| input=tuple(json_dict_from_mapping(input_raw).items()), |
There was a problem hiding this comment.
Suggestion: Schema version 2 snapshots that contain tool-use records written before this change do not have an input field. Because the schema version remains accepted as 2 but decoding now requires input, those previously resumable snapshots are reported as corrupt instead of being migrated or decoded with an empty input. Bump the schema version and provide an explicit migration for existing version-2 files, or preserve backward-compatible decoding. [api mismatch]
Severity Level: Major ⚠️
- ❌ Existing saved sessions containing tool calls cannot resume after deployment.
- ⚠️ `Harness.resume_session()` reports compatible files as corrupt.
- ⚠️ Users may lose access to persisted transcript state.Prompt for AI Agent 🤖
This is a comment left during a code review.
**Path:** src/dream/services/session_store.py
**Line:** 536:536
**Comment:**
*Api Mismatch: Schema version 2 snapshots that contain tool-use records written before this change do not have an `input` field. Because the schema version remains accepted as 2 but decoding now requires `input`, those previously resumable snapshots are reported as corrupt instead of being migrated or decoded with an empty input. Bump the schema version and provide an explicit migration for existing version-2 files, or preserve backward-compatible decoding.
Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fixThere was a problem hiding this comment.
This is not a schema compatibility regression. The pre-PR schema-v2 writer already serialized the input field for every tool-use and tool-call record, and the pre-PR decoder already required that field through _require_mapping. The PR changes only the in-memory immutable representation; the JSON shape remains unchanged.
There was a problem hiding this comment.
divo12 has reached the 50-credit limit for trial accounts. To continue receiving code reviews, upgrade your plan.
Seal session and trace JSON into public typed immutable values so control-plane readers cannot mutate captured execution state. Co-authored-by: Cursor <cursoragent@cursor.com>
9df8677 to
ef29c3d
Compare
Thanks for using CodeAnt! 🎉We're free for open-source projects. if you're enjoying it, help us grow by sharing. Share on X · |
There was a problem hiding this comment.
divo12 has reached the 50-credit limit for trial accounts. To continue receiving code reviews, upgrade your plan.
User description
Summary
RunTraceaggregate over the existing JSONL event storeSessionSnapshottranscript, tool-call, tool-input, and metadata collections immutableTests
pytest tests/test_runtime_contracts.py tests/test_services/test_session_store.py tests/test_public_api.py(34 passed)mypy src/dreampythonexecutable unavailable in this macOS environmentCodeAnt-AI Description
Expose immutable session snapshots and trace history
What Changed
RunTrace.read()for typed access to a session’s existing trace events, while ignoring truncated final log entries and rejecting events from another sessionImpact
✅ Fewer accidental mutations of captured session state✅ Reliable session-specific trace inspection✅ Safe resume with independent mutable session metadata💡 Usage Guide
Checking Your Pull Request
Every time you make a pull request, our system automatically looks through it. We check for security issues, mistakes in how you're setting up your infrastructure, and common code problems. We do this to make sure your changes are solid and won't cause any trouble later.
Talking to CodeAnt AI
Got a question or need a hand with something in your pull request? You can easily get in touch with CodeAnt AI right here. Just type the following in a comment on your pull request, and replace "Your question here" with whatever you want to ask:
This lets you have a chat with CodeAnt AI about your pull request, making it easier to understand and improve your code.
Example
Preserve Org Learnings with CodeAnt
You can record team preferences so CodeAnt AI applies them in future reviews. Reply directly to the specific CodeAnt AI suggestion (in the same thread) and replace "Your feedback here" with your input:
This helps CodeAnt AI learn and adapt to your team's coding style and standards.
Example
Retrigger review
Ask CodeAnt AI to review the PR again, by typing:
Check Your Repository Health
To analyze the health of your code repository, visit our dashboard at https://app.codeant.ai. This tool helps you identify potential issues and areas for improvement in your codebase, ensuring your repository maintains high standards of code health.