# The Web Designer — a core Astroagent console update **Status:** Implemented · **Date:** 2026-07-24 **Related:** [qa-agent.md](qa-agent.md) · [../agents/console/PLAN.md](../agents/console/PLAN.md) · [../agents/console/SETUP.md](../agents/console/SETUP.md) · [../brand/BRAND.md](../brand/BRAND.md) · [../.claude/skills/brand/SKILL.md](../.claude/skills/brand/SKILL.md) · [../.claude/skills/changelog/SKILL.md](../.claude/skills/changelog/SKILL.md) --- ## What it is The **Web Designer** is a new capability of the **astroagent console** — the base's in-site authoring engine (`agents/console/server.mjs`). It is not a separate OS agent; it is a new *mode* of the existing confined runner (`carlos-arias-agent`), reached through the already-gated `/devconsole/` nginx prefix. You hand it a **brief for any page** — a prompt/instructions, a draft, uploaded images, and links to short videos — and it does real design work (edit a section, restyle a component, lay out a new page) and publishes. Briefs are **queued**; the agent drains the queue one task at a time. This is a **core engine change**, not site content — a fresh SeedProject clone that runs the migration + `configure.mjs` inherits the whole thing. (See the site-coupling caveat below for the one thing that is not yet fully clone-clean.) It coexists with two sibling surfaces built earlier in the same arc: - the **in-page console** (the ▲ handle) — freeform, contextual, preview→publish by hand; - the **project builder** (`/admin/projects/new` → `/devconsole/build-project`) — the *structured* path that writes a `cja_projects` row. The Web Designer is the *freeform, queued* path for everything else. > **Framework note (2026-07-24):** the console runner was refactored into a generic host + > pluggable agent modules ([FRAMEWORK.md](../agents/console/FRAMEWORK.md)). The Web Designer now > lives in `agents/console/agents/web-designer.mjs` (exports `manifest` + `register`), is enabled > via the `astroagent.config.json` roster, and exposes `host.drainTasks` (QA autofix kicks it). > Logic unchanged from below. ## Added after first write (2026-07-24) Two capabilities were added to the Web Designer after this doc's first version: - **Self-logs every change to the changelog.** `buildDesignPrompt` instructs the agent, after finishing, to add one entry to `api/cli/seed-changelog.php` via the `changelog` skill, attributed to **`Website Designer Agent`**, with the current timestamp (injected by the runner via `nowStamp()` — a headless agent can't read the clock). Because the confined agent has no shell, **the runner reseeds** `cja_changelog` (`php api/cli/seed-changelog.php`) when that file changed, before the build, so the entry goes live. Changelog entries carry a `by`/`actor` field (migration 012): agents name themselves; human/CLI edits default to `Carlos Arias`. - **Fed by the QA agent.** QA findings become Web Designer tasks — auto-queued for safe categories, or dispatched from the `/admin/qa` instructions box. A QA-origin task is just a normal `cja_tasks` row, so it fixes, publishes, and self-logs like any other. See [qa-agent.md](qa-agent.md) for the closed loop. **Timezone note:** `nowStamp()` uses the runner's system timezone, set to `America/New_York` on 2026-07-24 (was UTC, which mis-stamped early agent entries). ## Architecture ``` /admin/designer ──create task──▶ cja_tasks (queued) [PHP: admintasks controller] │ poll status ▲ └──kick──▶ /devconsole/tasks/run ── drainTasks() loop [Node runner] │ claim next (php api/cli/tasks-next.php) │ 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). ## Files | File | Role | Core / site | |---|---|---| | `agents/console/server.mjs` | `buildDesignPrompt`, `runDesignTask`, `drainTasks`, `POST /devconsole/tasks/run` | **core** | | `api/db/migrations/011_create_cja_tasks.sql` | queue table | **core** | | `api/public/controllers/admintasks.php` | create / list / get / cancel | **core** | | `api/cli/tasks-next.php` | atomic claim (queued → running) | **core** | | `api/cli/tasks-finish.php` | mark published / failed + result | **core** | | `app/src/pages/admin/designer.astro` | submit brief + poll the queue | **core** | | `app/src/layouts/AdminLayout.astro` | "Web Designer" nav link | **core** | | `.claude/skills/ui-ux` (symlink) | generic design skill | **core** | | `.claude/skills/brand/SKILL.md` | this site's brand | **site-specific** | ## Site-coupling caveat (not yet clone-clean) The prompt-builders in `server.mjs` — both the new `buildDesignPrompt` and the pre-existing `buildPrompt` — **hardcode** "the Carlos Arias website" and "sumi-e" directly in the engine. This site identity was already baked into the console before this change; the Web Designer matches that pattern rather than adding new debt. For a truly clone-clean base, those strings should be sourced from `site.config.json` / `astroagent.config.json` + the `brand` skill, leaving the runner site-agnostic. Deferred — flagged, not done. ## How to run it 1. Sign in to the admin (secret link), open **/admin/designer**. 2. Submit a brief: pick a page (or "New page"), write instructions, optionally a draft, upload images, paste short-video links → **Add to queue & run**. 3. The queue drains automatically; each row shows `queued → running… → published` (with a live link + commit) or `failed` (with the reason). 4. Undo any task with `git revert ` on `main`, then rebuild.