Skip to content

feat: support git worktree resources in task inputs (#121)#302

Merged
github-actions[bot] merged 4 commits into
mainfrom
spboyer/issue-121-provide-a-way-to-start-with-a-copy-of-th-3130bf
May 26, 2026
Merged

feat: support git worktree resources in task inputs (#121)#302
github-actions[bot] merged 4 commits into
mainfrom
spboyer/issue-121-provide-a-way-to-start-with-a-copy-of-th-3130bf

Conversation

@spboyer

@spboyer spboyer commented May 26, 2026

Copy link
Copy Markdown
Member

Summary

Adds inputs.repos to task YAML so a task can materialize a clean copy of a local git repository into its per-run workspace via git worktree add --detach, plus an inputs.workdir field 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 in azure-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

inputs:
  prompt: "Explain the repo layout"
  workdir: my-repo          # optional; relative path inside workspace
  repos:
    - type: worktree        # required; only "worktree" today
      source: /path/to/local/clone
      commit: main          # optional; SHA, branch, or tag (HEAD if omitted)
      dest: my-repo         # optional; subdir under workspace
Field Required Notes
type yes Materialization strategy. Only worktree today.
source yes Local git repo to source from.
commit no Defaults to HEAD. Branch/tag names use --detach so they don't conflict with the source checkout.
dest no Relative subdir under workspace; defaults to root. Must not contain traversal.

Implementation

  • internal/models/testcase.go — new GitResource + Repos, WorkDir on TaskStimulus, with Validate() rejecting absolute / traversal dest and unsupported type.
  • internal/execution/engine.goExecutionRequest.GitResources / WorkDir, plus ResolveWorkDir() with bounded filepath.Rel traversal check.
  • internal/execution/git.go (new) — GitResource interface + CloneGitResources(ctx, repos, workspace). gitWorktreeAdd shells out to git worktree add --detach; cleanup uses git worktree remove --force. Partial setups roll back on error.
  • internal/execution/copilot.go — wires CloneGitResources into setupWorkspace, tracks resources under the existing workspace mutex, runs Cleanup BEFORE os.RemoveAll(workspace) in doShutdown, and applies WorkDir via ResolveWorkDir when setting WorkingDirectory on 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 — forwards Repos and WorkDir from the stimulus to ExecutionRequest.

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, missing source rejected, traversal dest rejected, unsupported type rejected, no-dest materialization at workspace root, ResolveWorkDir happy + traversal + absolute.
  • internal/models/git_resource_test.go — YAML round-trip for repos + workdir, missing source rejected, unsupported type rejected, workdir traversal rejected, Validate() accepts clean dest / rejects absolute dest.

Documentation impact

  • README.md — new ## Git Repository Resources section under the eval-spec docs (YAML example, field table, cleanup behavior, out-of-scope notes).
  • schemas/task.schema.json — added inputs.repos ($ref to new gitResource def) and inputs.workdir; gitResource requires type (enum ["worktree"]) and source.
  • docs/GUIDE.md — new ### Testing Against a Local Git Repo subsection under Advanced Usage.
  • site/src/content/docs/reference/schema.mdx — added ### repos and ### workdir to the inputs section 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 placeholder SOURCE_REPO_PATH. Listed in examples/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"):

  • HTTPS / SSH clone strategies (would let source: be a URL).
  • Git submodules and Git LFS in the materialized checkout.
  • Auto-detecting "the repo this test is running in" without an explicit source.

Risk mitigations baked in

  • Branch/tag names always check out with --detach so they don't trip "already checked out at …" against the source repo.
  • Git-resource cleanup is registered before os.RemoveAll(workspace) so a crash mid-test doesn't leak entries in the source repo's .git/worktrees/.
  • dest and workdir are validated with bounded filepath.Rel checks (reject .. escapes and absolute paths) at parse time and at execution time.

Review

Per .squad/team.md this 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

Copilot AI review requested due to automatic review settings May 26, 2026 16:08
@github-actions github-actions Bot enabled auto-merge (squash) May 26, 2026 16:10

Copilot AI 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.

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.repos and inputs.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

Comment thread internal/execution/git.go Outdated
Comment thread internal/execution/git.go Outdated
Comment thread internal/execution/git.go Outdated
Comment thread internal/execution/git_test.go Outdated
Comment thread README.md
Comment thread site/src/content/docs/reference/schema.mdx
Comment thread internal/models/testcase.go
Comment thread internal/models/testcase.go
Comment thread internal/execution/engine.go
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>
@spboyer spboyer force-pushed the spboyer/issue-121-provide-a-way-to-start-with-a-copy-of-th-3130bf branch from dd2f77e to 72fd00f Compare May 26, 2026 16:20
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>
Copilot AI review requested due to automatic review settings May 26, 2026 16:38

Copilot AI 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.

Copilot's findings

Comments suppressed due to low confidence (2)

internal/execution/copilot.go:566

  • --keep-workspace is 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-workspace is meant to preserve the workspace for debugging, but Shutdown always calls Cleanup on 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

Comment thread internal/execution/git.go Outdated
Comment thread internal/execution/git.go
Comment thread internal/execution/git.go Outdated
Comment thread internal/models/testcase.go
Comment thread internal/models/testcase.go
Comment thread internal/execution/engine.go
Comment thread internal/execution/git_test.go
spboyer and others added 2 commits May 26, 2026 12:48
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>
Copilot AI review requested due to automatic review settings May 26, 2026 17:19
@github-actions github-actions Bot merged commit 23e9dba into main May 26, 2026
7 of 8 checks passed
@spboyer spboyer removed the request for review from Copilot May 26, 2026 17:42
@spboyer spboyer mentioned this pull request Jun 6, 2026
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.

2 participants