Previously the orchestrator ran exactly one implementer + reviewer pair,
printed "(Automatic state mutation by the orchestrator is v2.)", and
exited — the human had to tick the bullet, flip story status, rebuild
state, and re-invoke for every slice. V2 closes the loop:
- Parses the JSON the implementer + reviewer prompts ask the agents to
emit (`parseAgentJson` — tolerates both ```json fenced and bare
trailing { ... } shapes). The reviewer's `decision` and the
implementer's `status` are the orchestrator's discriminators.
- On approve: ticks the bullet in `_story.md` and writes it back. If
the story now has zero unchecked bullets, flips its frontmatter
`status: in-progress → done`; if all sibling stories are also done,
flips the epic's frontmatter the same way. Commits the mutation on
the host as a separate `chore(work): tick/finish ...` commit so the
implementer's slice commit stays clean. `_state.json` regenerates
via the existing pre-commit `rebuild-state` hook.
- On reject: re-dispatches the implementer with the reviewer's notes
appended to TASK_FILE_CONTENT, bounded by SANDCASTLE_MAX_ATTEMPTS
(default 3). On the (max+1)th reject the loop exits 1 with the last
notes printed.
- After every approved slice, calls findNextTask again and dispatches
the next ready bullet — including across story boundaries (the
state-builder treats any non-done story with satisfied deps as
ready, so flipping story 01 to done unblocks story 02 automatically).
- Flags: `--once` (legacy single-slice behavior) and `--max-tasks N`
bound the loop. Default is unlimited — matches the
continuous-execution preference.
Auth/sandbox setup is now pulled out of the per-iteration path so the
loop reuses one sandbox across slices.
91 lines
3.3 KiB
Plaintext
91 lines
3.3 KiB
Plaintext
# =============================================================================
|
|
# Environment variables — copy this file to .env and fill in your values.
|
|
# See docs/guides/runbook.md for the full reference.
|
|
# =============================================================================
|
|
|
|
# --- Required for `pnpm dev` ---
|
|
|
|
# Postgres connection. Matches `docker compose up -d` default.
|
|
DATABASE_URL=postgresql://postgres:postgres@localhost:5433/template
|
|
|
|
# Payload CMS encryption key. Any random 32+ char string in dev.
|
|
PAYLOAD_SECRET=replace-with-a-random-32-char-string
|
|
|
|
# --- Optional: app URLs (defaults work in dev) ---
|
|
|
|
NEXT_PUBLIC_APP_URL=http://localhost:3000
|
|
CMS_URL=http://localhost:3001
|
|
|
|
# Force dev-seed binders (mock repos) regardless of NODE_ENV. Useful for
|
|
# running pnpm dev without Payload booted.
|
|
# USE_DEV_SEED=true
|
|
|
|
# --- Optional: Sentry observability ---
|
|
# Leaving these unset → instrumentation falls back to the no-op tracer/logger.
|
|
# Set the DSN for any app you want OTel + Sentry on.
|
|
|
|
# WEB_NEXT_SENTRY_DSN=
|
|
# NEXT_PUBLIC_WEB_NEXT_SENTRY_DSN=
|
|
# CMS_SENTRY_DSN=
|
|
# WEB_TANSTACK_SENTRY_DSN=
|
|
# VITE_WEB_TANSTACK_SENTRY_DSN=
|
|
|
|
# Source-map upload at build time (production only).
|
|
# SENTRY_AUTH_TOKEN=
|
|
# SENTRY_ORG=
|
|
# SENTRY_PROJECT_WEB_NEXT=
|
|
# SENTRY_PROJECT_CMS=
|
|
# SENTRY_PROJECT_WEB_TANSTACK=
|
|
|
|
# OTel trace sample rate (0.0 = none, 1.0 = all). 0.1 recommended in dev.
|
|
# SENTRY_TRACES_SAMPLE_RATE=0.1
|
|
# SENTRY_ENVIRONMENT=development
|
|
|
|
# --- Optional: git commit SHA for releases ---
|
|
|
|
# VERCEL_GIT_COMMIT_SHA=
|
|
# NEXT_PUBLIC_VERCEL_GIT_COMMIT_SHA=
|
|
# VITE_GIT_COMMIT_SHA=
|
|
|
|
# --- Optional: core-audit (only when `gen core-package audit` is scaffolded) ---
|
|
|
|
# Salt for GDPR pseudonymisation. PRODUCTION MUST set this to a stable secret.
|
|
# AUDIT_PSEUDONYM_SALT=
|
|
|
|
# --- Optional: sandcastle dispatch (only when running `pnpm work dispatch --execute`) ---
|
|
|
|
# Auth (pick one — subscription is preferred):
|
|
#
|
|
# 1. Subscription mode (recommended for Pro/Max subscribers):
|
|
# Run `claude login` on the host once. Sandcastle bind-mounts ~/.claude/
|
|
# into the sandbox so the container's Claude Code CLI uses your session.
|
|
# Zero per-task token spend. No env var needed.
|
|
#
|
|
# 2. API-key mode (fallback when no host creds available):
|
|
# ANTHROPIC_API_KEY=
|
|
# OPENAI_API_KEY=
|
|
|
|
# Override the path to host Claude Code creds (default: ~/.claude/)
|
|
# SANDCASTLE_CLAUDE_CREDS_DIR=
|
|
|
|
# GitHub access (optional — for orchestrator-created PRs)
|
|
# GITHUB_TOKEN=
|
|
|
|
# Sandbox provider (default: docker; alternatives: podman, vercel, daytona)
|
|
# SANDCASTLE_PROVIDER=docker
|
|
|
|
# Agent iteration budgets. Sandcastle's `run()` cuts the agent off after N
|
|
# iterations (one iteration = one tool-use + response round-trip). The
|
|
# repo's defaults are tuned for typical work; bump if an agent gets cut
|
|
# mid-commit (you'll see "Reached max iterations" in .sandcastle/logs/).
|
|
#
|
|
# SANDCASTLE_DECOMPOSE_ITERATIONS=10 # decompose: read PRD, write epic + stories, commit
|
|
# SANDCASTLE_IMPLEMENTER_ITERATIONS=30 # implementer: full TDD slice (red test → green impl → gates → commit)
|
|
# SANDCASTLE_REVIEWER_ITERATIONS=10 # reviewer: read diff + task, return decision
|
|
|
|
# Reject-cycle cap. After this many reviewer rejects on the same slice, the
|
|
# dispatch loop gives up on that slice and exits 1 with the last rejection
|
|
# notes printed. Bump for tricky slices; lower for fast-feedback iteration.
|
|
#
|
|
# SANDCASTLE_MAX_ATTEMPTS=3
|