docs: require impeccable skill for design and frontend

Adds the design-system issue, scaffolds .impeccable/live/config.json against
web/index.html, and records the aoe spawn commands for the codex GPT-5.6
models in the driver table.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Claude
2026-08-22 18:48:44 +01:00
parent 3804e62eea
commit af33b38c1b
3 changed files with 115 additions and 24 deletions
+70 -15
View File
@@ -170,6 +170,9 @@ embedded into the binary with `include_dir` and served with correct MIME types.
- Dev mode proxies `/api/*` to the running daemon so the SPA has hot reload.
- One page that calls `GET /api/health` and renders it, to prove the whole path.
Use the `impeccable` skill for this — see the design section of `CLAUDE.md`.
Acceptance: `cargo run` serves the built SPA from a single binary with no
external files.
""",
@@ -625,10 +628,40 @@ client compiles.
),
# ---------------------------------------------------------------- phase 5
issue(
"design", "Design system tokens",
"phase/5-ui", "area/web", "difficulty/moderate",
deps=["web"],
body="""
Author `.impeccable/design.json` — colours, typography, spacing, the token set
every component consumes. **Use the `impeccable` skill for this**; it is what
the file's schema belongs to (`CLAUDE.md`, design section).
`~/tea/arcada` has a finished `design.json` at `schemaVersion: 2` worth reading
first for the shape.
Constraints specific to this app:
- The manual-search view (`DESIGN.md` §9.3) is dense, tabular and chip-heavy.
The palette has to carry three verdict states — eligible, waived, rejected —
legibly at small sizes and against each other, without relying on colour
alone.
- Library status (§4.2) is five states, and they are informational rather than
severity-ranked. Do not borrow an error/warning/success ramp for them.
- It runs on a NAS dashboard, often on a phone, often in a dark room. Dark mode
is not an afterthought.
`.impeccable/live/config.json` is already scaffolded and points at
`web/index.html`.
Acceptance: components consume tokens, never literal colour or spacing values,
and CI can assert that.
""",
),
issue(
"uisearch", "Unified search box",
"phase/5-ui", "area/web", "difficulty/moderate",
deps=["web", "searchapi", "moviesapi"],
deps=["web", "design", "searchapi", "moviesapi"],
body="""
`DESIGN.md` §9.2. One box, results grouped: in-library first, TMDB below. Enter
on a TMDB result opens the add flow with root and policy pre-filled.
@@ -639,13 +672,16 @@ The requirement, stated as a test: there is never a moment where the user has to
know whether they are searching or adding. If the UI has an "Add Movie" page
separate from search, this issue is not done.
Use the `impeccable` skill for this — see the design section of `CLAUDE.md`.
Debounce, and cancel in-flight requests on keystroke.
""",
),
issue(
"uimanual", "Manual search buckets and attribute chips",
"phase/5-ui", "area/web", "difficulty/moderate",
deps=["web", "searchapi"],
deps=["web", "design", "searchapi"],
body="""
`DESIGN.md` §9.3. The fix for Radarr's unusable manual search.
@@ -659,12 +695,15 @@ Debounce, and cancel in-flight requests on keystroke.
- One click on a `waived` row grabs it and writes the override.
The client must not re-implement any policy. Verdicts arrive from the API.
Use the `impeccable` skill for this — see the design section of `CLAUDE.md`.
""",
),
issue(
"uilibrary", "Library views with derived status",
"phase/5-ui", "area/web", "difficulty/moderate",
deps=["web", "moviesapi"],
deps=["web", "design", "moviesapi"],
body="""
`DESIGN.md` §4.2. The default view shows `airing` and `incomplete` only;
everything else collapses behind one toggle.
@@ -675,12 +714,15 @@ the exact problem this design removed.
Show waivers honestly: a file imported under `allow_english_audio` reads as
"English, no dub", not as a clean match.
Use the `impeccable` skill for this — see the design section of `CLAUDE.md`.
""",
),
issue(
"uiqueues", "Attention queues: no-PT-source and needs-decision",
"phase/5-ui", "area/web", "difficulty/easy",
deps=["uilibrary", "failure"],
deps=["uilibrary", "design", "failure"],
body="""
`DESIGN.md` §5.2 and §5.7. Two lists of things a human has to look at.
@@ -691,6 +733,9 @@ Show waivers honestly: a file imported under `allow_english_audio` reads as
These are the only places the app asks for attention, and they are the same
events that notify (§9.5). Keep them consistent.
Use the `impeccable` skill for this — see the design section of `CLAUDE.md`.
""",
),
@@ -929,32 +974,42 @@ def main():
by_title = {i["title"]: i["number"] for i in open_issues}
numbers = {}
# Pass 1 — ensure every issue exists, so dependency numbers are all known
# before any body is rendered.
for spec in ISSUES:
title = spec["title"]
if title in by_title:
numbers[spec["key"]] = by_title[title]
print(f"issue = #{by_title[title]} {title}")
continue
body = spec["body"]
if spec["deps"]:
refs = []
for d in spec["deps"]:
refs.append(f"#{numbers[d]}" if d in numbers else f"`{d}` (not yet created)")
body += "\n\n---\n\n**Depends on:** " + ", ".join(refs)
body += f"\n\nLabels: `{'`, `'.join(spec['labels'])}`"
print(f"issue + {title}")
if DRY:
continue
made = req("POST", f"/repos/{OWNER}/{REPO}/issues", {
"title": title,
"body": body,
"body": spec["body"],
"labels": [label_ids[n] for n in spec["labels"] if n in label_ids],
})
if made:
numbers[spec["key"]] = made["number"]
# Pass 2 — render bodies with real dependency numbers and push them.
# Declarative: this overwrites hand-edits to issue bodies. Edit the spec
# here, not the issue on Gitea.
for spec in ISSUES:
if spec["key"] not in numbers:
continue
body = spec["body"]
if spec["deps"]:
refs = ", ".join(f"#{numbers[d]}" for d in spec["deps"] if d in numbers)
body += f"\n\n---\n\n**Depends on:** {refs}"
body += f"\n\nLabels: `{'`, `'.join(spec['labels'])}`"
if DRY:
continue
req("PATCH", f"/repos/{OWNER}/{REPO}/issues/{numbers[spec['key']]}", {
"body": body,
"labels": [label_ids[n] for n in spec["labels"] if n in label_ids],
})
print(f"\n{len(numbers)}/{len(ISSUES)} issues present")