Files
sure/test/migrations
buzzromainandClaude Opus 5 5594f8bc94 fix(holdings): an outbound transfer must not clear a cost basis (#3237)
* fix(holdings): an outbound transfer must not clear a cost basis

Found by Codex on #3154, reported after it merged.

The migration cleared any calculated basis on a position that had ever been
part of a Transfer, in either direction. The runtime does not: both
`Holding#calculate_avg_cost` and the calculators drop `qty <= 0` rows before
they look at the label, so only a transfer IN makes a position unknowable.

Shares sent elsewhere say nothing about what the remaining ones cost. That
figure came from real purchases and is correct, and the migration destroyed it
— irreversibly, on a position the app still stands behind.

The `EXISTS` clause gains the same `qty > 0` the runtime applies, and a test
holds both directions down: a transfer in clears, a transfer out does not, and
each asserts what the computed path says before running the migration so the
two cannot drift apart again. On the old SQL the outbound case fails.

Installs that already ran the previous version cannot have those figures
restored. A connected account recomputes its basis when it next materializes;
a manual or disconnected one will not, which is the case this migration was
written for in the first place.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016GTNba5qE5NwzaHzbp27ye

* test(holdings): reach the runtime through its public door

Review on #3237. The test asserted the runtime precondition by sending to
Holding's private calculate_avg_cost, which ties migration coverage to a
model's internals — the thing the repo's own guidance says not to do.

`avg_cost` answers the same question in public; it only returned the stored
figure first because this test had stored one. It now leaves the basis empty
until after the precondition, so the read path falls through to the
computation on its own, and each case stores the figure the migration is then
asked to judge.

The precondition stays rather than moving to the holding suite, because it is
the point: the migration and the runtime have to agree about which positions
are unknowable, and the bug was that they did not. A test on either side alone
would not fail if they drifted apart again.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016GTNba5qE5NwzaHzbp27ye

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-08-28 08:57:50 +02:00
..