Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions .sandcastle/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# GitHub token used by host-side discovery, claims, comments, pushes, and PRs.
# Required permissions: Contents, Issues, and Pull requests: read/write.
GH_TOKEN=

# Provider key used by SANDCASTLE_PI_MODEL.
OPENAI_API_KEY=
ANTHROPIC_API_KEY=
OPENROUTER_API_KEY=
GEMINI_API_KEY=
ZAI_API_KEY=

# Runner defaults.
SANDCASTLE_GITHUB_REPOSITORY=Whamp/pi-token-burden
SANDCASTLE_DOCKER_IMAGE=pi-token-burden-sandcastle:local
SANDCASTLE_PI_MODEL=anthropic/claude-sonnet-4.5
SANDCASTLE_PI_THINKING=medium
5 changes: 5 additions & 0 deletions .sandcastle/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.env
logs/
worktrees/
cache/
artifacts/
11 changes: 11 additions & 0 deletions .sandcastle/CODING_STANDARDS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Sandcastle agent guardrails

The repository root `AGENTS.md` is authoritative. Child agents must read it before changing files and must use the project’s existing domain language and tests.

- Work only on the claimed issue and deterministic `sandcastle/issue-<n>` branch.
- Use red/green TDD at stable public seams.
- Do not use `any`, unchecked type assertions, or unvalidated external JSON.
- Do not push, create or merge PRs, close issues, or change labels; the runner owns lifecycle operations.
- Implementation and fix agents commit their code. Review agents never edit files.
- Do not report validation success; the runner executes and records validation.
- Preserve unrelated work and avoid speculative cleanup.
36 changes: 36 additions & 0 deletions .sandcastle/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
FROM node:24-bookworm

RUN apt-get update && apt-get install -y --no-install-recommends \
bash \
ca-certificates \
curl \
git \
jq \
ripgrep \
tmux \
&& rm -rf /var/lib/apt/lists/*

# Install GitHub CLI for issue and PR operations.
RUN curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg \
| dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg \
&& echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" \
| tee /etc/apt/sources.list.d/github-cli.list >/dev/null \
&& apt-get update && apt-get install -y --no-install-recommends gh \
&& rm -rf /var/lib/apt/lists/*

# Keep UID/GID aligned for volume ownership.
ARG AGENT_UID=1000
ARG AGENT_GID=1000
RUN groupmod -o -g ${AGENT_GID} node && \
usermod -o -u ${AGENT_UID} -g ${AGENT_GID} -d /home/agent -m -l agent node

# Sandcastle runs on the host; only Pi executes inside the sandbox.
RUN npm install -g @earendil-works/pi-coding-agent@0.80.5

RUN corepack disable && npm install -g pnpm@10.3.0

USER ${AGENT_UID}:${AGENT_GID}
ENV HOME=/home/agent
WORKDIR /home/agent

ENTRYPOINT ["sleep", "infinity"]
53 changes: 53 additions & 0 deletions .sandcastle/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# Sandcastle AFK runner

The checked-in runner drains eligible `ready-for-agent` issues using the routing and orchestration contracts resolved in issues #22–#24.

## Files

- `afk-runner.ts` — thin queue-draining entrypoint.
- `lib/` — validated routing, atomic GitHub claims, and warm-sandbox workflows.
- `prompts/` — research, implementation, parallel review, and fix contracts.
- `Dockerfile` — pinned Pi execution image.
- `.env.example` — host and sandbox environment variables.

## Setup

```bash
cp .sandcastle/.env.example .sandcastle/.env
# Fill GH_TOKEN and the provider key selected by SANDCASTLE_PI_MODEL.

docker build \
--build-arg AGENT_UID="$(id -u)" \
--build-arg AGENT_GID="$(id -g)" \
-f .sandcastle/Dockerfile \
-t pi-token-burden-sandcastle:local .

pnpm install --frozen-lockfile
pnpm run sandcastle
```

The runner loads `.sandcastle/.env` itself; do not `source` it. Sandcastle injects those variables into the Docker sandbox without mounting host Pi configuration.

## Concurrency boundary

Run only one Sandcastle claimer per GitHub identity. Assignee-only claims cannot distinguish two runners that share the same token user; both could accept the same claim. When multiple hosts or processes share a token, place an external mutex around `pnpm run sandcastle`.

## Behavior

1. Run one coarse GitHub search per supported route label, merge results, and sort by `createdAt` then issue number.
2. Immediately revalidate route, exact assignee set, and native or fallback blockers before and after claim.
3. Create one warm Docker sandbox on `sandcastle/issue-<number>`.
4. Research tickets create and commit a cited `docs/research/` artifact without product validation.
5. Implementation tickets run parallel Standards and Spec reviews, persist JSON and logs under `.sandcastle/reports/issue-<number>/`, resume the implementation session for fixes, and allow at most three passes.
6. The runner executes `pnpm run check` and `pnpm run test:e2e` after every implementation pass. Skips and runtime gaps fail closed.
7. Successful branches are pushed before reporting. Implementations open or reuse a PR; terminal failures are commented, relabeled `ready-for-human`, and unassigned.
8. Discovery continues until no eligible issue remains.

## Local verification

```bash
pnpm run test:sandcastle
pnpm run typecheck:sandcastle
pnpm run check
pnpm run test:e2e
```
7 changes: 7 additions & 0 deletions .sandcastle/afk-runner.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { runAfk } from './afk-runner.js';

describe('Sandcastle AFK entrypoint', () => {
it('exports the queue-draining runner without executing it on import', () => {
expect(runAfk).toBeTypeOf('function');
});
});
Loading
Loading