Skip to content

feat(autofix): runnable project env (native + docker-compose) with build/test gating#5

Open
pat-lewczuk wants to merge 2 commits into
mainfrom
feat/ephemeral-run-env
Open

feat(autofix): runnable project env (native + docker-compose) with build/test gating#5
pat-lewczuk wants to merge 2 commits into
mainfrom
feat/ephemeral-run-env

Conversation

@pat-lewczuk

Copy link
Copy Markdown
Member

Summary

Gives autofix runs a real install → build → test feedback loop on the checked-out worktree, so the fix loop iterates against actual build/test output before opening a PR — and adds a per-run Docker Compose environment for repos that need services (db, redis, …) to build or test.

Both live paths (@cezar/runner and the cloud dispatcher) funnel through AutofixOrchestrator.processIssueViaEngine, so the new RunEnv is wired in there once and works for both.

What's in it

Core — new provision/ module (one interface, two impls)

  • RunEnv interface: install() / build() / test() / run() / dispose().
  • NativeShellEnv — host shell in the worktree.
  • DockerComposeEnv — per-run compose -p cezar-<runId> project (isolated network/volumes), worktree bind-mounted into the app service, deps persist back through the mount, down -v --remove-orphans on dispose. Override file is written outside the worktree so autosave / git add -A can't catch it; --project-directory keeps the base file's build contexts resolving.
  • createRunEnvauto-detects the compose file (overridable path), resolves the app service when unset, and falls back to native when Docker is absent (so the managed cloud path still works).

Core — workflow engine

  • New shell-check step kind. A gated build/test failure inside the fix loop fails-retriable → loops back to fix carrying the failure tail as retry notes; review only runs once build+test pass. No RunEnv / an unset command ⇒ the step is a no-op, so triage and env-less runs are byte-identical to before.
  • Autofix workflow: install (before root-cause) + build + test (in the fix loop), plus blackboard fields for the results.
  • Orchestrator builds the RunEnv from autofix.projectEnv, threads it through the engine on both the autofix and ci-followup paths, and disposes it.

Config + GUI

  • autofix.projectEnv (kind / install / build / test / compose {file,service,workdir} / envVars / gateOn{Install,Build,Test}).
  • Settings → Configuration gains a Project environment section, persisted to workspaces.config.autofix.projectEnv (no migration) and deep-merged in loadWorkspaceConfig.

Notes

  • Compose runs require a Docker daemon ⇒ self-hosted runner only. The cloud dispatch path auto-falls-back to native. The runner already caps parallelism via --concurrency (default 1); per-run worktrees + unique compose project names make >1 safe.
  • Dev server + a read-url agent tool are intentionally deferred to a follow-up (Phase 3).

Test plan

  • yarn typecheck — all workspaces clean
  • yarn build — all workspaces incl. Next.js
  • yarn workspace @cezar/core run test — 157 pass (incl. new provision unit tests + 5 engine shell-check scenarios: pass / gated-failure-loops-to-fix / loop-exhausted-fails / ungated / no-env)
  • Manual: configure projectEnv (native + compose) on a real workspace and watch the cockpit show install/build/test steps

🤖 Generated with Claude Code

…ild/test gating

Give autofix runs a real install/build/test feedback loop on the checked-out
worktree, so the fix loop iterates against actual build/test output before
opening a PR — and support a per-run docker-compose environment for repos that
need services (db, redis, …) to build or test.

Core:
- New `provision/` module: `RunEnv` interface with `NativeShellEnv` (host shell)
  and `DockerComposeEnv` (per-run `compose -p cezar-<run>` project; worktree
  bind-mounted; deps persist via the mount; `down -v` on dispose). `createRunEnv`
  resolves kind (`auto` detects a compose file; falls back to native when Docker
  is absent so the managed cloud path still works) and the compose service.
- New `shell-check` workflow step kind: runs install/build/test via the run's
  `RunEnv`, streams output, and gates the run. A gated build/test failure inside
  the fix loop fails-retriable, carrying the failure tail back as fix retry
  notes; review only runs once build+test pass. No `RunEnv` / unset command ⇒
  the step is a no-op, so triage and env-less runs are unchanged.
- autofix workflow: install (before root-cause) + build + test (in the fix
  loop) shell-checks; new blackboard fields for the results.
- Orchestrator builds the `RunEnv` from `autofix.projectEnv`, threads it through
  the engine on both the autofix and ci-followup paths, and disposes it.
- Config: `autofix.projectEnv` (kind / install / build / test / compose
  {file,service,workdir} / envVars / gateOn{Install,Build,Test}).

GUI:
- Settings → Configuration gains a "Project environment" section; persisted to
  `workspaces.config.autofix.projectEnv` and deep-merged in loadWorkspaceConfig.

Compose-based runs require a Docker daemon ⇒ self-hosted runner only; the runner
already caps parallelism via `--concurrency` (default 1) and per-run worktrees +
unique compose project names make >1 safe.

Tests: provision unit tests (detect/native/factory) + engine shell-check tests
(pass, gated-failure-loops-to-fix, loop-exhausted-fails, ungated, no-env).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@supabase

supabase Bot commented May 20, 2026

Copy link
Copy Markdown

This pull request has been ignored for the connected project mycifnmzgmaklyhcyyyw because there are no changes detected in supabase directory. You can change this behaviour in Project Integrations Settings ↗︎.


Preview Branches by Supabase.
Learn more about Supabase Branching ↗︎.

Boot the project's dev server once after install so the agent can probe the
running application while diagnosing and verifying its fix.

Core:
- `RunEnv` gains `startDevServer()` → `DevServerHandle { url, ready, stop }`,
  plus a `waitForHttp` readiness probe. `NativeShellEnv` spawns the command
  detached and SIGTERMs the process group on stop/dispose; `DockerComposeEnv`
  publishes the container port to an ephemeral host port via the override file,
  `up -d`s the service, and resolves the mapped host port with `compose port`
  (`parseHostPort`). Both are torn down by `dispose()`.
- New `dev-server` workflow step kind: boots the server, records the URL on the
  run, and (informational by default) keeps going if it's slow to come up;
  `gate: true` fails the run if it never responds. No env / disabled ⇒ no-op.
- The engine threads the dev-server URL to later agent steps: appends the URL to
  the step prompt and adds `curl` to the bash allowlist, so the agent can read
  the live app over its existing Bash tool (backend-agnostic "read-url" — a
  bespoke custom tool would need a per-backend MCP server).
- autofix workflow runs `dev-server` after install, before root-cause; new
  `devServerUrl` blackboard field.
- Config: `autofix.projectEnv.devServer { enabled, command, port, readyPath,
  readyTimeoutSec }`.

GUI: dev-server fields in the Settings → Project environment section; persisted
to `workspaces.config` and deep-merged in loadWorkspaceConfig.

Deferred: headless screenshots (needs a browser dep) — left for a follow-up.

Tests: real NativeShellEnv boot/probe/teardown against a throwaway Node server;
engine dev-server scenarios (URL+curl injection ordering, unconfigured no-op,
ungated boot failure). 162 core tests pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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