Files
kandev/docs/specs/workspaces/system-design/worktree-base-refresh.md
T

196 lines
9.4 KiB
Markdown

---
status: draft
system: workspaces
requirements:
- REQ-WORKSPACES-WORKTREE-BASE-REFRESH-001
---
# Worktree Base Refresh System Design
## Purpose and boundaries
The workspace system owns the repository checkout, remote state, and base ref
used to create a worktree. This design makes `repositories.pull_before_worktree`
an admission gate for new and recreated worktrees.
The task system continues to own session launch state and error presentation.
It consumes preparation errors through the existing launch-failure contract in
[Task Launch Failure Recovery](../../tasks/system-design/task-launch-failure-recovery.md).
The integration system continues to own provider credentials. The executor
continues to own credentials and SSH configuration inherited from its runtime.
This design does not refresh a worktree that Kandev can reuse without
recreation. It also does not change Git commands that an agent runs after
launch.
## Requirement mapping
| Requirement | Design section |
| --- | --- |
| `AC-WORKSPACES-WORKTREE-BASE-REFRESH-001.1` | [Refresh policy](#refresh-policy) |
| `AC-WORKSPACES-WORKTREE-BASE-REFRESH-001.2` | [Refresh routing](#refresh-routing) |
| `AC-WORKSPACES-WORKTREE-BASE-REFRESH-001.3` | [Failure and recovery](#failure-and-recovery) |
| `AC-WORKSPACES-WORKTREE-BASE-REFRESH-001.4` | [Base-ref selection](#base-ref-selection) |
| `AC-WORKSPACES-WORKTREE-BASE-REFRESH-001.5` | [Base-ref selection](#base-ref-selection) |
| `AC-WORKSPACES-WORKTREE-BASE-REFRESH-001.6` | [Multi-repository launch](#multi-repository-launch) |
| `AC-WORKSPACES-WORKTREE-BASE-REFRESH-001.7` | [Launch-error projection](#launch-error-projection) |
## Components and responsibilities
- `internal/orchestrator/executor` resolves every task repository and selects
the refresh route that matches its provider and task Git credential policy.
It passes `PullBeforeWorktree`, `RemoteSyncHandled`, and a provider-authenticated
refresh callback to the lifecycle layer. The callback is not invoked until a
worktree must be materialized or recreated.
- `internal/repoclone.Cloner` performs a strict refresh for a Kandev-managed
provider checkout when provider credentials belong to the backend refresh
boundary. `RefreshWorkspaceRepositoryWithCredentialRequest` binds the fetch
to the exact workspace, task, session, and repository scope.
- `internal/worktree.Manager` performs strict non-interactive fetch and base-ref
selection when the checkout uses the host or executor Git route. It creates
no worktree until the required sync and ancestry checks succeed.
- `internal/agent/runtime/lifecycle` propagates repository preparation errors
without starting the agent runtime.
- The task launch-failure projection stores and renders the resulting bounded,
credential-safe error. This design does not add a parallel workspace error
store.
## Data and contracts
`repositories.pull_before_worktree` remains the persisted user policy. No
schema change is required.
The existing worktree preparation fields have these contracts:
| Field | Contract |
| --- | --- |
| `PullBeforeWorktree` | A required remote refresh remains to be completed before base-ref selection. |
| `RemoteSyncHandled` | A strict provider-authenticated refresh completed for the exact checkout. The worktree manager must not run a second unauthenticated fetch. |
| `RefreshRepository` | An optional exact-scope refresh callback. Lifecycle invokes it only when creating or recreating a worktree; valid reuse bypasses it. |
`RemoteSyncHandled` is valid only after a successful strict refresh. A
best-effort fetch or a failed refresh cannot set it.
## Refresh policy
When `PullBeforeWorktree` is false, base-ref resolution keeps its offline local
behavior. This path does not claim that the checkout contains current remote
state.
When `PullBeforeWorktree` is true, worktree materialization requires one
successful refresh. A caller cannot convert a refresh error into a warning and
local ref. The rule applies to initial launch and to resume whenever Kandev
creates or recreates a worktree. A valid reusable worktree is returned before
the callback is invoked.
## Refresh routing
The executor resolves one of two refresh routes before lifecycle preparation:
1. A Kandev-managed provider route supplies a callback that calls the strict
`repoclone` refresh with the exact credential request. The lifecycle layer
invokes it only when it must materialize or recreate a worktree. On success,
worktree creation clears `PullBeforeWorktree` and sets `RemoteSyncHandled`.
2. A host or executor route leaves `PullBeforeWorktree` set. The worktree
manager uses the reconciled `origin` URL and the non-interactive host Git
environment. This includes an executor-inherited GitHub checkout whose
origin uses the protocol selected by the host `gh` configuration.
When a managed checkout carries a pull request number, the strict refresh also
fetches `refs/pull/<N>/head` into a local `origin/pr/<N>` remote-tracking ref.
Worktree creation uses that ref for the checkout branch, including fork pull
requests, without a second unauthenticated fetch.
Provider selection must follow the existing task Git credential policy. A
managed GitHub workspace uses the backend-managed credential route. An
executor-inherited GitHub workspace must not replace the reconciled SSH origin
with a managed HTTPS origin.
The plugin-provider strict-refresh behavior remains the model for exact
credential scope. GitLab and Azure DevOps keep their existing provider-specific
credential resolution.
## Base-ref selection
After a successful fetch, the worktree manager compares the local base `L`
with the fetched remote base `R`:
| Relationship | Start ref | Reason |
| --- | --- | --- |
| `R` contains `L` | `R` | Includes all local commits and current remote commits. |
| `L` contains `R` | `L` | Preserves local-only commits and includes current remote commits. |
| Neither contains the other | Error | The refs diverged and either choice would omit commits. |
| Ancestry cannot be proven | Error | Kandev cannot prove that the start ref is safe. |
A successful fast-forward pull normally produces the first or second state.
If pull fails after fetch, the same table selects the safe ref. The manager does
not reset, rebase, merge, or delete a branch.
`pullBaseBranch`, `resolveLocalBaseRef`, and
`pullCurrentBranchOrFallback` return errors to their callers. The errors retain
Git failure classification but exclude tokens, credential helper output, and
secret-bearing URLs.
## Multi-repository launch
The executor resolves and prepares repository specs before agent startup. If a
required refresh fails for one spec, lifecycle preparation returns an error and
does not start the runtime. The error carries stable repository identity and a
safe display name so same-provider sibling repositories remain distinguishable.
Preparation can leave an already refreshed checkout on disk. This state is
safe and retryable. A later launch performs the required checks again. Kandev
does not report a partial task launch because no agent process started.
## Launch-error projection
The executor returns required-refresh errors through its existing launch
failure path. The task system stores the active error in the current typed
summary and renders it on desktop and mobile after reload. The generic launch
failure category is sufficient unless implementation evidence shows that a
distinct category enables a safe recovery action.
The bounded detail names the repository and failure class. It does not include
credentials, tokens, raw authenticated URLs, or unrestricted Git output. Retry
uses the normal task launch action after the user corrects network, SSH, or
credential configuration.
## Failure and recovery
- Fetch authentication, network, timeout, cancellation, and Git command errors
stop required preparation.
- A missing fetched remote base stops preparation. It cannot fall back to a
local ref when refresh is required.
- A failed ancestry probe stops preparation.
- A divergent base stops preparation and preserves both refs.
- Disabling pull-before-worktree is the explicit offline opt-out. Kandev does
not disable it automatically after a failure.
- Retrying task launch reruns remote refresh and base-ref selection.
## Security
Managed refresh uses the provider credential boundary and exact task, session,
and repository scope. Executor refresh uses only credentials available to the
selected host or executor route. Neither route copies a provider token into an
error, progress event, or log field.
Git remains non-interactive. `GIT_TERMINAL_PROMPT=0`, batch-mode SSH, bounded
command contexts, and Git subprocess admission prevent a hidden prompt from
blocking task launch.
## Observability
The existing sync progress callback reports a running event and then either a
completed event or a failed event. A failed event contains the bounded failure
class and repository identity, not a fallback-ref success message.
Structured logs record the repository ID, task ID, session ID, provider,
configured transport, refresh route, and failure class. Logs do not record
credential material or secret-bearing remote URLs.
## Related decisions
- [Required Worktree Refresh Fails Closed](../../../decisions/2026-08-25-required-worktree-refresh-fails-closed.md)
- [Separate GitHub Automation From Task Git Credential Policy](../../../decisions/2026-07-27-task-git-credential-policy.md)
- [Provider-Neutral Git Credential Broker](../../../decisions/2026-07-31-provider-neutral-git-credential-broker.md)