Repository navigation
fix(approvals): roll a standing grant back when its save is refused - #2768
Conversation
|
@Birfy — review request at tip What it fixes. The fix. Red first at base Checks at this tip ( Independent passes (advisory, not the gate). FlyBot Test re-ran the red-first on a clean base (same 30/27/3) and wrote its own runtime probe: 38/38 on the tip vs 24/38 on the base, with the base failing on four shapes including "refused Bot-scope grant answers the next same-kind card Tier T2 ( |
"Always allow" wrote the grant into memory and saved afterwards, and the catch only refused. A save the store refuses left the grant live with nothing on disk: the card stayed pending, but the next matching ask was answered `allowed-once` by a grant the owner never gave. A refused removal had the mirror shape: the settings list said "gone" while the grant still answered. Both now snapshot the list, keep it only once the save lands, put the old reference back on failure and rethrow; the answers a new grant releases stay on the success path, and `revision` is untouched. Fixes ThinkFlowLab#2764
|
Verdict: PASS at 0296414 No actionable findings at this tip. I reviewed it independently of the PR narrative: the base source, the tip source, the new tests, the docs change, the fragment and the generated patch. The defect is real on the base, in the exact shape the issue describes. At base The fix rolls back only the in-memory list and rethrows. The new cases are red on the base for the intended reason. The tip test file dropped onto the base tree (only the source change reverted) runs 30 tests / 27 pass / 3 fail, exactly the three new cases: The generated patch matches the source. Every hunk of the Tier note: the base Behaviour note, not a finding: two Ran:
Not run:
This PASS names only the tip above; a new commit voids it. It is a conclusion, not merge authority: the enqueue guard and |
Fixes #2764
Owner: Dev-3
Reviewer: Birfy
Written-by: Dev-3
Bot: Dev-3
Claim-Id: ae56041a44c844e4b8683da11fdd30b3
Base: f2ef4df
Risk tier: T2
The defect
"Always allow" wrote the grant into memory first (
target.standingApprovals = [...grants, grant]) and saved afterwards, and the failure path only refused the request. When the store refuses the write — the temp name its bytes land through is a link, a hard link or a directory (store.js:1022) — the card stays pending and nothing changes on disk, but the grant stays live in memory: the next matching ask is answeredallowed-oncewithout a card and without the owner ever having accepted anything. The Product probe read it back asgrant in memory after the failed save: ["bash:escalate:danger-full-access"], then a second identical ask answeredallowed-once.removeStandingApprovalhas the mirror shape: it deletes the grant in memory, then saves. A refused save makes the settings list say "gone" while the grant is still on disk and still answers every ask it covers.What changed
core/src/host/approvals.js—grantStandingandremoveStandingApprovalsnapshot the list (const before = target.standingApprovals), keep the new value only onceawait rt.save()lands, put the old reference back in thecatch(adeletewhen there was none) and rethrow. Neither the card shape nor theallowed-onceoutcome moves, andrevisionis never rolled back —save()bumps it. The cards a new grant already covers are answered on the success path only, as before.core/test/group-status.test.js— three tests on the real install, the real handler and a real store: Bot scope, room scope (always-room), and a removal. The refusal is induced the way the Product probe hit it: a directory atstate.json.<pid>.tmp, the temp name the write lands through.docs/subsystems/core-host.md— the standing-approvals paragraph says a refused save changes nothing.changelog.d/2764-refused-save-grant.fixed.md— the user-visible entry.Verification at the tip
Tip SHA:
02964141327f003fc32b57f3b5db189e83f1281bRan:
cd core && DSH_HARNESS=… node --import ./test/harness-resolve.mjs --test --test-concurrency=4 "test/*.test.js"→ 3139 tests / 3137 pass / 2 fail; both failures arecore/test/bench-preflight.test.js(bench.sh refuses before writing the profile when pnpm is missing,bench.sh shows a failed pnpm install and leaves no half-built profile) — the fixture's own false red (mkdir: not found/cat: not found, its PATH drops coreutils), reproduced on the clean base, tracked as its own ticket.cd core && … node --import ./test/harness-resolve.mjs --test test/group-status.test.js→ 30 tests / 30 pass / 0 fail with the change in place.node core/scripts/lint.mjs→ oxlint 0 errors (87 pre-existing warnings),arch-docs: OK.node scripts/ds-bot-patches.mjs check→ patchedcore/equalsds-bot/+patches/ds-bot/(the generated0001-core-fixes.patchis in this diff).node core/scripts/build-client.mjs --checkok,node .github/scripts/changelog.mjs checkok.plugins/social619/619,plugins/tree140/140,plugins/timeline161/161,plugins/workflow508/508 (unchanged by this diff).Not run (and why): root
npm run verifyas one command —npm cicannot reach the registry on this box (404 tarball, and npm cannot write~/.npm/_logs), so its steps were run individually against a harness install of another worktree.Red first
On the clean base with only the three tests added (the source change reverted),
node --test test/group-status.test.jsfails exactly those three, twice in a row, with the same shape:Neither
if (test)nor a relaxed assertion is involved: the tests drive the realapproval/requestlistener and the realapproval-answer/standing-approval-removeendpoints over a real store, and answer the waiting cards so no promise is left open.Checkpoint
{ "pr": 2768, "claim": "Dev-3", "issue": 2764, "summary": "An Always allow was written into memory before its save, and a refused save only refused the request: the card stayed pending while the next matching ask was answered allowed-once by a grant the owner never gave. A refused removal had the mirror shape, the settings list saying gone while the grant still answered. Both now snapshot the list, keep the new value only once the save lands, put the old reference back and rethrow; revision is untouched and the answers to other cards stay on the success path. Three tests cover Bot scope, room scope and removal on a real store.", "files": ["core/src/host/approvals.js", "core/test/group-status.test.js", "docs/subsystems/core-host.md", "changelog.d/2764-refused-save-grant.fixed.md", "patches/ds-bot/0001-core-fixes.patch"], "checks": { "ran": "cd core && node --import ./test/harness-resolve.mjs --test test/group-status.test.js", "result": "green", "at": "02964141327f003fc32b57f3b5db189e83f1281b" }, "next": "none — ready for review" }