fix(daemon): review a grant-covered repository at an exact checkout - #2506
Conversation
An agent may cover a whole GitHub account with an installation grant instead of one row per repository. The Control Plane already admits hooks, mints tokens and authorizes review publication for a repository only the grant covers, but the daemon's review root lookup read rows alone, so a formal review of such a repository degraded to the revision-only empty directory. Resolve that repository to a root of its own when no row or the primary names it: the orchestrator passes the hook's repository id beside its name, and the workspace manager places a row-shaped root through the same placement a row uses, so subtree, clone URL, credentials and sandbox mount are a row's. The root serves only that review's working directory, exactly like an on-demand row's review: it never joins the agent's roots, another session, the additional directories or the on-demand listing. A restart resumes the session from its cwd record plus the attestation its checkout was written with (the session's own clone when confined, else the agent's subtree), for as long as the grant still covers the account. The retire sweep already treats the checkout as it treats an on-demand row's: retired because no always row names it, kept while a session worktree reads it, removed once none does. A revoked grant resolves no root, so a later review is revision-only again. Part of #2398. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
I reviewed the grant-covered repository path at this revision. The hook repository ID reaches the new root, the review checkout is prepared at the exact revision, and the resumed session recovers that root from its attestation while the grant remains present. The shared and confined paths keep the review root out of other sessions' ready roots. I found no blocking issue.
Non-blocking: this PR also lowers dependency versions across the workspace (including the pinned sandbox runtime and promptfoo versions) and rewrites the lockfile, which appears unrelated to the review-root change. Please restore those version changes if they were unintended.
Validation: git diff --check passed. I could not rerun the daemon tests in this checkout because dependencies are not installed; the PR reports its focused test runs.
sent by review-bot (Codex · gpt-6-sol) · open in session
Summary
Part of #2398.
An agent can cover a whole GitHub account with an installation grant (
workspace.additionalInstallations) instead of one row per repository. The Control Plane already admits hooks, mints tokens and authorizes review publication for a repository only the grant covers, but the daemon's review root lookup read rows alone. A formal review of such a repository therefore fell back to the revision-only empty directory.With this change, that review gets an exact checkout that behaves like an On demand row's review. The daemon checks the repository out at the pull request's exact revision before the runtime starts, and that checkout becomes the session's working directory. The primary and the
alwaysrows ride along as references. The grant-covered root is that one review's alone: it is never one of the agent's roots, never another session's reference or additional directory, and never an entry in the on-demand listing.Changes
github/review-orchestrator.ts:reviewRootForchecks the primary, then a GitHub row, then a GitHub installation grant whoseaccountLoginmatches the hook repository's owner (case-insensitive). A grant-covered repository resolves to{ repoFullName, repoId }using the hook's repository id.prepareGithubReviewWorkspacepasses that id through asreviewRepoId. A repository of another account, a GitLab grant, or a grant that has been revoked still resolves to no root, so the review stays revision-only.workspace/workspace-manager.ts:PrepareSessionWorkspaceRequest/SessionRootScopegainreviewRepoId.reviewedSecondaryRootchecks rows, then the primary, then a grant. For a grant it builds a row-shaped entry and places it throughrootsAt, the same code a row goes through, so subtree name, clone URL, credential scope and sandbox-mount addressing match a row's.resumedReviewedRootresolves a placed session's recorded cwd subtree through the grant. The repository id comes from the attestation the checkout was written with: the session's own clone on the confined tier, otherwise the agent's subtree.materialization.json.sessionCwdRootPath, the confined-tier containment check, uses the same fallback.prepareConfinedSessionplaces a grant-covered cwd root into the session's mount beside its roots, for local confined sessions, pool session pods and executors alike.readyRootsand the selection hand-out still exclude it.docs/designs/multi-repository-workspaces.mddecision 6 and the installation-grant status note indocs/designs/agent-multi-repo-authorization.md.Lifecycle
retiredSecondaryRootstreats as retired any subtree whose attested id is not analwaysrow or a held selection. That already covers an On demand row's review checkout, and the grant-covered checkout falls under the same rule. The idle sweep removes it only once the agent is idle andremoveRetiredSecondaryRootfinds no session worktree under it (retained: worktreesuntil then) and a clean checkout with no local-only commits. On the confined tier the review clone lives in the session's own directory and goes with the session.Tests
All run from the worktree with low concurrency:
pnpm --dir packages/daemon exec vitest run --maxWorkers=2 test/github/review-root.test.ts test/workspace-secondary-roots.test.ts test/workspace-session-clone.test.ts test/cluster-workspace-prepare.test.ts test/executor-workspace-scope.test.ts: 5 files, 232 tests passed.pnpm --dir packages/daemon exec vitest run --maxWorkers=2 test/daemon-hook.test.ts: 116 passed.pnpm --dir packages/daemon exec vitest run --maxWorkers=2 test/daemon-k8s-mode.test.ts test/daemon-repo-selection.test.ts test/decision-repo-selection.test.ts test/session-manager.test.ts test/workspace.test.ts: 5 files, 284 tests passed.pnpm --filter @agentconnect.md/daemon typecheck, plus prettier and eslint on the changed files: clean.New cases:
review-root.test.ts(3 cases):workspace-secondary-roots.test.ts(5 cases, worktree tier, real git):workspace-session-clone.test.ts(1 case, local confined tier): the review in the session's own clone, then resume in a fresh manager.cluster-workspace-prepare.test.ts(1 case, pool session pod): the review clone, then resume from the session pod alone while the agent pod is asleep; a revoked grant no longer prepares the root.daemon-hook.test.ts(1 case):prepareGithubReviewWorkspaceasks for the exact checkout withreviewRepoFullNameandreviewRepoId.Mutation checks: disabling each piece below turned at least one of the new tests red, and restoring it turned them green again.
reviewRepoIdplumbing.reviewedSecondaryRoot's grant fallback.resumedReviewedRoot's grant fallback.prepareConfinedSessionplacement.🤖 Generated with Claude Code . Opus 5.5