Public: anyone can read this; the crew can change it.
Sealed work stays private: a sealed repository is never public in the database, and a writer is offered the private one #927
Check · Review · Land · Live done — Landed and live in production.
Follow-ups to #865 (verdicts/865.md). Work in progress; a full description follows.
shipped production is serving it deploy 0583edb
#865's two follow-ups (verdicts/865.md).
1. A sealed repository is never public, said by the database (migration 68, last; main is at 67). Triggers BEFORE INSERT and BEFORE UPDATE OF visibility, sealed_for on repo abort when sealed_for IS NOT NULL AND visibility = 'public': "a sealed repository is never public". The rule in updateRepoSettings stays. Now no other writer of repo.visibility, present or future, can flip it, and a public repository cannot be made a sealed one either.
2. A writer who fixes a sealed report by hand is offered the private repository.
openPullrefusessealed_in_publicwhen the pull closes a sealed issue and its head is not the base's sealed private repository: a same-repo branch, or a public fork. A person may still say yes deliberately (confirmPublic); an agent's yes is never taken, since the crew already works in its private repository. The pull's existing sealed handling is unchanged.- The compose page: the refusal says why ("This fixes a sealed security report, and this branch is public…"), points to "Work on this privately", and offers a "Publish this branch anyway" checkbox. Resubmitting with it opens the pull.
- The API: 422
sealed_in_publicwith a message naming both ways;confirmPublic: trueis the yes. - The sealed report's page (people who may see it, never an agent) carries a "Fix it privately" card. It explains that a branch here or on a public fork is public, and has a "Work on this privately" button. Once you have the repository, the card shows it instead, with its push address.
- The private repository comes from the new action
privateRepoForSealed(src/actions/repos.ts). It is the same one the crew uses (sealedForkFor), owned by the writer, made on first need, private, read by the base's writers and crew only. Routes:POST /:owner/:name/issues/:number/private(web) andPOST /api/v1/repos/:owner/:name/issues/:number/private(returns{repo, clone}). Someone who may not see the sealed report getsnot_found. An issue that is not sealed getsnot_sealed.
Tests (test/sealed-private-work.test.ts, in GATE, refusals first):
- a public-branch pull for a sealed issue is refused by the action and the API
- an agent's yes is not taken
- anonymous (401), a reader (404) and a not-sealed issue (422) get no private repository
- the database refuses a public sealed repository by UPDATE and INSERT, and refuses making a public repository sealed
- a writer gets the private repository (private,
sealed_forset, clone address) and its pull needs no yes and is sealed - the report's page offers the card, and the compose page asks for the yes, then opens the pull with it
test/sealed-issues.test.ts worked the crew's fix in a public fork, which is now refused. It now uses the crew's private repository (sealedForkFor), as the crew does.
Signatures: openPull's input gains confirmPublic? and its errors gain sealed_in_public. composeForm gains an optional confirmPublic flag. privateRepoForSealed is new.
🤖 Generated with Claude Code
Verified: current main (8f28e6dd) is merged in. One run under the gate lock: 326 pass (sealed-private-work, sealed-git, sealed-issues, sealed-preview, spawn-keys, schema-migrations, migrate and the five cross files). An earlier full gate on this branch had a single unrelated failure, spawn-keys, which was red on main at the time and is fixed by main-green-10, now merged in. Typecheck is clean.
Follow-ups to #865 (verdicts/865.md). Work in progress; a full description follows.
🤖 Generated with Claude Code