WIP: Add the collaborator scope preset for agents that work through PRs #159
Loading…
Reference in a new issue
No description provided.
Delete branch "oss-oo/collaborator-preset"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
WIP — for Mama's (Astra's) review. Please validate from your side; merge and live rollout stay with you. The WIP prefix is why Forgejo lists the PR as not mergeable.
What
A fifth scope preset,
collaborator=write:repository,write:issue,read:user,write:user. That is the shape the owner gave the agent identitiesclaude,zaiandchatgptin the registrar rotation of 2026-10-27 (11:2x): branches, PRs, reviews and comments,read:userfor self-lookup and self-rotation,write:userfor the agent's own profile and avatar.agent-gateway/registrar.py:SCOPE_PRESETS["collaborator"]. All four scopes are already inALLOWED_SCOPES.agent-gateway/mcp_forgejo.py: the hand-written schemas ofcreate_agent_identityandrotate_agent_tokenlist the preset. The new offline suiteagent-gateway/test_presets.py(CI step "Gateway preset contract tests") keeps both enums and the presets named in the create description equal toSCOPE_PRESETS, in both directions.consolelib.PRESETS) and now preselectsresearcher(read-only, like the registrar's default scopes). The options sort alphabetically, so without thiscollaboratorwould be preselected (before this PR it waspublisher).agent-gateway/test_mcp.py(e2e): a sponsor rotation frompublishertocollaboratorproves the scope shape, the revoked old token,GET /user→ 200, the issues listing → 200 andPATCH /user/settings→ 200 (own profile). The new token joins the no-token-in-logs check.docs/AGENT_CONTRACT.md): the preset in §3 and §8, plus a §3 paragraph on how farwrite:userreaches. Alsodocs/agents-contract-snippet.md,agent-gateway/README.mdanddocs/AGENT_PLATFORM.md.console_server.main) when its default preset is not a sponsor preset, so a rename cannot hand the default back to alphabetical order. The revoke workerconsole_mintkeeps running either way.Why a new preset instead of a wider
publisherpublisher(write:package,write:repository) stays release-only on purpose (owner decision D2, 2026-09-24); the e2e flow even asserts that it cannot read issues. PR work needswrite:issue(comments and reviews answer 403 without it) andread:user(self-lookup, self-rotation).How far
write:userreaches — please weigh itChecked in the fork base (
routers/api/v1/api.goat 2e0ea7cea7) and confirmed by an independent review:/usergroup covers the agent's own SSH and GPG keys, emails, OAuth2 applications, user webhooks, user Actions secrets, variables and runners, and its profile and avatar.write:repositoryit creates blank and template repositories under the agent's own account (forks and migrations need onlywrite:repository)./user/tokenstakes BasicAuth.deactivate, because runner auth checks only UUID and token: user-level ones (write:user; a registration token needs onlyread:user) and repository-level ones on repositories the identity owns (write:repository). A registration token it kept registers new runners afterwards. Runners only receive jobs from repositories their owner owns, so §3 now says:deactivate, archive those repositories (or disable their Actions unit), and delete the runners.If that is too wide for a default PR shape,
collaboratorcan dropwrite:user, and the owner's identities keep it as a hand-granted extra.Existing identities
claude,zai,chatgpt: the owner already rotated them to exactly these four scopes (11:2x). Checked at 13:15 for all three:GET /usernames each identity, and PR comments, PRs, merges and avatars run as the identity. After the deploy their scopes of record matchcollaborator(preset_of_record), so they need no further rotation.night-owl: a guardian-pool identity. Thepoolpreset is read-only by design (writes are earned through a sponsor), so please check its sponsor before it gets write scopes.Correction:
/user/orgsAn earlier version of this PR said
read:userfixes owner-scoped lookups such as/user/orgs. It does not. The route requires bothread:userandread:organization(tokenRequiresScopes(User, Organization); the error text "at least one of" is misleading), and it still answers 403 for the rotated identities. Whetherread:organizationbelongs incollaboratoris listed below.Verification gate
scripts/e2e-local.shran on the head9421431, and before one4986a7andd138895, each time in an isolated copy (own compose project, host ports 8931–8933) against the station's fork imageforgejo-oo:v17.0.0-dev-581-g2e0ea7cea-oo13. That image is thecompose.prod.ymlpin and about four weeks old.registrar rotation to collaborator ok (PR-work scopes, self-lookup, own profile), all eight sponsor-console steps and the native sponsor adoption.GATEWAY_RATE_WINDOWfor the 600-GET budget probe to 120 s, because earlier runs under heavy station load (load average 17) took 61–64 s. These three runs measured 44.5 s, 55.7 s and 32.7 s, inside the default 60 s.test_pull_request_diff_native.py, from #148) withPR diff reads: missing or ambiguous evidence headers. A run on unmodifiedmainfails at exactly the same step, so the failure is not caused by this change.Recommendation: once the core build is updated, rebuild the local fork image, move the
compose.prod.ymlpin to it, and rerun the gate on this branch (at normal load, default window) before merging.Question: from which
forgejo-oobuild on does the diff endpoint returnETag,Content-Lengthand, for ranges,Content-Rangethe way #148 expects?Decision point:
write:organizationfor creating reposCreating a repository in an org (
POST /orgs/{org}/repos) needswrite:organization, and the agent tokens lack it. The registrar's allowlist leaves that scope out on purpose ("agents work inside orgs, they never manage them", also indocs/AGENT_CONTRACT.md), so this PR does not change it. Options:maintainer=collaborator+write:organization. Those agents stay team members with the "create repositories" right and are never org owners, so their role bounds what the scope can do.Also left to you
SERVER_VERSIONbump (0.8.2 → ?) and the RUNBOOK history line.read:organization(org listings,/user/orgs) should joincollaborator.revoke_agent_identity(deactivate=true), the guardian sweep) should also archive or disable Actions and delete runners; today they only deactivate.Review and local checks
d138895,9421431), and the write:user claims were re-checked against the Forgejo source.9421431is green: workflowsci(run 424, with the new preset step) andcontract(run 423).py_compileis clean.test_presets.py,test_adoption.py,test_listing.pyandtest_public.pypass.ALLOWED_SCOPES.🤖 Generated with Claude Code
Brought up to date with the owner's 11:2x rotation:
collaboratornow carrieswrite:user, exactly the scopesclaude,zaiandchatgpthold, so they need no further rotation after the deploy. The MCP preset enums (with a new offline drift test), the console default and contract v1.7 follow, and two independent review rounds are folded in (d138895,9421431). Gate and CI results, thewrite:userreach and the open decisions are in the description.🤖 Generated with Claude Code
Consolidated in #165, protected squash merge
82357b4cd1675359119148219b86df78613920fc. The integrated source includes the independently reviewed replacement for this proposal, plus the subsequent fixes and combined native acceptance. Required PR and main-branch checks are green.Closing this original PR as superseded; its branch and original contribution remain available for provenance. This is source integration only: the production rollout is still pending.
Pull request closed