Skip to content

feat: support automation worktree placement#1004

Merged
Astro-Han merged 6 commits into
devfrom
codex/i950-automation-pr5-worktree
May 31, 2026
Merged

feat: support automation worktree placement#1004
Astro-Han merged 6 commits into
devfrom
codex/i950-automation-pr5-worktree

Conversation

@Astro-Han

@Astro-Han Astro-Han commented May 30, 2026

Copy link
Copy Markdown
Owner

Summary

Adds issue #950 PR5 backend worktree placement for automations:

  • enables where.worktree for fresh automations on git projects
  • normalizes worktree placement strings into stable managed slugs
  • rejects invalid placements, non-git worktree placement, and continue + worktree
  • prepares/reuses a managed worktree before prompt execution and binds the run session execution context to that worktree
  • keeps automation event fixtures and SDK type fixtures covering the worktree placement shape

Why

PR1 froze the automation contract with where.worktree present but rejected until PR5. PR2-4 made runs, scheduling, and persistence real. This PR flips that backend placement contract to supported so PR6 can build the Automations panel and PR7 can build create/form/template flows against a stable backend behavior.

Related Issue

Closes part of #950. This is PR5 in the agreed 7-PR breakdown.

Human Review Status

Pending

Review Focus

Please focus on the placement contract (where.worktree as a normalized managed slug), the fresh-only validation boundary, and the runner path that prepares a worktree and updates the automation session execution context before prompting.

Risk Notes

Behavior/platform: this touches git worktree creation/reset and session execution directories. It relies on the existing Worktree service canonicalization/reset behavior and leaves broader platform coverage to CI.

Lifecycle: repeated runs with the same placement release prior automation session worktree bindings before resetting the managed worktree; non-automation sessions remain protected by the existing Worktree reset guard.

Skipped checklist items:

  • Visible UI check: no visible UI or copy changed.

How To Verify

Diff whitespace check: git diff --check -> passed
Automation regression bundle: OPENCODE_DB=:memory: bun test test/server/automation-runner.test.ts test/server/automation-scheduler.test.ts test/server/automation-routes.test.ts test/server/automation-event-fixtures.test.ts test/server/event-replay.test.ts test/tool/automate.test.ts --timeout 30000 -> 134 passed, 0 failed
Opencode typecheck: OPENCODE_DB=:memory: bun --cwd packages/opencode typecheck -> passed
SDK typecheck: bun run typecheck in packages/sdk/js -> passed
SDK build: bun run build in packages/sdk/js -> passed; generated SDK output unchanged except existing checked-in type fixture edit

Screenshots or Recordings

Not applicable. No visible UI changes.

Checklist

How to use this checklist:

  • Tick a box by replacing [ ] with [x]. Do not edit, add, or remove items.
  • The bot-applied label items can only be honestly ticked AFTER the PR is opened and the labeler / priority-triage bots have run — return to the PR description and tick them then.
  • Most items are required. The few that are conditional are explicitly marked (conditional); for those, leave unticked if they truly do not apply and explain why in Risk Notes. All other items must be ticked before requesting human review.
  • Type label — this PR carries exactly one of bug, enhancement, task, documentation. Type labels are author-added; the labeler bot does NOT assign them. Add the label in the GitHub UI, then tick this.
  • Routing labels — this PR carries at least one of app, ui, platform, harness, ci. The labeler bot assigns these on PR open based on changed paths. Confirm the bot's choice (or override if wrong), then tick this.
  • Priority label — this PR carries exactly one of P0, P1, P2, P3. The priority-triage bot suggests one on PR open. Confirm or override, then tick this.
  • Human Review Status above is set to Pending, Approved by @<reviewer>, or Not required: <reason> (default is Pending; "not required" is restricted to bot-authored low-risk PRs).
  • I linked the related issue, or stated in Summary why there is no issue.
  • I described the review focus and any meaningful risks.
  • I replaced the example block in How To Verify with the real verification steps and the key result for each.
  • I did not introduce unrelated refactors, dependencies, generated files, or file changes beyond the stated scope.
  • (conditional) I manually checked visible UI or copy changes when needed, with screenshots or recordings. Leave unticked only if no visible UI or copy changed.
  • (conditional) I considered macOS and Windows impact for platform, packaging, updater, signing, paths, shell, or permissions changes. Leave unticked only if no platform/packaging surface was touched.
  • (conditional) I called out docs, release notes, dependencies, permissions, credentials, deletion behavior, generated content, or local file changes when relevant. Leave unticked only if none of those surfaces was touched.
  • I reviewed the final diff for unrelated changes and suspicious dependency changes.
  • I am targeting dev, and my PR title and commit messages use Conventional Commits in English.

Summary by CodeRabbit

  • New Features

    • Added worktree placement support for automation workflows with standardized naming conventions.
    • Introduced deterministic worktree creation with optional exact naming.
  • Bug Fixes

    • Enhanced validation to reject invalid worktree placements and prevent incompatible configuration combinations.
    • Prevented accidental resets of worktrees actively bound to sessions.
    • Improved automation execution context management for reliable worktree handling during runs.

@Astro-Han Astro-Han added enhancement New feature or request P1 High priority app Application behavior and product flows harness Model harness, prompts, tool descriptions, and session mechanics labels May 30, 2026
@github-actions github-actions Bot removed the app Application behavior and product flows label May 30, 2026

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggested priority: P2 (includes non-doc, non-test paths outside the low-risk bucket).

P1/P0 are reserved for maintainer confirmation. Please relabel manually if this is a release blocker, security issue, data-loss risk, or updater/runtime failure.

@coderabbitai

coderabbitai Bot commented May 30, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@Astro-Han, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 33 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 7f22c357-8aab-4840-85d3-0d2ed58e2642

📥 Commits

Reviewing files that changed from the base of the PR and between ace0483 and 48b73a8.

📒 Files selected for processing (5)
  • packages/opencode/src/automation/runner.ts
  • packages/opencode/src/worktree/index.ts
  • packages/opencode/test/project/worktree.test.ts
  • packages/opencode/test/server/automation-routes.test.ts
  • packages/opencode/test/server/automation-runner.test.ts
📝 Walkthrough

Walkthrough

This PR implements deterministic worktree placement for automation runs by introducing normalization rules, service-layer infrastructure for worktree creation and reset, and automated session context provisioning. All automation route handlers now preserve request-scoped instance context during scheduler settlement to maintain clean state management.

Changes

Automation Worktree Placement & Service Integration

Layer / File(s) Summary
Automation worktree normalization and validation
packages/opencode/src/automation/index.ts, packages/opencode/src/automation/fixtures.ts, packages/sdk/js/src/v2/event-types.test-d.ts, packages/opencode/test/server/automation-event-fixtures.test.ts
Introduces normalizeWorktreePlacement to slugify worktree strings and getWriterKey to derive writer-lock keys from normalized where.worktree (or fallback to projectID). Validation now rejects invalid placements, forbids continue context with worktree, and blocks non-git projects. Create/update paths normalize incoming payloads before persistence. Durable active-writer detection and run execution use shared getWriterKey logic instead of inline expressions. Fixtures and type tests updated to reflect worktree placement shape.
Worktree service: createReady and enhanced reset
packages/opencode/src/worktree/index.ts, packages/opencode/test/project/worktree.test.ts, packages/opencode/test/project/worktree-remove.test.ts
New createReady operation supports deterministic naming via exactName flag, restricting name generation to a single attempt when set. Enhanced reset checks Session.findActiveWorktreeBinding before proceeding and throws ResetFailedError if the worktree is actively bound; reconciles registry branch metadata when discovered branch differs; removes Effect.forkIn(scope) from start-script execution. Interface updated with createReady contract; service wiring and public async wrapper added. Tests verify exactName rejection behavior, reset guards against active bindings, start-command completion, and branch metadata refresh.
Automation runner: worktree placement and session context
packages/opencode/src/automation/runner.ts, packages/opencode/test/server/automation-runner.test.ts
Runner adds helpers to release active automation bindings and resolve desired worktree (reset existing or create ready). sessionPromptExecutor resolves worktree placement and enriches session execution context with directory, name, branch, and source metadata. Tests validate fresh worktree execution with correct context, placement reuse across runs with branch updates, and fallback-prevention when placement slug is pre-occupied.
Server routes: request instance preservation and worktree validation
packages/opencode/src/server/instance/automation.ts, packages/opencode/test/server/automation-routes.test.ts, packages/opencode/test/tool/automate.test.ts
Introduces withSettledRequestInstance helper to preserve request-scoped Instance context during scheduler settlement. All route handlers (definitions, runs, pause/resume) are wrapped to capture instance, settle scheduler, and execute inside Instance.restore(...). Tests validate worktree acceptance for git projects, rejection for non-git, slug normalization, prohibition of continue-context with worktree, and invalid placement rejection. Fixture helper now accepts optional git parameter to control temporary project setup.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • Astro-Han/pawwork#960: Introduces the initial where.worktree contract in automation definitions; this PR implements the full normalization, validation, and worktree service integration on top.
  • Astro-Han/pawwork#983: Modifies sessionPromptExecutor and writer-locking semantics; this PR's worktree placement resolution in the runner directly overlaps with writer-key-based run execution behavior.
  • Astro-Han/pawwork#998: Implements durable active-run and writer ownership enforcement; this PR's getWriterKey and active-writer detection logic directly align with that enforcement layer.

Poem

🐰 A rabbit hops through worktree paths so fine,

With slugs and keys that neatly all align,

From runner's weave to server's gentle grasp,

Each session context in the lock's tight clasp! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat: support automation worktree placement' is concise, clear, and directly summarizes the main feature addition: enabling worktree placement support for automations.
Description check ✅ Passed The PR description follows the template with all required sections present and substantially completed: Summary clearly describes what changed and links to issue #950, Why explains the context, Related Issue references the relevant issue, Human Review Status is set to Pending, Review Focus identifies key areas, Risk Notes call out relevant risks and skip reasons, How To Verify includes actual verification results with test outcomes, and the Checklist is mostly complete with most items ticked.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/i950-automation-pr5-worktree

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@Astro-Han Astro-Han added the app Application behavior and product flows label May 30, 2026
@github-actions github-actions Bot removed the app Application behavior and product flows label May 30, 2026

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review

This pull request introduces support for worktree placements in automation definitions, including input normalization, validation rules (such as restricting worktrees to git projects and fresh contexts), and automated worktree preparation during execution. However, a critical issue was identified in the runner's worktree preparation logic: if a worktree is currently in use by an active user session, the runner will still proceed to reset the worktree, leading to silent and permanent data loss of uncommitted user changes. It is recommended to safely check and release bindings, throwing an error if a user session remains active.

Comment thread packages/opencode/src/automation/runner.ts

@Deniwn22 Deniwn22 left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Tested locally and it works. Consider extracting the retry logic into a shared utility — we have similar code in the auth module.

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/opencode/src/worktree/index.ts (1)

371-381: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Reject empty slugs when exactName is set.

If name slugifies to an empty string, base || undefined drops back to random name generation even with exactName: true. That breaks the deterministic-placement contract and reintroduces fallback names for invalid exact placements.

Suggested fix
       const makeWorktreeInfo = Effect.fn("Worktree.makeWorktreeInfo")(function* (name?: string, exactName?: boolean) {
         const ctx = yield* InstanceState.context
         if (ctx.project.vcs !== "git") {
           throw new NotGitError({ message: "Worktrees are only supported for git projects" })
         }

         const root = pathSvc.join(ctx.worktree, ".worktrees", "pawwork")
         yield* fs.makeDirectory(root, { recursive: true }).pipe(Effect.orDie)

         const base = name ? slugify(name) : ""
+        if (exactName && name && !base) {
+          throw new NameGenerationFailedError({ message: "Failed to generate a unique worktree name" })
+        }
         return yield* candidate(root, base || undefined, exactName)
       })
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/opencode/src/worktree/index.ts` around lines 371 - 381, In
makeWorktreeInfo, when slugifying name into base ensure you reject an empty slug
if exactName is true instead of passing undefined to candidate; specifically
after computing const base = name ? slugify(name) : "" check if exactName &&
base === "" and throw a clear error (or return a failure) so deterministic
placement is preserved; update the call to candidate(root, base || undefined,
exactName) to only pass base when non-empty and rely on the new guard to prevent
falling back to random names.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/opencode/src/worktree/index.ts`:
- Around line 464-469: The createReady generator calls makeWorktreeInfo, setup
and then boot but boot currently swallows checkout/bootstrap failures by logging
and returning, allowing createReady to report success for an unprepared
worktree; modify boot (and any code paths it calls) so failures are raised as
Effect failures (e.g., throw or Effect.fail inside the Effect.fn generator)
instead of just logging/returning, so createReady will propagate the error and
not return an Info for an unbootstrapped directory; locate symbols createReady,
boot, setup, makeWorktreeInfo and the CreateInput/exactName usage to update the
error paths accordingly.

---

Outside diff comments:
In `@packages/opencode/src/worktree/index.ts`:
- Around line 371-381: In makeWorktreeInfo, when slugifying name into base
ensure you reject an empty slug if exactName is true instead of passing
undefined to candidate; specifically after computing const base = name ?
slugify(name) : "" check if exactName && base === "" and throw a clear error (or
return a failure) so deterministic placement is preserved; update the call to
candidate(root, base || undefined, exactName) to only pass base when non-empty
and rely on the new guard to prevent falling back to random names.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 87f24225-c351-4a1c-8b17-e8fc801e701e

📥 Commits

Reviewing files that changed from the base of the PR and between ff40869 and ace0483.

📒 Files selected for processing (12)
  • packages/opencode/src/automation/fixtures.ts
  • packages/opencode/src/automation/index.ts
  • packages/opencode/src/automation/runner.ts
  • packages/opencode/src/server/instance/automation.ts
  • packages/opencode/src/worktree/index.ts
  • packages/opencode/test/project/worktree-remove.test.ts
  • packages/opencode/test/project/worktree.test.ts
  • packages/opencode/test/server/automation-event-fixtures.test.ts
  • packages/opencode/test/server/automation-routes.test.ts
  • packages/opencode/test/server/automation-runner.test.ts
  • packages/opencode/test/tool/automate.test.ts
  • packages/sdk/js/src/v2/event-types.test-d.ts

Comment thread packages/opencode/src/worktree/index.ts
@Astro-Han Astro-Han force-pushed the codex/i950-automation-pr5-worktree branch from ace0483 to 2250775 Compare May 31, 2026 04:06
@Astro-Han Astro-Han merged commit f9219cc into dev May 31, 2026
27 checks passed
@Astro-Han Astro-Han deleted the codex/i950-automation-pr5-worktree branch May 31, 2026 10:09
Astro-Han added a commit that referenced this pull request May 31, 2026
Root cause:
- PR #1004 forked the whole worktree start-script task, leaving no handshake for the middle state where the command has spawned but has not exited.
- Windows scheduling exposed callers continuing before command launch, while short test waits misclassified slow runner setup as waiting on command exit.

Changes:
- Add a Deferred launch barrier so worktree create/reset waits only until the configured start command reaches the spawn boundary.
- Keep stdout/stderr draining and process exit tracking in the background for long-lived start commands.
- Add deterministic worktree regression coverage and relax Windows-sensitive automation runner waits.

Verification:
- git diff --check
- bun --cwd packages/opencode test test/project/worktree.test.ts --timeout 30000
- OPENCODE_DB=:memory: bun --cwd packages/opencode test test/server/automation-runner.test.ts --timeout 30000
- bun --cwd packages/opencode test test/project/worktree-remove.test.ts --timeout 30000
- OPENCODE_DB=:memory: bun --cwd packages/opencode typecheck
- PR checks passed, including unit-opencode
- Manual windows-advisory run passed: https://github.com/Astro-Han/pawwork/actions/runs/26711660621

Review:
- XHigh read-only review found no P0/P1/P2 issues and the optional Deferred<void> cleanup was applied.

Follow-up:
- None required.
Astro-Han added a commit that referenced this pull request Jun 2, 2026
Closes the backend gaps left after PR1-5 so the PR6/PR7 frontend slice can
build on a complete, stable automation contract.

Goal / change boundary:
- model { providerID, modelID } now required on every AutomationDefinition;
  runner passes it through so runs no longer depend on the runtime model
  fallback chain (which drifts across restarts).
- variant? optional reasoning/effort, validated against the live provider
  catalog on create/update; invalid variant returns 422 instead of failing
  late in the run.
- stop.kind === "condition" rejected at create/update with structured
  { field: "stop", message: "unsupported_stop_condition" } (scheduler never
  schedules condition stops, so accepting them only leaked dead UI surface).
- Derived fields nextFireAt / nextFires / failureStreak populated at
  create/update and refreshed after every terminal run; scheduler re-publishes
  automation.definition.updated with a bumped revision for global-sync.
- cron validation consolidated into src/automation/cron.ts; scheduler and
  derived both consume it.
- automate tool schema picks up model/variant + Provider-backed create-time
  validation.
- Migration 20260601100000 drops pre-release rows so model can be NOT NULL.

Verification:
- CI green on 5e177ba (unit-opencode flake on an unrelated VCS-routes
  20s timeout cleared on rerun).
- Local: 129 automation tests (routes/runner/scheduler/event-fixtures/tool/
  cron) + 35 session processor tests pass.

Review follow-ups addressed:
- codex: scheduler self-loop guard keyed by id:revision (not id alone).
- P2: recordRunOutcome retries on ConflictError instead of silently dropping
  the run outcome.
- CodeRabbit: 422 create test uses a guaranteed-invalid model; fixed sleeps
  replaced with terminal-state polling.
- Test seam for the ConflictError path moved off the public Automation API
  into an internal __test_hooks module.

Residual risk / deferred (tracked, not blocking PR6):
- needs_user_input / loop_gate run-error codes remain reserved; producing
  them needs prompt-loop semantics changes.
- Session.automationID reverse lookup deferred (session-contract migration).
- stop=condition kept in the schema layer for SDK shape parity but rejected
  at validate time; collapses once a condition evaluator lands.

Linked: issue #950; follows PR #960 #983 #984 #998 #1004; unblocks PR6/PR7.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request harness Model harness, prompts, tool descriptions, and session mechanics P1 High priority

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants