fix(workspace): default an unstated access tier to what the target ca… - #1567
Conversation
…rries
Creating an agent and replacing its workspace disagreed on the default
access tier: creation reads `gitAccess ?? 'write'`, while
`SetAgentWorkspaceBody` declared `.default('read')`. An API caller who
omitted the field therefore got write at creation and read on the very
next edit — and on GitLab that silently re-clamped the agent's own
account from Developer down to Reporter, since the membership level is
derived from the workspace tier.
The tier now follows what the target can actually carry, which is the rule
creation already applied. An App-backed GitHub repository and a managed
GitLab binding both mint credentials, so an unstated tier is write there,
and the identity gate holds the caller to it. An anonymous checkout has
nothing to push with, so it stays read; an explicit write against one is
still refused.
The console always sends the field, so this only changes what an API
caller gets, and only toward the tier the same caller would receive from
creation.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
There was a problem hiding this comment.
No blocking findings.
The omitted field now reaches the route instead of being prematurely rewritten to read. For GitHub, the route defaults to write only after finding a live covering installation, passes that resolved tier through the existing user-identity authorization gate, and still forces an anonymous checkout to read while rejecting explicit anonymous write access. For GitLab, the resolved write default is persisted and passed into inline account provisioning, so a workspace edit no longer re-clamps the agent from Developer to Reporter. The OpenAPI description, design contract, and web client type all describe the same behavior, and the focused integration case covers both credential-backed and anonymous GitHub defaults.
git diff --check HEAD^1..HEAD passed. I could not execute the control-plane typecheck or focused integration suite in this isolated checkout because dependencies are absent and pnpm stopped before project code on its unwritable store database; a fresh task-scoped Corepack cache then could not resolve the npm registry. I treated that as a runner verification gap, not a code failure.
sent by review-bot (Codex · gpt-5.6-sol) · open in session
* docs(design): the host-neutral git workspace model The workspace discriminant is the code host today (scratch|github|gitlab), which bakes a UI notion into the wire, the database, and two independently written route validations — the root shared by #1561 and #1567. The daemon never adopted that model: it collapses the arms into git-repo|from-scratch plus an independent gitCredential on arrival. This design promotes the daemon's model outward: mode answers "is there a repository", a credential union answers "who vouches for it". Provenance is derived server-side by one function shared by creation, replacement, and a resolve endpoint the console's badges call, so the picker can no longer disagree with the write path. Provider tiles stay in the console — they carry lexicon and guidance — but all produce the same payload, and a "From a Git URL" tile plus GitLab public-project candidates fall out of the neutral shape. Rollout is readers-first behind a workspace-git-v1 daemon feature so an old daemon never receives an arm it fatals on. Records the decided rules: provenance fixed at write time and never auto-upgraded, bare Git rides the anonymous arm on host credentials, unstated access takes the highest tier the target carries, and a covered owner's ungranted repository refuses rather than degrading to anonymous. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * docs(design): actor-scoped derivation and the persisted read shape From review: the shared derivation cannot run the per-user identity gate without the acting user, so the actor and requested tier join its contract — provenance stays a function of (orgId, gitRepo), eligibility and ceiling are the actor's. And the console renders a stored workspace from the persisted credential in the agent DTO, never the live resolver, or an owner installing the App would re-badge an anonymous workspace in place — the auto-upgrade the design rules out. Also notes the gitlab daemon-feature gates re-key onto the credential provider before dual encoding begins. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
…rries
Creating an agent and replacing its workspace disagreed on the default access tier: creation reads
gitAccess ?? 'write', whileSetAgentWorkspaceBodydeclared.default('read'). An API caller who omitted the field therefore got write at creation and read on the very next edit — and on GitLab that silently re-clamped the agent's own account from Developer down to Reporter, since the membership level is derived from the workspace tier.The tier now follows what the target can actually carry, which is the rule creation already applied. An App-backed GitHub repository and a managed GitLab binding both mint credentials, so an unstated tier is write there, and the identity gate holds the caller to it. An anonymous checkout has nothing to push with, so it stays read; an explicit write against one is still refused.
The console always sends the field, so this only changes what an API caller gets, and only toward the tier the same caller would receive from creation.