│ run design agent: claude -p (skills + brief, Write/Edit)
│ build → git commit LIVE (auto-publish)
│ mark finished (php api/cli/tasks-finish.php)
└─ next task …
```
The Node runner is deliberately dependency-free (no DB driver), so it reads/updates the queue
by shelling out to small **PHP CLI helpers** — keeping MariaDB behind the single PHP data
layer. The design work itself is a `claude -p` spawn, the same harness as the in-page console.
### Locked decisions
| Question | Decision |
|---|---|
| Scope of the agent | **Full builder** — may edit any page and create new pages (updating the nav in `Header.astro` + `Footer.astro`). |
| Publish model | **Auto-publish & continue.** Each task builds, commits live, and the queue advances hands-off. Safety net = git history (one revert per task) + a build-must-pass gate. |
| Skills | **CLI-native.** Skills live under `.claude/skills/` so `claude` auto-discovers them; the persona also reads them explicitly for headless reliability. |
| Queue store | **DB table `cja_tasks`** (durable, survives restart), not the in-memory `jobs` Map. |
| Runner ↔ DB | **PHP CLI bridge** (`tasks-next.php` / `tasks-finish.php`); the runner stays dependency-free. |
| Progress UX | **Polling**`admintasks/list`, not SSE — sidesteps the drifted server/DevConsole event contract. |
## Skills (the CLI-native design brain)
`.claude/skills/` is new and is what makes the agent design rather than merely edit:
-`brand/SKILL.md` — the sumi-e brand system distilled from `brand/BRAND.md` (the seal rule,
palette tokens, type scale, radii, do/don'ts). **This file is the site-specific piece** — a
clone swaps its contents.
-`ui-ux/` — a symlink to the existing generic `app/.astroagent/skills/ui-ux` (reusable).
Before my change the console passed **no** skill wiring at all; the skills array in
`astroagent.config.json` was inert. The runner still passes no `--skill` flag — discovery is
by location (`.claude/skills/`) plus an explicit "read these SKILL.md files" instruction in
the design persona.
## Safety model
- **Clean-tree guard.** A task refuses to run if the git scope (`app brand api/db api/cli`) is
dirty — otherwise the auto-publish `git add` would sweep unrelated WIP into the task's
commit. Uploaded assets under `app/public/media/` are exempt (they *are* the task's images
and get committed with it). This guard exists because exactly that hazard bit during the
build: an uncommitted file was swept into a task commit and a revert then deleted it.
- **Build-must-pass gate** before any commit; a failing task reverts its own edits
(`git checkout` + `clean`) and the queue continues.
- **Scoped commits** — `git add` never touches secrets/config, only `app brand api/db api/cli`
(+ media). The agent runs confined, Write/Edit within the repo only.
- **Single-flight** — the queue shares the runner's global `busy` lock, so a draining queue and
the in-page console can never run at once (the console returns 429 while draining).