Skip to content

fix(daemon): review a grant-covered repository at an exact checkout - #2506

Merged
zfy0701 merged 1 commit into
mainfrom
claude/grant-review-checkout
Sep 25, 2026
Merged

zfy0701 merged 1 commit into
mainfrom
claude/grant-review-checkout

Conversation

@zfy0701

@zfy0701 zfy0701 commented Sep 25, 2026

Copy link
Copy Markdown
Contributor

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 always rows 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: reviewRootFor checks the primary, then a GitHub row, then a GitHub installation grant whose accountLogin matches the hook repository's owner (case-insensitive). A grant-covered repository resolves to { repoFullName, repoId } using the hook's repository id. prepareGithubReviewWorkspace passes that id through as reviewRepoId. 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 / SessionRootScope gain reviewRepoId.
    • reviewedSecondaryRoot checks rows, then the primary, then a grant. For a grant it builds a row-shaped entry and places it through rootsAt, the same code a row goes through, so subtree name, clone URL, credential scope and sandbox-mount addressing match a row's.
    • Resume: when no row answers, resumedReviewedRoot resolves 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.
    • prepareConfinedSession places a grant-covered cwd root into the session's mount beside its roots, for local confined sessions, pool session pods and executors alike. readyRoots and the selection hand-out still exclude it.
    • Row precedence and primary precedence come from call order.
  • Docs: docs/designs/multi-repository-workspaces.md decision 6 and the installation-grant status note in docs/designs/agent-multi-repo-authorization.md.

Lifecycle

  • Retirement: no code change was needed. retiredSecondaryRoots treats as retired any subtree whose attested id is not an always row 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 and removeRetiredSecondaryRoot finds no session worktree under it (retained: worktrees until 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.
  • Precedence: a repository with its own row keeps the row (its own id and materialization). The workspace repository stays the primary.

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):
    • A grant-covered repository resolves to its own root, named by the hook's id.
    • A row and the primary win over a grant.
    • Another account, a GitLab grant and a revoked grant each resolve no root.
  • workspace-secondary-roots.test.ts (5 cases, worktree tier, real git):
    • Exact checkout at the merge, attested under the hook's id, absent from every other session's roots and directories.
    • Resume in a fresh manager.
    • A row wins over the grant.
    • Another account, a revoked grant and a name without an id are each refused, with nothing left behind.
    • The retire sweep keeps the checkout while the session worktree lives and removes it after.
  • 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): prepareGithubReviewWorkspace asks for the exact checkout with reviewRepoFullName and reviewRepoId.

Mutation checks: disabling each piece below turned at least one of the new tests red, and restoring it turned them green again.

  • The orchestrator's grant branch.
  • The reviewRepoId plumbing.
  • reviewedSecondaryRoot's grant fallback.
  • resumedReviewedRoot's grant fallback.
  • The confined containment fallback.
  • The prepareConfinedSession placement.

🤖 Generated with Claude Code . Opus 5.5

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>
@zfy0701
zfy0701 enabled auto-merge (squash) September 25, 2026 12:36

@agentconnect-md-test agentconnect-md-test Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

@zfy0701
zfy0701 merged commit db779c2 into main Sep 25, 2026
14 checks passed
@zfy0701
zfy0701 deleted the claude/grant-review-checkout branch September 25, 2026 12:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant