chadtmiller.com/knotless
git that lives in your Atmosphere account
Close and reopen refuse what no reader would count
Mergedset_state now reads the item first, then refuses a missing item, a caller allowed would not count, a merged pull, and a state the item is already in. The state is read as merge reads it, plus the caller's own newest state records. Docs, the changeset and the tests are updated, and I found nothing wrong in the logic.
- Order of checks:
org::needs_publicruns beforeallowed, so a hidden org member gets the org message and not the generic one. That matches the PR body. - Moved repositories: the old owner's close after a transfer is refused, because
allowedis givenkeys::tid()for now. That is the intended behaviour, and the changed test expects it. - Not checked: I didn't build or run anything. I also didn't confirm that every existing caller that closes or reopens twice in a row still passes. The one I read,
knotless-run/tests/run.rs, alternates close and reopen, so it is fine. CI will show the rest. - Conflict with the "already closed" refusal: a script that retries a close after a timeout now gets an error. You chose that on purpose and documented it, so it is acceptable.
The change since my approval only touches tests/oauth.rs. The test's pull close call is gone because close now reads the item from an appview, and that net has none. The test writes the state record directly instead, so it still checks that the OAuth scope allows dev.knotless.state and refuses app.bsky.feed.post. My earlier approval stands. I didn't build or run anything.
8 passed, 2 skipped
What changes
pull close,pull reopen,issue closeandissue reopennow read the item before writing anything. The same goes for their MCP tools and the pages' close button, since all three go throughforge::set_state. Each of these is now refused, with nothing written:no pull or issue … on …, the same onecomment addalready gives.alloweddecides, the same rule readers apply. The error isonly <item>'s author, and <repo>'s owner and collaborators, may close it. A hidden member of an organization getsorg::needs_public's message instead, as merge, label and queue already give. The item's author needs no membership.pull revert.<item> is closed already.The item's state is read as merge reads it: the appview's entries, with the newest state records of the owner, the collaborators and the lander read from their repos (
fresh_entries), plus the caller's own. Without the caller's own, closing and reopening at once would read the item still open and refuse the reopen.Why
Before this,
set_statewrote a state record for whatever URI it was given and answeredclosed. Readers then ignored it. While triaging issues, anissue closeon a URI with the wrong author DID reported success and left a state record in the closer's account pointing at nothing. A stranger's reopen did the same, and so did a reopen of a merged pull. That last one is worse: the owner'sopenreplacedmergedand cleared the merge.This settles three items of Pulls and states: small correctness gaps:
set_statewriting without checking permission,reopenworking on a merged pull, and the missing-item case added to it.I chose to refuse a state the item is in already, rather than answer success without writing, so that a script learns its view of the item was out of date.
How it was checked
crates/knotless-serve/tests/forge.rs:issues_and_who_may_close_themnow expects four things:dev.knotless.staterecord, and the author's holds one.forks_pulls_reviews_and_mergesexpects a reopen of the merged pull to be refused.moving_to_another_accountexpected the old owner's close after the move to pass and count for nothing. It now expects it to be refused.crates/knotless/tests/oauth.rs:an_oauth_login_writes_only_what_knotless_keepsusedpull closeto show that the OAuth scope lets a state record through. Its net has no appview, so close can no longer run there. The test now writes the state record itself, as close would.Nothing was built or run here. The first CI run passed every test binary but
oauth (test), which the second commit fixes; the rest is left to CI.Shortcomings
KNOTLESS_APPVIEWset empty they fail, where they used to write blind.listRecords, as merge does, rather than one write.