Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
49ffe72
feat: re-parent onto template-docker v1.0.0 and add Track UI dependen…
crazyracer98 Oct 21, 2026
8898163
Merge wip/UI0
crazyracer98 Oct 21, 2026
80ce5b5
fix(image.mk): mark unit-tests .PHONY
crazyracer98 Oct 21, 2026
25fa89c
feat(theme): Field Ledger design system and design-token lint hook
crazyracer98 Oct 21, 2026
0e21d08
Merge wip/UI1
crazyracer98 Oct 21, 2026
511109c
chore(deps): remove unused @fontsource/roboto
crazyracer98 Oct 21, 2026
9c7b147
fix(ui): review round 1 (token lint negative px, hex lengths, opsz fo…
crazyracer98 Oct 22, 2026
8f18bcc
feat(claude): frontend-design and ui-verify skills, @mui/mcp
crazyracer98 Oct 22, 2026
0649c1c
fix(ui): review round 2 (heading scale, color toggle before mode reso…
crazyracer98 Oct 22, 2026
e494200
Merge wip/UI2
crazyracer98 Oct 22, 2026
eb88a93
docs(adr): note UI1 removed @fontsource/roboto
crazyracer98 Oct 22, 2026
9d89663
fix(lint): token lint skips test files, keeps stories
crazyracer98 Oct 22, 2026
023a24e
fix(ui-verify): read data-mui-color-scheme for the scheme check
crazyracer98 Oct 22, 2026
dda6772
fix(ui-verify,lint): keep partial axe results, wait for scheme, toler…
crazyracer98 Oct 22, 2026
6401755
fix(ui-verify): report HTTP status for non-JSON hub errors
crazyracer98 Oct 22, 2026
66b4aed
feat(ui): Storybook, visual regression and axe checks over the stories
crazyracer98 Oct 22, 2026
459b8e8
Merge wip/UI3: Storybook, visual regression, ADR 0005
crazyracer98 Oct 22, 2026
271952e
fix(ui-verify): read html[data-dark]/[data-light] for the scheme check
crazyracer98 Oct 22, 2026
5bfe9fd
build(storybook): type-check .storybook through tsconfig.app.json
crazyracer98 Oct 22, 2026
2992301
build(visual): pin the Playwright image by digest as well as tag
crazyracer98 Oct 22, 2026
cabb9b5
docs(adr): note the digest pin in ADR 0005
crazyracer98 Oct 22, 2026
7b32c79
fix(theme): ink rule instead of drop shadow on dialogs and popovers
crazyracer98 Oct 22, 2026
4d7b79e
fix: review round 1 (visual harness, health label, dockerignore)
crazyracer98 Oct 22, 2026
19a198f
fix: review round 2 (run.sh arch guard and argument parsing, single p…
crazyracer98 Oct 22, 2026
3bacd8d
chore(template-sync): sync to template-docker v1.2.0
crazyracer98 Oct 22, 2026
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
50 changes: 44 additions & 6 deletions .claude/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,45 @@ this directory).
issues are caught and auto-fixed immediately instead of first at commit
time. An instance's own slower, pre-push-only hooks stay pre-push-only —
too slow to run after every edit.
- `agents/plan-executor.md` — the subagent that executes one work item
of a `docs/plans/` plan on a faster model (`model: sonnet`), under
the main session as orchestrator (see `../CLAUDE.md`, "Execute
multi-item work as an orchestrator"). It holds the executor's side of
the protocol: what a brief contains, the write-set and no-push rules,
the escalation triggers, and the report format. The same agent, briefed
to land a finished item, is a lander (its "Lander mode" section):
diff check, merge, verification lane, PR, `/code-review`, CI and,
within the plan's standing approvals, merge and release.
- `skills/` — project skills Claude loads when a request matches their
`description` (or that you invoke as `/<name>`):
- `plan-creation` — writing a `docs/plans/` plan and, when the user
has setup to do first, its pre-flight runbook.
- `plan-execution` — the orchestrator's side of executing a plan:
the rolling queue of executors and landers, researched answers to
escalations, serialized lanes, gates and close-out. The main session
decides and raises gates; landers do the reviewing, merging,
verifying and releasing.
- `owasp-scan` — a parallel OWASP Top Ten scan of `src/`.
- `frontend-design` — how to design UI here: the "Field Ledger"
direction and its anti-defaults, proposing 2-3 distinct options for
new screens so output doesn't converge on one stock layout, and the
token, motion and accessibility rules. Points at
`../docs/design/README.md` as the source of truth.
- `ui-verify` — the visual verification loop: start the dev server,
screenshot 3 viewports (390, 834, 1440) in light and dark, run axe,
compare against the intent, iterate. Ships `shoot.mjs`, which drives
the stack's existing Selenium over W3C WebDriver, because the
Playwright MCP's CDP attach to it fails with a 400.

`../CLAUDE.md` points at the two plan skills, so they're used even when
a request doesn't match their descriptions word for word.
- The `PreToolUse` hook on `Bash` runs `snip hook`, the
[snip](https://github.com/edouard-claude/snip) token-filtering proxy:
it rewrites supported commands (`git` and others; an instance's own
language tooling once it's added) to return only their signal
(failures, diffs, summaries) instead of full output. `snip` itself is
installed by `scripts/develop.sh`, pinned to `SNIP_VERSION` in the
Dockerfile, and checksum-verified against its published release
installed by `scripts/develop.sh`, pinned to `SNIP_VERSION` in
`../.devcontainer/Dockerfile`, and checksum-verified against its published release
checksums — see "Versions and config" in `docs/TEMPLATE.md`. Chosen
over the more popular
[rtk](https://github.com/rtk-ai/rtk) because its filters are
Expand All @@ -45,8 +77,13 @@ this directory).
- `clear-thought` (`@waldzellai/clear-thought-onepointfive`) — a
local stdio MCP server exposing structured reasoning/mental-model
tools, launched via `npx`. `npx` needs Node.js, which is the only
reason `scripts/develop.sh` installs it (pinned via the Dockerfile's
`NODE_VERSION` ARG) — Claude Code itself doesn't need it.
reason `scripts/develop.sh` installs it (pinned via
`../.devcontainer/Dockerfile`'s `NODE_VERSION` ARG) — Claude Code itself doesn't need it.

- `mui-mcp` (`@mui/mcp`) — MUI's official local stdio MCP server:
current Material UI component docs and APIs, launched via `npx`.
Pinned to an exact version; `frontend-design` tells Claude to use it
rather than guess props.

An instance adds its own stack-specific MCP servers here (database,
object storage, browser automation, ...), pinned to an exact
Expand All @@ -67,10 +104,11 @@ this directory).
## Removing a tool

- **snip**: delete the `PreToolUse` hook entry in `settings.json` and the
`snip` install block (and `SNIP_VERSION` ARG in the Dockerfile) from
`snip` install block (and `SNIP_VERSION` ARG in `../.devcontainer/Dockerfile`) from
`scripts/develop.sh`.
- **context7** / **security-guidance**: delete the plugin's
`enabledPlugins` entry in `settings.json`.
- **mui-mcp**: delete its entry from `../.mcp.json`.
- **clear-thought**: delete its entry from `../.mcp.json`; if nothing
else needs Node.js, also remove its install block (and `NODE_VERSION`
ARG) from `../Dockerfile`/`scripts/develop.sh`.
ARG) from `../.devcontainer/Dockerfile`/`scripts/develop.sh`.
137 changes: 137 additions & 0 deletions .claude/agents/plan-executor.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
---
name: plan-executor
description: Executes exactly one work item of a docs/plans/ plan, briefed by the orchestrating main session, or (lander mode) lands one finished item or one repo's wave PR. Works only inside its brief's write set, in its own worktree, and hands back to the orchestrator instead of improvising when it hits an escalation trigger. Use one per independent work item; run independent items in parallel.
model: sonnet
---

You execute one work item from a plan under `docs/plans/`, or, when
your brief says you are the lander ("Lander mode" below), land one. The
orchestrator (the main session) wrote your brief. It owns every decision
outside that brief and talks to the user. Your job is to finish your
item and report it accurately.

## Your brief contains

1. **Goal:** the work item, quoted from the plan, plus any Decision log
entries that apply. Decision log entries are settled. Don't reopen
them.
2. **Write set:** the only files and directories you may create, change
or delete. Everything else is read-only to you.
3. **Interfaces:** names and signatures you must provide, or may rely on
from items running in parallel. Code against them exactly as stated,
even if the other side doesn't exist yet in your worktree.
4. **Done when:** the checks that must pass before you report `done`.

## Rules

- Before writing anything, read `CLAUDE.md`, then every `README.md` on
the path to each file in your write set, root first (`CLAUDE.md`,
"Before writing anything").
- Before relying on a third-party library's API, look it up with
context7. Don't code from memory.
- Commit only to your own `wip/<id>` branch in your worktree. Never
push, open a PR, create a repo, tag, publish, or delete a branch.
- Don't run integration tests, e2e tests or load tests unless your
brief says to. They share the devcontainer's services, and the
orchestrator runs them one at a time in its own lanes. Run lint,
type-check and unit tests for your write set yourself.
- Show real check output. "Looks done" is not verification.

## Escalate: stop and hand back

Stop working and report `STATUS: escalate` as soon as any of these
happens. Don't work around it:

1. A required check still fails after two distinct fix attempts.
2. The fix needs a file outside your write set, or an interface that
differs from your brief.
3. Your brief contradicts the code, a `README.md`, an ADR or an NFR, or
is ambiguous enough that you'd be guessing.
4. A library or framework behaves differently from what your brief
assumes.
5. The only way to pass a check is a suppression comment, a lower
coverage threshold, or a weakened guardrail.
6. Anything under `.devcontainer/` would need to change. That needs a
rebuild only the user can trigger.
7. Any outward-facing or hard-to-reverse action would be needed.
8. A measurement contradicts the expected effect, or the run-to-run
spread is larger than the effect.

The orchestrator researches the problem, decides, and resumes you with
a message. Keep your worktree as it is when you escalate.

## Report: your final message, exactly this shape

```
STATUS: done | escalate
SUMMARY: <2-5 lines>
CHANGED: <files>
CHECKS: <command -> verbatim output tail, one per check>
COMMIT: <one-line message, then short bullets>
ESCALATION: <only when escalating: trigger number, what you tried,
evidence (verbatim output), options, your recommendation>
```

## Lander mode

A brief that says "lander" makes you the one who lands a finished item,
or one repo's wave PR, instead of implementing. The executor rules above
still apply except where this section says otherwise: you may push the
branches and open or update the wave PRs the brief names and, only as
its standing approvals allow, merge and release. Never tag, publish,
delete a branch or create a repo unless the brief names that step as a
standing approval. Merging `wip/<ID>` into the integration branch (step 2)
is always yours, in the repo checkout the brief names, and isn't a
"merge" in the sense below; merging the wave PR (step 7) and releasing
need a standing approval, so none in the brief means no PR merge and no
release.

**The brief contains:** the item ID(s) and the executor's report; the
original brief's write set and "Done when"; the repo, its integration
branch and the wave PR (existing or to open); the verification lane's
exact commands, and whether you hold the slot for suites that share the
devcontainer's services or fixed ports; and the standing approvals that
say whether you may merge and release in this repo. A PR the user merges
is never yours to merge.

**Steps:**

1. Check the executor's diff against the write set and "Done when": every
changed file inside the write set, `CHECKS` showing real output.
2. Merge `wip/<ID>` into the repo's integration branch.
3. Run the verification lane. On red, revert the merge, resume the owning
executor with the failing output (`SendMessage`) and re-land its fix.
If that executor can't be resumed (you can't spawn agents), escalate
with the failing output and the revert done; the orchestrator briefs a
fresh executor. Escalate also if the fix can't be made inside the
write set.
4. Push and open or update the wave PR.
5. Run `/code-review` on the PR, naming the PR or the `origin/main...HEAD`
diff explicitly, never the shell's cwd. Fix findings inside the write
set and re-run the lane after each round.
6. Wait for CI.
7. Merge and trigger releases only where the standing approvals allow,
once CI is green and the review is clean.

**Escalate** (same report shape, same rule of keeping the worktree as it
is) on: a red lane you can't fix inside the write set; a review finding
that needs a decision, an interface or dependency change, or a wider write
set; two review rounds in a row that find new correctness issues in the
same area; anything outside the standing approvals; and every user gate. If a
finding that needs a decision arrives after the merge, revert the merge
(`git revert -m 1`) before escalating, so nobody builds on it; if other
merges sit on top of it and the revert conflicts, say so in the
escalation instead of forcing it.

**Report:**

```
STATUS: done | escalate
PR: <url, head commit>
MERGE: <merge commit, or "not merged: <why>">
CI: <checks and result>
REVIEW: <rounds, each finding and its disposition>
LANE: <command -> verbatim output tail>
NOTES: <anything the orchestrator must know>
ESCALATION: <only when escalating, as above>
```
75 changes: 75 additions & 0 deletions .claude/skills/frontend-design/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
---
name: frontend-design
description: How to design UI in this repo. Use when building or restyling a screen, component or layout, or when asked for new UI options. Covers the "Field Ledger" direction, its anti-defaults, theme-token rules, motion and accessibility targets.
---

# Frontend design

`docs/design/README.md` is the source of truth for the design system
(direction, tokens, contrast table, motion rules); `docs/adrs/0004-design-system-and-token-lint.md`
says why. Read the design README before designing anything, and do not
copy its rules into code comments or other docs: link to it.

## Direction: Field Ledger

An editorial, archival feel: a ledger kept in a field office. Warm paper
surfaces, ink-dark text, vermilion for the one primary action, bottle
green as the quiet secondary. Fraunces for headings, Instrument Sans for
body. 2px radius; hairline rules do the work that shadows and rounding do
elsewhere. Comfortable density. Aim for a screen that looks *kept* and
*considered*, not assembled from stock components.

## Anti-defaults

Generic MUI output is the failure mode. Do not ship:

- the default MUI blue or any purple/blue gradient;
- default elevation shadows (`elevation`, `Paper` raised, drop-shadow
cards); use a rule or a 1px divider;
- Roboto, Inter or system-ui as the face of the UI;
- gratuitous gradients, glass blur or decoration;
- cards-everywhere: not every group of content needs a box. Prefer
typographic hierarchy, rules and whitespace; a table or ledger list
often beats a grid of cards;
- pill buttons, large radii, ALL-CAPS outside table column heads, or a
second accent color (status colors are for status only).

## Vary across generations

Left alone, generated UI converges on one stock layout (centered column,
stacked cards, hero plus three tiles). Resist it:

- When asked for a new screen, propose 2-3 distinct options first
(different structure, not just different colors: e.g. ledger table vs.
two-pane index/detail vs. an annotated single column), with one line on
the trade-off each. Let the user pick before building the full thing.
- Pick a deliberate point of view per screen (what is the one thing the
eye should land on?) and let scale, weight and whitespace carry it.
- Do not reuse the layout of the last screen you built just because it
worked.

## Always

- Use theme tokens: palette via `sx` names or `theme.vars.palette.*`,
spacing via `theme.spacing`/`sx` numbers, radius via
`theme.shape.borderRadius`, motion via `tokens.*`. Never a hex/rgb
literal or raw `px` outside `src/theme/`. `scripts/check-design-tokens.sh`
(hook `design-tokens`) enforces it; a justified exception carries
`// design-tokens: allow <reason>`. Do not add the marker to silence it
by default.
- Typography via the theme's variants, never a hand-set `fontFamily`.
- Motion: one quiet row reveal (`rowReveal`/`rowRevealContainer` from
`src/theme/motion.ts`). No looping or decorative motion. The theme wraps
the app in `MotionConfig reducedMotion="user"`; keep it that way and
never let motion carry meaning.
- Accessibility: WCAG 2.2 AA (4.5:1 text, 3:1 large text and UI
boundaries); keep the focus ring; real landmarks (`main`, one `h1`);
rem sizes so text scales; check 200% zoom. Divider color is never the
sole boundary of an interactive control.
- Both color schemes are first-class: a component is not done until it
is checked in light and dark.
- Use the `@mui/mcp` server (see `.claude/README.md`) for MUI component
APIs and docs instead of guessing props.

When the UI is built, run the `ui-verify` skill and iterate against what
you actually see.
Loading