feat: support git worktree resources in task inputs (#121)#302
Merged
github-actions[bot] merged 4 commits intoMay 26, 2026
Merged
Conversation
Contributor
There was a problem hiding this comment.
Pull request overview
This PR adds first-class support for materializing local git repositories into a task’s per-run workspace via inputs.repos (currently type: worktree) and allows choosing the agent’s starting directory via inputs.workdir, enabling isolated checkouts when developing skills inside the same repo they operate on.
Changes:
- Extends the task YAML model + JSON schema with
inputs.reposandinputs.workdir, including validation. - Implements git-resource materialization/cleanup (worktrees) and wires it into both Copilot and mock execution engines.
- Adds tests, docs, and a new runnable example demonstrating repo resources.
Show a summary per file
| File | Description |
|---|---|
| site/src/content/docs/reference/schema.mdx | Documents new inputs.repos and inputs.workdir fields in the reference site. |
| schemas/task.schema.json | Adds schema definitions for repos, workdir, and gitResource. |
| README.md | Adds end-user documentation for Git Repository Resources. |
| internal/orchestration/runner.go | Forwards Repos/WorkDir from stimulus to ExecutionRequest. |
| internal/models/testcase.go | Adds GitResource model, stimulus fields, and YAML-time validation. |
| internal/models/git_resource_test.go | Tests YAML round-trip + validation for repos/workdir. |
| internal/execution/mock.go | Materializes git resources in mock engine and cleans them up. |
| internal/execution/git.go | New git materialization implementation (worktree add/remove) + rollback logic. |
| internal/execution/git_test.go | Tests worktree materialization and ResolveWorkDir. |
| internal/execution/engine.go | Adds ExecutionRequest.GitResources/WorkDir and ResolveWorkDir. |
| internal/execution/copilot.go | Wires git resources into workspace setup and ensures cleanup happens before workspace deletion. |
| examples/repo-resources/tasks/explain-repo.yaml | New example task using inputs.repos + inputs.workdir. |
| examples/repo-resources/README.md | Explains setup and usage of the repo-resources example. |
| examples/repo-resources/eval.yaml | Adds an eval that runs the repo-resources example with the mock executor. |
| examples/README.md | Lists the new repo-resources example. |
| docs/GUIDE.md | Adds an “Advanced Usage” guide section for testing against a local git repo. |
Copilot's findings
- Files reviewed: 16/16 changed files
- Comments generated: 9
Adds an `inputs.repos` field to task YAML so a task can materialize a clean copy of a local git repository into its per-run workspace before the agent runs, plus an `inputs.workdir` field for the agent's starting directory inside that workspace. Today only the `worktree` strategy is supported: it uses `git worktree add --detach` against a local clone, which is cheap (shares the source repo's .git object store), needs no network, and never conflicts with the source repo's current checkout. Worktrees are removed via `git worktree remove --force` on engine shutdown before the temp workspace is deleted, so the source repo's .git/worktrees/ bookkeeping stays clean. This is the smallest complete fix for the issue. HTTPS/SSH clone, submodules, Git LFS, and auto-detecting 'the current repo' are explicitly out of scope and tracked as follow-ups. Closes #121 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
dd2f77e to
72fd00f
Compare
ExecutionRequest has no Timeout field and the timeout variable is not in scope inside buildExecutionRequest. Timeouts are already applied via context.WithTimeout at the call sites (executeRun and executeFollowUps). Fixes build failures on all platforms. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Contributor
There was a problem hiding this comment.
Copilot's findings
Comments suppressed due to low confidence (2)
internal/execution/copilot.go:566
--keep-workspaceis documented as preserving the temp workspace “for debugging”, but shutdown always cleans up git resources first. For worktrees, cleanup removes the checked-out repo directory, so the preserved workspace won’t actually contain the repo contents. Consider either documenting this behavior (workspace preserved but worktrees removed), or adjusting behavior/flags so users can opt into preserving the worktree checkout when debugging (with an explicit warning that it will leave stale entries in the source repo if not cleaned).
// Clean up git resources first — `git worktree remove` needs the
// workspace directory to still exist so it can record the removal in
// the source repo's .git/worktrees/ bookkeeping.
for _, gr := range gitResources {
if err := gr.Cleanup(ctx); err != nil {
slog.Warn("failed to cleanup git resource", "error", err)
}
}
for _, ws := range workspaces {
if ws != "" {
if e.keepWorkspace {
fmt.Fprintf(os.Stderr, "Workspace preserved: %s\n", ws)
} else if err := os.RemoveAll(ws); err != nil {
internal/execution/mock.go:171
--keep-workspaceis meant to preserve the workspace for debugging, butShutdownalways callsCleanupon git resources first. With worktrees, that removes the checked-out repo directory, so keeping the workspace won’t keep the repo checkout. Consider aligning mock-engine behavior with the intended debugging semantics (or explicitly documenting that git resources are still cleaned even when the workspace is preserved).
func (m *MockEngine) Shutdown(ctx context.Context) error {
for _, gr := range m.gitResources {
if err := gr.Cleanup(ctx); err != nil {
slog.Warn("failed to cleanup mock git resource", "error", err)
}
}
m.gitResources = nil
if m.workspace != "" {
if m.keepWorkspace {
fmt.Fprintf(os.Stderr, "Workspace preserved: %s\n", m.workspace)
} else if err := os.RemoveAll(m.workspace); err != nil {
- Files reviewed: 16/16 changed files
- Comments generated: 7
Two Windows-specific failures in the git resource tests: 1. git core.autocrlf=true converts LF to CRLF on checkout, breaking assertions comparing file content. Fix: add core.autocrlf=false to initSourceRepo so checkouts always use LF on all platforms. 2. filepath.IsAbs returns false for Unix-style paths like /abs on Windows because they are rooted but not fully qualified (no drive letter). Fix: also reject paths starting with / or backslash in GitResource.Validate(), resolveDest(), and ResolveWorkDir(). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- git.go: drop dead filepath.Clean('') == '' check (Clean of empty
returns '.'); just guard on the empty string directly.
- git.go: require non-empty dest for the worktree strategy because the
engine creates the workspace directory before CloneGitResources runs,
and `git worktree add` refuses targets that already exist.
- git.go: gitWorktree.Cleanup now only marks cleaned=true on successful
`git worktree remove`, so transient git failures can be retried
instead of silently leaking the worktree.
- testcase.go / engine.go: reject '..' segments in the RAW dest /
workdir input. filepath.Clean would normalize 'foo/../bar' to 'bar'
before any later check could see it; the docs/schema say no
traversal, so honor that on the raw input.
- README.md, site/.../schema.mdx, schemas/task.schema.json: mark dest
required and document why (worktree target must not exist).
- Tests: drop NoDest_MaterializesAtRoot (it gamed the constraint by
using a non-existent workspace path); add coverage for the new
required-dest rule and for '..'-segment rejection in both dest and
workdir.
Validation: go vet ./..., go test ./..., cd site && npm run build —
all green.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds
inputs.reposto task YAML so a task can materialize a clean copy of a local git repository into its per-run workspace viagit worktree add --detach, plus aninputs.workdirfield for the agent's starting directory inside that workspace.Motivation (from #121): when you're developing skills inside the same repo the skills are intended to operate on (e.g.
eng/tooling inazure-sdk-for-rust), hand-staging fixtures is painful and stale. With this change a task can point at the local clone and each run gets an isolated checkout — the agent's edits never touch your real working copy.Closes #121.YAML surface
typeworktreetoday.sourcecommit--detachso they don't conflict with the source checkout.destImplementation
internal/models/testcase.go— newGitResource+Repos,WorkDironTaskStimulus, withValidate()rejecting absolute / traversaldestand unsupportedtype.internal/execution/engine.go—ExecutionRequest.GitResources/WorkDir, plusResolveWorkDir()with boundedfilepath.Reltraversal check.internal/execution/git.go(new) —GitResourceinterface +CloneGitResources(ctx, repos, workspace).gitWorktreeAddshells out togit worktree add --detach; cleanup usesgit worktree remove --force. Partial setups roll back on error.internal/execution/copilot.go— wiresCloneGitResourcesintosetupWorkspace, tracks resources under the existing workspace mutex, runsCleanupBEFOREos.RemoveAll(workspace)indoShutdown, and appliesWorkDirviaResolveWorkDirwhen settingWorkingDirectoryon Copilot sessions.internal/execution/mock.go— mirror the same plumbing so mock-engine tasks exercise the worktree pipeline and tests that don't use the SDK still work.internal/orchestration/runner.go— forwardsReposandWorkDirfrom the stimulus toExecutionRequest.Validation
All run from the repo root with
go1.26.1 darwin/arm64:go build ./cmd/waza✅go vet ./...✅go test ./...✅ (full suite)cd site && npm ci && npm run build✅ (18 pages built)New tests:
internal/execution/git_test.go— worktree from HEAD, branch name via--detach, missingsourcerejected, traversaldestrejected, unsupportedtyperejected, no-destmaterialization at workspace root,ResolveWorkDirhappy + traversal + absolute.internal/models/git_resource_test.go— YAML round-trip forrepos+workdir, missing source rejected, unsupported type rejected, workdir traversal rejected,Validate()accepts clean dest / rejects absolute dest.Documentation impact
README.md— new## Git Repository Resourcessection under the eval-spec docs (YAML example, field table, cleanup behavior, out-of-scope notes).schemas/task.schema.json— addedinputs.repos($refto newgitResourcedef) andinputs.workdir;gitResourcerequirestype(enum["worktree"]) andsource.docs/GUIDE.md— new### Testing Against a Local Git Reposubsection under Advanced Usage.site/src/content/docs/reference/schema.mdx— added### reposand### workdirto theinputssection so the reference site mirrors the README.examples/repo-resources/— new minimal example (eval.yaml, tasks/explain-repo.yaml, README) showing the field with a placeholderSOURCE_REPO_PATH. Listed inexamples/README.md.No web/docs screenshots are impacted (no UI surface change).
Out of scope / follow-ups
These were deliberately left out to keep the PR targeted (the previous attempt at this — PR #154 — was closed by its author as "too general"):
source:be a URL).source.Risk mitigations baked in
--detachso they don't trip "already checked out at …" against the source repo.os.RemoveAll(workspace)so a crash mid-test doesn't leak entries in the source repo's.git/worktrees/.destandworkdirare validated with boundedfilepath.Relchecks (reject..escapes and absolute paths) at parse time and at execution time.Review
Per
.squad/team.mdthis is a 🟡 "needs review" feature (medium-sized feature with clear spec following established patterns). Requesting review from Rusty (lead / architect) and Linus (Go backend). Docs-impact gate to Saul / Livingston.Co-authored-by: Copilot 223556219+Copilot@users.noreply.github.com