Add Timestamp int64 field to all NATS event structs#59
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (19)
✅ Files skipped from review due to trivial changes (5)
🚧 Files skipped from review as they are similar to previous changes (7)
📝 WalkthroughWalkthroughThis change adds a top-level Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Establishes the convention that every NATS event struct gets a Timestamp int64 field (Unix millis) set at publish time, plus a CLAUDE.md rule to enforce the pattern for future events. https://claude.ai/code/session_01Qy6cCrGnkHQpzkjbEF8aAv
10-task TDD plan covering all 8 event structs, 9 publish sites, CLAUDE.md rule, and full test updates. https://claude.ai/code/session_01Qy6cCrGnkHQpzkjbEF8aAv
Unmarshal the invite request, stamp it with the current UTC millisecond timestamp, re-marshal, and publish the timestamped payload to the ROOMS stream so downstream workers receive an authoritative event time. https://claude.ai/code/session_01Qy6cCrGnkHQpzkjbEF8aAv
025ba0e to
e67c9da
Compare
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
pkg/roomkeysender/roomkeysender_test.go (1)
39-81: Add a nil-event test case forSend.Since
Sendaccepts*model.RoomKeyEvent, please add a table case forevt == nilto lock in error behavior and prevent panic regressions.Also applies to: 107-107
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@pkg/roomkeysender/roomkeysender_test.go` around lines 39 - 81, Add a table-driven test case where evt is nil to the tests slice used by TestSend (and the other tests slice at the later occurrence) to assert Send handles a nil *model.RoomKeyEvent without panicking; in the tests in pkg/roomkeysender/roomkeysender_test.go add an entry with evt: nil, account set (e.g., "alice" or blank), publishErr nil, and set wantErr to expect an error (or an error substring like "room key" or "nil")—then ensure the test checks for a non-nil error/substring match from Send rather than allowing a panic.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@pkg/model/model_test.go`:
- Around line 306-317: The test TestNotificationEventJSON fails to compile
because roundTrip uses a [T comparable] constraint while model.NotificationEvent
embeds model.Message with time.Time (non-comparable); replace the roundTrip
usage with an explicit JSON marshal/unmarshal flow: json.Marshal the src
NotificationEvent, json.Unmarshal into a fresh model.NotificationEvent variable,
and compare src vs the unmarshaled value using reflect.DeepEqual (or cmp.Equal)
asserting equality; reference the TestNotificationEventJSON, roundTrip,
model.NotificationEvent, model.Message, json.Marshal/json.Unmarshal, and
reflect.DeepEqual in your change.
In `@pkg/roomkeysender/roomkeysender.go`:
- Around line 28-30: The Send method currently dereferences evt to set
evt.Timestamp and will panic if evt is nil; update Sender.Send to first guard
against a nil evt (e.g., if evt == nil) and return a descriptive error before
touching evt.Timestamp or calling json.Marshal, referencing the Send method and
the RoomKeyEvent/evt variable to locate where to add the check.
---
Nitpick comments:
In `@pkg/roomkeysender/roomkeysender_test.go`:
- Around line 39-81: Add a table-driven test case where evt is nil to the tests
slice used by TestSend (and the other tests slice at the later occurrence) to
assert Send handles a nil *model.RoomKeyEvent without panicking; in the tests in
pkg/roomkeysender/roomkeysender_test.go add an entry with evt: nil, account set
(e.g., "alice" or blank), publishErr nil, and set wantErr to expect an error (or
an error substring like "room key" or "nil")—then ensure the test checks for a
non-nil error/substring match from Send rather than allowing a panic.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 4f3d3f4d-97e2-4985-a09d-15e8b3f18a1c
📒 Files selected for processing (19)
CLAUDE.mdbroadcast-worker/handler.gobroadcast-worker/handler_test.godocs/superpowers/plans/2026-04-08-nats-event-timestamps.mddocs/superpowers/specs/2026-04-08-nats-event-timestamps-design.mdinbox-worker/handler.goinbox-worker/handler_test.gomessage-gatekeeper/handler.gomessage-gatekeeper/handler_test.gonotification-worker/handler.gonotification-worker/handler_test.gopkg/model/event.gopkg/model/model_test.gopkg/roomkeysender/roomkeysender.gopkg/roomkeysender/roomkeysender_test.goroom-service/handler.goroom-service/handler_test.goroom-worker/handler.goroom-worker/handler_test.go
| func TestNotificationEventJSON(t *testing.T) { | ||
| src := model.NotificationEvent{ | ||
| Type: "new_message", | ||
| RoomID: "room-1", | ||
| Message: model.Message{ | ||
| ID: "m1", RoomID: "room-1", UserID: "u1", UserAccount: "alice", | ||
| Content: "hello", CreatedAt: time.Date(2026, 1, 1, 12, 0, 0, 0, time.UTC), | ||
| }, | ||
| Timestamp: 1735689600000, | ||
| } | ||
| roundTrip(t, &src, &model.NotificationEvent{}) | ||
| } |
There was a problem hiding this comment.
roundTrip helper will not compile with NotificationEvent.
The roundTrip helper uses a [T comparable] constraint, but NotificationEvent contains an embedded Message struct with time.Time fields, making it non-comparable in Go. This test will fail to compile.
🐛 Proposed fix: use explicit marshal/unmarshal with reflect.DeepEqual
func TestNotificationEventJSON(t *testing.T) {
src := model.NotificationEvent{
Type: "new_message",
RoomID: "room-1",
Message: model.Message{
ID: "m1", RoomID: "room-1", UserID: "u1", UserAccount: "alice",
Content: "hello", CreatedAt: time.Date(2026, 1, 1, 12, 0, 0, 0, time.UTC),
},
Timestamp: 1735689600000,
}
- roundTrip(t, &src, &model.NotificationEvent{})
+ data, err := json.Marshal(&src)
+ require.NoError(t, err)
+ var dst model.NotificationEvent
+ require.NoError(t, json.Unmarshal(data, &dst))
+ if !reflect.DeepEqual(src, dst) {
+ t.Errorf("round-trip mismatch:\n got %+v\n want %+v", dst, src)
+ }
}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@pkg/model/model_test.go` around lines 306 - 317, The test
TestNotificationEventJSON fails to compile because roundTrip uses a [T
comparable] constraint while model.NotificationEvent embeds model.Message with
time.Time (non-comparable); replace the roundTrip usage with an explicit JSON
marshal/unmarshal flow: json.Marshal the src NotificationEvent, json.Unmarshal
into a fresh model.NotificationEvent variable, and compare src vs the
unmarshaled value using reflect.DeepEqual (or cmp.Equal) asserting equality;
reference the TestNotificationEventJSON, roundTrip, model.NotificationEvent,
model.Message, json.Marshal/json.Unmarshal, and reflect.DeepEqual in your
change.
There was a problem hiding this comment.
False positive — NotificationEvent is comparable in Go. time.Time is a struct containing only primitive fields and a pointer (wall uint64, ext int64, loc *Location), which satisfies the comparable constraint. The test compiles and passes:
$ go test -run TestNotificationEventJSON -v ./pkg/model/...
=== RUN TestNotificationEventJSON
--- PASS: TestNotificationEventJSON (0.00s)
PASS
No changes needed.
Generated by Claude Code
There was a problem hiding this comment.
@Joey0538, you are correct — I apologize for the false positive. In Go, time.Time satisfies comparable because all its underlying fields (wall uint64, ext int64, loc *Location) support ==, and pointer types are comparable by address. The comparable constraint only requires that == is syntactically valid, not deep structural equality. The test compiles and passes as you demonstrated, so no change is needed.
✏️ Learnings added
Learnt from: Joey0538
Repo: hmchangw/chat PR: 59
File: pkg/model/model_test.go:306-317
Timestamp: 2026-04-08T08:25:48.132Z
Learning: In Go, `time.Time` satisfies the `comparable` constraint (used in generic type parameters) because all of its fields — `wall uint64`, `ext int64`, `loc *Location` — support `==`. Pointer types are comparable by address. Therefore, any struct embedding or containing `time.Time` fields (including `model.Message` and structs embedding it, such as `model.NotificationEvent`) is also `comparable`. Do not flag `roundTrip[T comparable]` usage with types containing `time.Time` as a compilation error.
Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.
🧠 Learnings used
Learnt from: CR
Repo: hmchangw/chat PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-08T01:57:58.612Z
Learning: Applies to pkg/model/model_test.go : `pkg/model/model_test.go` verifies all domain types marshal/unmarshal correctly via a generic `roundTrip` helper
Learnt from: mliu33
Repo: hmchangw/chat PR: 55
File: pkg/model/room.go:27-32
Timestamp: 2026-04-08T01:06:38.100Z
Learning: When reviewing Go code under pkg/model/, only require both `json` and `bson` tags for MongoDB document structs (the types that are persisted to MongoDB, e.g., `Room`, `Subscription`, `Message`, `User`). For NATS request/response DTOs (e.g., `CreateRoomRequest`, `InviteMemberRequest`) that are not written to MongoDB, it is intentional to have only `json` tags—do not flag missing `bson` tags on these DTOs as a violation.
Learnt from: mliu33
Repo: hmchangw/chat PR: 55
File: pkg/model/event.go:25-29
Timestamp: 2026-04-08T01:06:33.467Z
Learning: In `pkg/model/event.go` (Go), `InviteMemberRequest` is a NATS-only request DTO that is never persisted to MongoDB. The dual `json`+`bson` tag convention (required for MongoDB document structs in `pkg/model/`) does not apply to it. Do not flag missing `bson` tags on `InviteMemberRequest` as an issue.
Learnt from: Joey0538
Repo: hmchangw/chat PR: 52
File: docs/superpowers/specs/2026-04-07-search-sync-worker-design.md:82-82
Timestamp: 2026-04-07T06:13:53.100Z
Learning: In the `hmchangw/chat` repository, the `UpdatedAt time.Time` field (with `json:"updatedAt"` tag) is planned to be added to the `Message` struct in `pkg/model/message.go` as part of implementation step 1 (model changes), as specified in the design doc `docs/superpowers/specs/2026-04-07-search-sync-worker-design.md`. Any constructors, serializers, or helpers that build or handle `Message` should also be updated to populate/handle `UpdatedAt`.
Learnt from: CR
Repo: hmchangw/chat PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-08T01:57:58.612Z
Learning: Applies to pkg/model/**/*.go : `camelCase` for both `json` and `bson` tags, except `_id`
Learnt from: CR
Repo: hmchangw/chat PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-08T01:57:58.612Z
Learning: Applies to pkg/model/**/*.go : All model structs get both `json` and `bson` tags
Summary
Timestamp int64(Unix millis) field to all 8 NATS event structs inpkg/model/event.gofor consistent event-level timestampsTimestamp: time.Now().UTC().UnixMilli()at publish timeRoomEvent.Timestampfromtime.Timetoint64for consistency with the new conventionCLAUDE.mdto enforce the pattern for future eventshugeParamlint warnings ininbox-worker/handler.goMotivation
NATS events had inconsistent timestamp coverage — some carried timestamps indirectly via embedded structs, some had explicit
time.Timefields, and three had none at all. This makes event ordering, debugging, and observability harder. Every event now has a first-classTimestampfield distinct from domain-level timestamps (e.g.,Message.CreatedAt).Test plan
TimestampassertionsNotificationEvent,SubscriptionUpdateEvent,InviteMemberRequest,OutboxEvent,RoomMetadataUpdateEventmake lint— 0 issuesmake test— all passhttps://claude.ai/code/session_01Qy6cCrGnkHQpzkjbEF8aAv
Summary by CodeRabbit
New Features
Documentation
Tests