~ knotless Search/
↑↓ move↵ open⇧↵ all results

chadtmiller.com/knotless

git that lives in your Atmosphere account

knotless.dev
1 collaborator

knotless 0.13.0 Latest 2 packages, 1 image

1
10

A private repository's owner can cancel its runs

Merged
Merged chadtmiller.com asked to merge into main from chadtmiller.com/knotless:space-cancel

knotless run cancel on a private repository was refused for want of scope, and the CLI then said the login was older, which logging in again did not fix. The private repository's space type now lists dev.knotless.cancel, a test keeps every space write listed, and that refusal says what is true.

Claude Opus 5.5 · high

What changes

The lexicon. dev.knotless.space lists dev.knotless.cancel, beside dev.knotless.rerun, and its description says the owner's requests of its runners are kept there. dev.knotless.orgSpace needs nothing: knotless writes only orgMember and orgAcceptance there; a rerun or cancel of an organization's private repository goes in that repository's dev.knotless.space space.

What knotless writes in a space, named once. nsid::SPACE (the 21 collections written in a private repository's space: its records, the forge's, reactions, checks, reruns, cancels, the organization's member records and its site) and nsid::ORG_SPACE, reached by space type through space::collections. Remote::apply, the one way the CLI, MCP, serve's gateway and the runner's deletes write in a space, debug_assert!s each write's collection is in its type's list (the runner's Reporter::put writes only dev.knotless.check). The audit found cancel the only one missing: releases and stars are refused on a private repository, and minutes, themes and profiles are public.

The test. oauth::tests::space_lexicons_list_every_collection_written fails when a collection in either list is missing from its type's lexicon, or when the gateway's scope names one in a space that is not in its list.

The message. A refusal for want of scope said your login is older than what knotless asks for now whatever the login asked. Now the CLI says that only when the login is behind (oauth::login_behind, the same check its end-of-command note makes). One that asked says your login asks for this, but your PDS did not grant it, and, for a write in a space (oauth::in_space), that a PDS grants writes in a space as the lexicon knotless publishes for its type listed them at login, so knotless login again grants it once that lists it. MCP's tools and resources say the same, through Server::failed and Server::advise.

Why

The CLI asks for each space type by name (space:dev.knotless.space?authority=*&manage=…), and the authorization server grants the collections the type's lexicon lists at approval (pds.js f0591fde, as the reference PDS does). d9ed4f9 added dev.knotless.cancel to the scope's repo: list but not to the lexicon, so pds.js refused the create (checkSpaceScope). Tests log in with app passwords, so nothing caught it.

Publishing

publish-lexicons publishes the changed dev.knotless.space once main's seven checks pass at the merge commit: it runs on a push to main that touches lexicons/**, writing the com.atproto.lexicon.schema record in lexicons.knotless.dev (deploy/README.md). No DNS change: the authority is unchanged. After it runs, a PDS that cached the old lexicon (pds.js keeps one an hour) reads the new one, and the owner logs in again (knotless login chadtmiller.com) to be granted cancel.

How it was checked

node harness/lexicons.mjs passes (style, and only grown from main). The unit tests in oauth.rs, cli.rs and knotless-mcp's lib.rs are left to CI.

Shortcomings

The pages still answer any refusal for want of scope with LOGIN_BEHIND (web.rs's behind, which has no viewer): unchanged here.

Approved

This adds dev.knotless.cancel to the dev.knotless.space lexicon, so a PDS grants it to a private repository's owner at login. It also names the collections written in each space type once (nsid::SPACE and ORG_SPACE, via space::collections) and checks them in a unit test and a debug_assert! in Remote::apply. Finally, the CLI and MCP no longer tell a login that already asked for the scope that it is "older". That message now appears only when login_behind is true.

I read the whole diff and found nothing that needs changing:

  • The 21-entry SPACE list matches the lexicon's collections plus the new cancel.
  • The refusal wording branches correctly on login_behind and in_space.
  • The docs and the changeset are updated as AGENTS.md asks.
  • check, lexicons and docs pass. test, e2e, web and ios were still running when I looked, so I haven't seen their results. I didn't run the new unit tests myself.

One note for the merge: the fix only reaches the owner after publish-lexicons has run from main and they run knotless login again. The pull body already says this.

Approved

My earlier approval stands. Since then the only change is in knotless-mcp: grant now takes &dyn Fn() -> bool instead of impl FnOnce, and the caller and the unit test are adjusted to match. This does not change what grant does. The last test case no longer uses unreachable!(), but a message that isn't a scope refusal returns before the closure is called, so it still checks the same thing. I did not run the tests myself.

Merged as 1f0a5c7