Skip to content

fix(state): never emit created_at on the wire - #146

Merged
guangyu-reflexio merged 1 commit into
mainfrom
fix/exclude-created-at-from-wire
Jul 27, 2026
Merged

fix(state): never emit created_at on the wire#146
guangyu-reflexio merged 1 commit into
mainfrom
fix/exclude-created-at-from-wire

Conversation

@guangyu-reflexio

@guangyu-reflexio guangyu-reflexio commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Follow-up to #144, from a CodeRabbit Major on that PR after it merged.

#144 removed the ts -> created_at carry but left created_at in the allowlist the serializer filters against, so a buffer record containing that key literally would still pass straight through. Verified on merged main:

wire turn: {'role':'User','content':'x','created_at':999}  -> leaks? True

Nothing writes that key today, so it was latent rather than live — but closing exactly this kind of door is what the allowlist is for, and the existing test only supplied ts, so it could not catch it.

The fix

Splits two concepts that were conflated:

  • _INTERACTION_DATA_FIELDS stays the model-contract set. created_at genuinely is an InteractionData field, and the drift test pins this set against the real model, so it must remain here.
  • _WIRE_FIELDS is what the serializer filters on, and excludes it.

Why created_at must never be emitted

The extractor's bookmark is keyed on interaction created_at (last_processed_timestamp, compared with created_at >= ?). A backdated batch — one recovered after the bookmark has moved — is stored and then never seen by the extractor. That was reproduced end to end as permanent, silent loss of learning data, on precisely the offline-recovery path this buffer exists to protect. Letting the server stamp its own time is the lesser evil until ingest ordering stops depending on caller-supplied event time.

Tests: one asserting a literal created_at in the buffer is not emitted, one pinning _WIRE_FIELDS < _INTERACTION_DATA_FIELDS so the two sets cannot silently converge again. 457 tests pass.

Summary by CodeRabbit

  • Bug Fixes

    • Prevented internal creation timestamps from being included in published interaction events.
    • Ensured outbound event data follows the intended payload contract, even when timestamps are present in source records.
  • Tests

    • Added coverage verifying that creation timestamps are consistently excluded from transmitted event data.

#144 removed the ts -> created_at carry but left created_at in the
allowlist the serializer filters against, so a buffer record containing
that key literally would still pass straight through. Nothing writes it
today, so this was latent rather than live — but closing exactly this kind
of door is what the allowlist is for, and the test only supplied ts so it
could not catch it.

Splits the two concepts: _INTERACTION_DATA_FIELDS stays the model-contract
set (created_at IS a real InteractionData field, and the drift test pins
the set against the model), while _WIRE_FIELDS is what the serializer
filters on and excludes it.

Why created_at must not be emitted: the extractor's bookmark is keyed on
interaction created_at (last_processed_timestamp, compared with
created_at >= ?), so a backdated batch — one recovered after the bookmark
moved — is stored and then never extracted. That is permanent, silent loss
of learning data on precisely the offline-recovery path this buffer exists
for. Letting the server stamp its own time is the lesser evil until ingest
ordering stops depending on caller-supplied event time.

Caught by CodeRabbit on #144 after it merged.
@coderabbitai

coderabbitai Bot commented Jul 27, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a7ca8dcb-3423-4f20-816c-dab231082669

📥 Commits

Reviewing files that changed from the base of the PR and between ee602f1 and 9df5e55.

📒 Files selected for processing (2)
  • plugin/src/claude_smart/state.py
  • tests/test_state.py

📝 Walkthrough

Walkthrough

The outbound turn serializer now uses a wire-specific allowlist that excludes created_at. Tests verify omission from emitted payloads while retaining created_at in the broader interaction-data contract.

Changes

Wire payload contract

Layer / File(s) Summary
Wire field filtering and validation
plugin/src/claude_smart/state.py, tests/test_state.py
Adds _WIRE_FIELDS, applies it to unpublished turn serialization, and tests that created_at is excluded from wire payloads but retained in the interaction-data field set.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related PRs

Suggested reviewers: yyiilluu

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: preventing created_at from being emitted in state wire serialization.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/exclude-created-at-from-wire

Comment @coderabbitai help to get the list of available commands.

@guangyu-reflexio
guangyu-reflexio merged commit fdbefb2 into main Jul 27, 2026
9 checks passed
@guangyu-reflexio
guangyu-reflexio deleted the fix/exclude-created-at-from-wire branch July 27, 2026 22:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant