docs: subscription auth is the primary sandcastle flow, API key is fallback

This commit is contained in:
2026-05-13 09:31:33 +02:00
parent 4e1167e390
commit e734a9e7a1
4 changed files with 72 additions and 31 deletions

View File

@@ -54,7 +54,22 @@ CMS_URL=http://localhost:3001
# --- Optional: sandcastle dispatch (only when running `pnpm work dispatch --execute`) --- # --- 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= # ANTHROPIC_API_KEY=
# OPENAI_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= # GITHUB_TOKEN=
# Sandbox provider (default: docker; alternatives: podman, vercel, daytona)
# SANDCASTLE_PROVIDER=docker # SANDCASTLE_PROVIDER=docker

View File

@@ -1,11 +1,20 @@
# Anthropic Claude API # .sandcastle/.env — runtime tokens for sandcastle dispatch.
ANTHROPIC_API_KEY= # Copy to .sandcastle/.env (gitignored) and fill what you need.
#
# Most developers don't need ANY of these if they've run `claude login` on
# the host — sandcastle mounts ~/.claude/ into the sandbox by default.
# OpenAI / Codex # Anthropic API key (fallback when no host Claude Code session exists)
OPENAI_API_KEY= # ANTHROPIC_API_KEY=
# GitHub (for PR creation by the orchestrator) # OpenAI / Codex (alternative)
GITHUB_TOKEN= # OPENAI_API_KEY=
# Sandcastle's own configuration — sandbox provider (docker | podman | vercel | custom) # GitHub access for orchestrator-created PRs
# GITHUB_TOKEN=
# Override Claude creds path (default: ~/.claude/)
# SANDCASTLE_CLAUDE_CREDS_DIR=
# Sandbox provider (docker / podman / vercel / daytona)
SANDCASTLE_PROVIDER=docker SANDCASTLE_PROVIDER=docker

View File

@@ -50,12 +50,21 @@ Concretely:
orchestrator does NOT mutate state in v1 it prints suggested mutations orchestrator does NOT mutate state in v1 it prints suggested mutations
for the human to apply. for the human to apply.
5. **Two modes:** `pnpm work dispatch` (planning, no agent invoked) and 5. **Two modes:** `pnpm work dispatch` (planning, no agent invoked) and
`pnpm work dispatch --execute` (real sandcastle call, requires `pnpm work dispatch --execute` (real sandcastle call, requires auth see
`ANTHROPIC_API_KEY` or `OPENAI_API_KEY`). point 7).
6. **Reviewer agent verifies generator-first.** Hand-rolled output that should 6. **Reviewer agent verifies generator-first.** Hand-rolled output that should
have been a `pnpm turbo gen <kind>` invocation is grounds for rejection. have been a `pnpm turbo gen <kind>` invocation is grounds for rejection.
7. **Bring-your-own-key for cost control.** No bundled API key. Agents only 7. **Bring-your-own-auth.** Two paths are supported, in priority order:
dispatch when the operator explicitly provides credentials. - **Subscription (primary)** bind-mount the host's `~/.claude/` into the
sandbox. Claude Code CLI inside the sandbox uses the host's logged-in
subscription session. Zero per-task token spend for Pro/Max subscribers.
Path overridable via `SANDCASTLE_CLAUDE_CREDS_DIR` env var.
- **API key (fallback)** `ANTHROPIC_API_KEY` or `OPENAI_API_KEY` passed
through to the sandbox env. Used when no host creds directory exists.
- The resolver (`resolveClaudeAuth` in `scripts/work/dispatch.mjs`) picks
automatically with subscription always preferred. Sandcastle's own issue
#191 documents that subscription support won't be added natively;
this mount-based pattern is our workaround promoted to first-class.
8. **Per-task max-attempts honoured (v2).** Each task's frontmatter may carry 8. **Per-task max-attempts honoured (v2).** Each task's frontmatter may carry
`max-attempts: N` to bound the implementerreviewer retry loop. Default 3. `max-attempts: N` to bound the implementerreviewer retry loop. Default 3.

View File

@@ -87,7 +87,7 @@ pnpm work ready # all ready stories
pnpm work blocked # blocked stories + what they wait on pnpm work blocked # blocked stories + what they wait on
pnpm work rebuild-state # regenerate docs/work/_state.json pnpm work rebuild-state # regenerate docs/work/_state.json
pnpm work dispatch # print next dispatch plan pnpm work dispatch # print next dispatch plan
pnpm work dispatch --execute # invoke sandcastle (requires ANTHROPIC_API_KEY) pnpm work dispatch --execute # invoke sandcastle (subscription or API key — see runbook)
``` ```
--- ---
@@ -139,12 +139,15 @@ Copy `.env.example` to `.env` and fill what you need. NOT every variable is requ
### Optional — sandcastle dispatch (only when running `pnpm work dispatch --execute`) ### Optional — sandcastle dispatch (only when running `pnpm work dispatch --execute`)
| Var | Why | Auth is resolved automatically. Subscription (via `~/.claude/`) is the primary path; API key is the fallback.
| --------------------- | ------------------------------------------------- |
| `ANTHROPIC_API_KEY` | Claude API key (sandcastle's default agent) | | Var | Why |
| `OPENAI_API_KEY` | OpenAI/Codex alternative | | ----------------------------- | ---------------------------------------------------------------------------------- |
| `GITHUB_TOKEN` | GitHub access for PR creation by the orchestrator | | `ANTHROPIC_API_KEY` | Claude API key — fallback when no `~/.claude/` present; not needed for subscribers |
| `SANDCASTLE_PROVIDER` | `docker` (default) / `podman` / `vercel` | | `OPENAI_API_KEY` | OpenAI/Codex alternative (fallback) |
| `SANDCASTLE_CLAUDE_CREDS_DIR` | Override host Claude creds path (default: `~/.claude/`) |
| `GITHUB_TOKEN` | GitHub access for PR creation by the orchestrator |
| `SANDCASTLE_PROVIDER` | `docker` (default) / `podman` / `vercel` |
--- ---
@@ -239,17 +242,14 @@ For the full design see `docs/architecture/agent-first-workflow-and-conformance.
### Prerequisites ### Prerequisites
1. **Docker running** sandcastle uses Docker for the sandbox by default. `docker info` should succeed. 1. **Docker running** sandcastle uses Docker for the sandbox by default. `docker info` should succeed.
2. **Agent API key** set ONE of: 2. **Authentication — pick ONE:**
- `ANTHROPIC_API_KEY` (recommended; sandcastle's default agent is `claudeCode`) - **Recommended: Claude Pro / Max subscription.** Run `claude login` once on the host. Sandcastle's sandbox bind-mounts your `~/.claude/` into the container so the Claude Code CLI inside the sandbox uses your subscription session. Zero per-task token spend for subscribers.
- `OPENAI_API_KEY` (alternative) - **Alternative: API key.** Set `ANTHROPIC_API_KEY` or `OPENAI_API_KEY` in your environment. Falls back automatically when `~/.claude/` is absent.
- **Override the creds path** via `SANDCASTLE_CLAUDE_CREDS_DIR` if your Claude Code config lives somewhere non-standard.
3. **GitHub token** (optional) `GITHUB_TOKEN` if you want the orchestrator to create PRs. 3. **GitHub token** (optional) `GITHUB_TOKEN` if you want the orchestrator to create PRs.
4. **`.sandcastle/` config present** already in tree: 4. **`.sandcastle/` config present** already in tree:
- `Dockerfile` node:22 + pnpm sandbox image - `Dockerfile` node:22 + pnpm + Claude Code CLI; reads creds from `~/.claude/` inside the container
- `prd-eliciter.prompt.md` interviews humans to draft PRDs - `prd-eliciter.prompt.md`, `adr-eliciter.prompt.md`, `decomposer.prompt.md`, `implementer.prompt.md`, `reviewer.prompt.md` the five role prompts
- `adr-eliciter.prompt.md` same shape, for infrastructure decisions
- `decomposer.prompt.md` PRD epic + stories with generator-first task lists
- `implementer.prompt.md` executes one task; runs all 5 gates before committing
- `reviewer.prompt.md` reviews implementer's diff against AC + scope
### The dispatch flow ### The dispatch flow
@@ -305,6 +305,11 @@ This is safe to run anywhere — it never invokes Sandcastle.
**Step 2 — Execute** **Step 2 — Execute**
```bash ```bash
# Subscription mode (recommended):
claude login # one-time, host
pnpm work dispatch --execute # uses ~/.claude/
# API-key mode (fallback):
ANTHROPIC_API_KEY=sk-ant-... pnpm work dispatch --execute ANTHROPIC_API_KEY=sk-ant-... pnpm work dispatch --execute
``` ```
@@ -361,8 +366,8 @@ You (the human) then:
### Troubleshooting Sandcastle ### Troubleshooting Sandcastle
**`✗ --execute requires ANTHROPIC_API_KEY or OPENAI_API_KEY in env.`** **`✗ --execute requires either: 1. Claude Code logged in on host ... 2. ANTHROPIC_API_KEY ...`**
Set one. The default agent is Claude. No auth resolved. Run `claude login` to enable subscription mode (recommended), OR set `ANTHROPIC_API_KEY` (fallback). Override the host creds path via `SANDCASTLE_CLAUDE_CREDS_DIR`.
**`Error: Cannot find module '@ai-hero/sandcastle'`** **`Error: Cannot find module '@ai-hero/sandcastle'`**
Run `pnpm install`. Sandcastle is a dev dependency at the workspace root. Run `pnpm install`. Sandcastle is a dev dependency at the workspace root.
@@ -381,6 +386,9 @@ You (the human) then:
**Cost control** each dispatch typically uses 50K200K agent tokens depending on task complexity. The orchestrator does NOT cap retries; if you want to limit, set `max-attempts: 1` in the task's frontmatter (the orchestrator respects this in v2 for now, just don't re-run dispatch after a reject). **Cost control** each dispatch typically uses 50K200K agent tokens depending on task complexity. The orchestrator does NOT cap retries; if you want to limit, set `max-attempts: 1` in the task's frontmatter (the orchestrator respects this in v2 for now, just don't re-run dispatch after a reject).
**Sandbox boots but Claude Code inside it says "Not authenticated" / "API key required"**
The host `~/.claude/` mount didn't make it into the sandbox, OR your local Claude Code session expired. On the host, run `claude` once to confirm your session is live, then re-dispatch. If you're on Linux + SELinux, the mount may have been blocked check the sandcastle output for SELinux warnings; set `selinuxLabel: "z"` or `false` in dispatch.mjs's docker opts if needed.
### Cost-aware variant: planning-only loop ### Cost-aware variant: planning-only loop
If you want sandcastle's structure without the agent spend, use planning mode + manual execution: If you want sandcastle's structure without the agent spend, use planning mode + manual execution:
@@ -417,8 +425,8 @@ This gives you the same DAG-aware "what's next?" without invoking any agent. Use
**Tests fail in `@repo/turbo-generators` with Vitest worker timeouts** **Tests fail in `@repo/turbo-generators` with Vitest worker timeouts**
Known flaky on slow machines. Re-run; if persistent, increase the `turbo-generators` package's vitest `testTimeout`. Known flaky on slow machines. Re-run; if persistent, increase the `turbo-generators` package's vitest `testTimeout`.
**`pnpm work dispatch --execute` errors with "ANTHROPIC_API_KEY required"** **`pnpm work dispatch --execute` errors with "requires either: 1. Claude Code logged in..."**
You're trying to run the sandcastle orchestrator without an agent API key. Set the env var, or run `pnpm work dispatch` (no flag) to just print the plan. No auth source found. Run `claude login` (subscription mode, recommended), or set `ANTHROPIC_API_KEY` (fallback). Run `pnpm work dispatch` (no flag) to just print the plan without auth.
--- ---