Skip to content

refactor(sdk): cleanup tests, rename model.go, fix history compactor race#43

Merged
lIang70 merged 4 commits into
mainfrom
refactor/sdk-test-cleanup
Apr 27, 2026
Merged

refactor(sdk): cleanup tests, rename model.go, fix history compactor race#43
lIang70 merged 4 commits into
mainfrom
refactor/sdk-test-cleanup

Conversation

@lIang70

@lIang70 lIang70 commented Apr 27, 2026

Copy link
Copy Markdown
Collaborator

Summary

Three small changes inside sdk/:

  • sdk/model: rename model.go / model_test.go to message.go / message_test.go. The file's content is the Message type, so the new filename matches its primary symbol. Pure rename, no API change.
  • sdk/recall & sdk/history: move TestAllCategories, TestRegisterCategory and TestAllCategoryStrings out of sdk/history/history_test.go into a new sdk/recall/categories_test.go (package recall_test). These tests only exercise recall.Category APIs and were the sole reason history had a (test-only) import of recall. After the move, history no longer imports recall at all, removing a virtual cross-package dependency. Also drops a dead code block in the original test that built an expected map and discarded its values without asserting.
  • sdk/history: fix a real data race the CI race detector caught on TestCoordinator_ArchiveRunsThroughQueue. convQueue.recovered was touched from two unrelated goroutines without a shared mutex (lazyRecover ran on the per-conversation worker without compactor.mu; kickoffStartupRecovery wrote the same field while holding compactor.mu). Switched the field to atomic.Bool and used CompareAndSwap inside lazyRecover so "already recovered?" and "mark recovered" become a single atomic step.

Auto-tag

The previous version of this PR carried [skip-tag:sdk] because it was a pure test relocation + filename rename. The newly added race fix is a production-code change in sdk/history/compactor.go, so the marker has been removed and sdk should receive a normal patch-version bump on merge.

Test plan

  • go test ./sdk/history/... ./sdk/recall/... passes locally
  • go test -race ./sdk/... passes locally (full sdk module, including the previously-flaky TestCoordinator_ArchiveRunsThroughQueue)
  • go test -race -run TestCoordinator_ArchiveRunsThroughQueue -count=200 ./sdk/history/ passes
  • rg 'flowcraft/sdk/recall' sdk/history returns no matches (history no longer imports recall)
  • CI green on this PR

lIang70 added 3 commits April 27, 2026 11:00
The file's content is the Message type (not generic "model"); aligning
the filename with its primary symbol makes navigation more intuitive.
Pure rename, no API change.

Made-with: Cursor
TestAllCategories, TestRegisterCategory and TestAllCategoryStrings
exclusively exercise recall.Category APIs but were living in
sdk/history/history_test.go, which forced an otherwise unnecessary
history -> recall test-only dependency. Move them to a new
sdk/recall/categories_test.go (package recall_test, matching the
other black-box tests in the package) and drop the history import.

Also drops a dead code block in the original TestAllCategories that
built an "expected" map and only assigned its values to _ without
asserting on them.

Made-with: Cursor
The convQueue.recovered flag was read/written from two paths without a
shared mutex:

  - lazyRecover, called from the per-conversation worker goroutine,
    did an unlocked read+write on q.recovered.
  - kickoffStartupRecovery's pre-marking pass wrote
    q.recovered = true while holding compactor.mu.

CI's race detector caught this on TestCoordinator_ArchiveRunsThroughQueue
(sdk/history/compactor.go:420 vs :562). Switch the field to atomic.Bool
and use CompareAndSwap inside lazyRecover so the "already recovered"
short-circuit and the "mark recovered" step are a single atomic step.
The startup pre-marking path uses Store(true), which interleaves
correctly with the worker's CAS regardless of which one observes the
queue first.

Made-with: Cursor
@lIang70 lIang70 changed the title refactor(sdk): rename model.go and relocate recall category tests [skip-tag:sdk] refactor(sdk): cleanup tests, rename model.go, fix history compactor race Apr 27, 2026
CI's race detector (Go 1.26) caught a wg.Add / wg.Wait ordering
violation on TestEventReloader_CloseStopsRun. The test launches Run on
one goroutine and immediately calls Close on another:

    go func() { _ = r.Run(ctx); close(done) }()
    if err := r.Close(); err != nil { ... }

The previous implementation called wg.Add(1) inside Run, so when the
scheduler let Close win the race, Close's wg.Wait observed counter=0
and returned without ever waiting — silently violating Close's
contract that "after Close returns, no further Rebuild / timer flush
can fire" because the late-starting Run could still enqueue and run
flushes after the notifier was closed. sync.WaitGroup also explicitly
forbids wg.Add to race with wg.Wait when the counter starts at zero.

Move wg.Add(1) into NewEventReloader so it is strictly happens-before
any Close-side wg.Wait. The Add is conditional on target/notifier
being non-nil so the documented "nil → no-op Run/Close" path keeps
the counter at zero and Close stays a no-op without deadlocking. Run
is documented as at-most-once, matching how it has always been used
in this package.

Made-with: Cursor
@lIang70
lIang70 merged commit e5a6d47 into main Apr 27, 2026
9 checks passed
@lIang70
lIang70 deleted the refactor/sdk-test-cleanup branch April 27, 2026 03:28
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