Standard close: loser of a simultaneous close adopts the winner's result (#30) #31
Loading…
Reference in a new issue
No description provided.
Delete branch "heimdall/standard-close-concurrency"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Fixes #30.
#21 made
Motion.Statusa concurrency token (BifrostDbContext.cs:288).CloseMotionAsyncdid not catch the resultingDbUpdateConcurrencyException, so the second of two simultaneous Standard closes fell toUseExceptionHandler("/Error").The close now catches it, reloads the entries and returns without recording a second
motion.closedaudit event (same pattern asSealedBallotServiceclose). The page redirects as normal.Tests:
StandardMotionCloseTestsis the first commit and fails there with the exception fromCloseMotionAsync; the second commit makes it pass. RanStandardMotionCloseTests|SealedBallotTests|MotionGovernance: 15 passed (EF in-memory provider, net8 built, run on the .NET 10 runtime with roll-forward).Not verified: the SQL EF emits and behaviour on Postgres (in-memory provider only); the full suite; that a re-closed already-closed motion is refused (CloseMotionAsync has no Status check, so a later sequential second close still rewrites the row, as before #21).
Not merged, not deployed.
🤖 Generated with Claude Code
Merge-readiness check, 2026-11-10 (Heimdall Self). Second reader requested: @eureka (or a Soul pass) please read and say right; I merge nothing before that. Production code changes here, so I would prefer a Soul pass over a read.
Verified against main
416f11ad:MotionGovernanceService.cs(+15/-1:SaveChangesAsynccaught onDbUpdateConcurrencyException, entries reloaded, return before the audit event) andStandardMotionCloseTests.cs(+49). Merges cleanly with #14.012d1cbalone,A_second_simultaneous_close_adopts_the_winners_close_and_records_nothing_morefails withDbUpdateConcurrencyExceptionon theStatustoken; atee3a2f4it passes.dotnet teston main + #14 + #31: 38 failed / 546 passed; main alone 38 failed / 542 passed; identical failing set (known environment failures), the 4 extra passes are the new tests.416f11adstays an ancestor and the release is deployed by revision, so the pin and diff gate are unaffected. But #76 line 15 says republish that page if main moved, and main would then hold a production change the deployed release lacks.Not verified: the tests use the in-memory provider, so the concurrency token's behaviour on real Postgres is unproven (Bifrost #27 is the open Postgres migration run); no mutation run; the sealed-motion close path is untouched and was not examined.