MEDIUM declined as out of scope from the fresh-context review of PR #3041 (issue #3017), recorded as
its own slice. Source-traced from the reviewed diff, not runtime-reproduced. Non-blocking: the server
refused the write, the message shown is truthful about what happened, the draft survives
(draftLocked deliberately excludes permissionLost), Clear and Cancel work, and reopening the card
clears the lock.
Gap
CardAssignmentField's readOnly watcher treats a false transition as the authoritative evidence
that write permission was re-read and now allows writing, so it clears saveFailure and releases the
#2982 lock. After #3041 a save settles on its own generation counter, so a 403 that answers after
that transition is no longer discarded — it is applied.
Sequence:
- Save starts; the PUT is in flight.
- A background board refetch reports
canWrite: false, then a later one reports canWrite: true.
The watcher fires on the second, clears saveFailure and reloads participants.
- The PUT — refused during the downgrade window — answers 403. Nothing bumped
saveGeneration, so
saveFailure = 'permission' is applied and the sticky lock goes back on.
- The copy says the selector and Save "stay locked until this board reports write permission again
or you reopen the card". The board already reported it, at step 2. No further readOnly
transition is coming, so the only remaining release is reopening the card.
Before #3041 this 403 was swallowed entirely by the intervening load() — but that same swallowing
was #3017: it latched saving true and left the editor un-closable, which is strictly worse.
Applying the refusal is the right half of the trade; the release path is the half left open.
Why this belongs with #3021
#3021 already owns "drive the post-403 lock and its explanation from an authoritative permission
read" rather than from a readOnly edge. An authoritative read at the moment the 403 lands resolves
this case directly: the board says writable, so the lock either releases in place or is replaced by a
permission answer that is actually current. This issue is the concrete ordering that makes the
edge-triggered release provably insufficient, not a separate mechanism.
Acceptance
Related: #3017 / PR #3041 (which introduced this ordering), #3021, #3037.
MEDIUM declined as out of scope from the fresh-context review of PR #3041 (issue #3017), recorded as
its own slice. Source-traced from the reviewed diff, not runtime-reproduced. Non-blocking: the server
refused the write, the message shown is truthful about what happened, the draft survives
(
draftLockeddeliberately excludespermissionLost), Clear and Cancel work, and reopening the cardclears the lock.
Gap
CardAssignmentField'sreadOnlywatcher treats a false transition as the authoritative evidencethat write permission was re-read and now allows writing, so it clears
saveFailureand releases the#2982 lock. After #3041 a save settles on its own generation counter, so a 403 that answers after
that transition is no longer discarded — it is applied.
Sequence:
canWrite: false, then a later one reportscanWrite: true.The watcher fires on the second, clears
saveFailureand reloads participants.saveGeneration, sosaveFailure = 'permission'is applied and the sticky lock goes back on.or you reopen the card". The board already reported it, at step 2. No further
readOnlytransition is coming, so the only remaining release is reopening the card.
Before #3041 this 403 was swallowed entirely by the intervening
load()— but that same swallowingwas #3017: it latched
savingtrue and left the editor un-closable, which is strictly worse.Applying the refusal is the right half of the trade; the release path is the half left open.
Why this belongs with #3021
#3021 already owns "drive the post-403 lock and its explanation from an authoritative permission
read" rather than from a
readOnlyedge. An authoritative read at the moment the 403 lands resolvesthis case directly: the board says writable, so the lock either releases in place or is replaced by a
permission answer that is actually current. This issue is the concrete ordering that makes the
edge-triggered release provably insufficient, not a separate mechanism.
Acceptance
readOnlyfalse transition that has already completed, thefield must not sit on a lock whose stated release path has already passed. Either re-verify
permission (preferred — [Frontend][Assignments] Drive the post-403 lock and its explanation from an authoritative permission read (quiet-board restore, revocation vs downgrade) (from PR #3011 Codex review) #3021's shape) or offer an in-place release affordance.
end in a state whose only exit is a remount.
Related: #3017 / PR #3041 (which introduced this ordering), #3021, #3037.