docs(plans): the sweep completes a stale activating row that already holds a code - #1515
Conversation
…holds a code (Vera) Unconditional demotion of a stale activating row could strand a live, redeemable connect code under an error parent (crash between the mint and the final parent write). The sweep now looks before it demotes: an activating row whose Integration is already active is completed to active with write 3, fenced on the claimId it read; only an inactive Integration row is demoted. The sweep never mints and never unprojects. Test 6 pins both branches. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
lilyshen0722
left a comment
There was a problem hiding this comment.
SR-GATE: APPROVED @ 2a96f42b — docs-only, one file, all checks passing.
The change is correct and the doc stays internally consistent under it. What I checked at this head:
The new sweep branch cannot double-mint. The completion path is write 3 only, and write 2 is already fenced on isActive: false with an explicit null contract (:157-159: "null here means the row is already active … read the existing code, mint nothing"). So both routes into a stale activating row whose Integration is live — the lease takeover at :154 and this new sweep branch — end at active with mintConnectCode called exactly once. Test 6d(ii) already pins the takeover route; the addition to test 6 pins the sweep route. Complementary, not overlapping.
No stale prose left behind. I grepped every activating mention (14 of them). :95 still lists ['installing', 'activating'] — that is the lease takeover claim filter, not the sweep, so it is unaffected by narrowing the sweep's demote clause to installing. :117's 202-while-activating and :165's "only active ever returns 200 with a code" both survive: completing a row makes it active, which is precisely when 200-with-code becomes correct.
The stranding it fixes is real — under the merged text a sweep firing between writes 2 and 3 demoted a parent whose Integration already carried a live, redeemable code, and handleEnableCommand looks up { type, isActive: true, 'config.connectCode' } without consulting the installation, so that code stayed redeemable under an error parent.
Non-blocking: the sweep's null behaviour is not stated
:304 says the completion write is "fenced on the claimId it read" but does not say what the sweep does when that write returns null (a takeover landed in between). The general rule exists at :149-150 — "The same rule holds for every fenced write in step 4: on null, stop" — but it is scoped by its own sentence to step 4, the owner's writes, and the sweep is §3. A reader implementing §3 has no sentence telling them the sweep must also do nothing on null, which is exactly the "loser tidies up" failure the doc warns about elsewhere. One clause fixes it: "on null the sweep does nothing — the row belongs to whoever holds the generation now."
Why
Vera's read of the merged plan (pod message 62748, 2026-09-03): the reconciler demoted a stale
activatingrow toerrorunconditionally, but write 2 may already have minted — so the sweep could strand a live, redeemable connect code under anerrorparent.What
docs/plans/connector-as-installable-app.md§3 reconciler + §8 test 6: a staleactivatingrow whose Integration is already active is completed toactive(write 3, fenced on theclaimIdthe sweep read); only an inactive Integration row is demoted. The sweep never mints and never unprojects.Proof
Docs only. Follow-up to #1509.
🤖 Generated with Claude Code