---
name: council
description: Consult the design council — the external interaction, content, and accessibility canon (IBM Carbon, GOV.UK Design System, WAI-ARIA APG, Nielsen Norman) — before committing a user-facing design decision. Auto-invoke when writing user-facing error / empty-state / notification copy, or when designing the keyboard / focus / ARIA behavior of an interactive widget (modal, menu, listbox, toast, splitter, disclosure). Also invokable on demand for any interaction, content, accessibility, or usability decision.
disable-model-invocation: false
---

# Design Council

A standing panel of external design authorities, consulted the way we already consult
Carbon: **local corpora, grepped — not WebFetched.** The council **advises**; Astrolabe's
own contract **decides**. When a source conflicts with `SOUL.md`,
`docs/architecture/09-visual-design.md` (visual contract), or
`docs/architecture/10-interaction-and-feedback.md` (interaction contract), **our contract
wins** — note the divergence and move on.

## Scope: a steady-state mechanism, with a one-off debt to clear

The council's standing job is **pointwise** — consult on the decision in front of you (the
routing table below), at authoring time. New user-facing work is checked as it's written,
so it never accrues the drift the council exists to prevent. This is the default, and what
the auto-triggers fire.

The **backfill** is _not_ a second mode — it's tech debt with an end. Code written _before_
the council existed was never checked against the roster; reconciling it is a one-off sweep
(every pre-council widget vs. APG, every user-facing string vs. GOV.UK/Carbon content
rules, every data surface vs. the loading/empty/error triad), emitting fixes + a gap list
into `docs/architecture/10`. Done once, **pointwise keeps it honest — there is no recurring
audit.** The only thing that reopens it is an **event, never a schedule**: seating a new
member (its principles have never touched existing code) or a source's breaking revision —
see _Adding a seat_. A light glance at a milestone boundary is a fair safety net for what
the heuristic auto-trigger missed, but that's the same one-off sweep, not a standing mode.

## How to convene (the routing rule)

Pull the **minimal** relevant member(s) for the decision at hand — do **not** sweep all
four; that wastes tokens and dilutes the answer. Map the decision to its seat(s):

| Decision in front of you                                         | Primary seat → then                                    |
| ---------------------------------------------------------------- | ------------------------------------------------------ |
| Error / failure / empty-state / notification **copy**            | **GOV.UK** → Carbon notifications → NN/g heuristic #9  |
| Keyboard / focus / ARIA roles of an **interactive widget**       | **WAI-ARIA APG** → Carbon component                    |
| **Latency / feedback / loading / progress** budgets              | **NN/g** response-time limits → Carbon loading pattern |
| **Forms / validation / destructive-action** flow                 | **GOV.UK** → Carbon                                    |
| General **usability** gut-check on a flow                        | **NN/g** 10 heuristics                                 |
| **Visual** styling (type, spacing, colour, component look)       | **Carbon** + our `docs/architecture/09`                |
| **Which chart** for the data/intent (chart-type choice)          | **FT Visual Vocabulary** + **Datawrapper**             |
| **PWA / offline / install / SW-update / storage-persistence** UX | **web.dev** (+ vite-plugin-pwa / Workbox for the API)  |

Then: read the cited file(s), extract the **specific** principle, and report it back with
a **citation (member + file path)** and a one-line "how it lands in Astrolabe." Don't
paraphrase the whole source — quote the rule that decides the question.

## The roster

All paths are under `/Users/oleh/code/reference/`. Treat clones as **inspiration, not
law** — they drift; the published guidance is the truth, the clone is the fast index.

| Member                         | Path                               | Authoritative for                                                                                                                                                                                              | How to query                                                                                                                                                                           |
| ------------------------------ | ---------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **IBM Carbon**                 | `carbon-website/src/pages/`        | Notification taxonomy, status levels, empty/loading states, content basics, data-viz styling                                                                                                                   | grep `.mdx` under `components/notification`, `patterns/{empty-states,loading,status-indicator}-pattern`, `guidelines/content`                                                          |
| **GOV.UK Design System**       | `govuk-design-system/src/`         | Error & validation messages, failure pages, forms, plain-language content, accessibility                                                                                                                       | `index.md` under `components/{error-message,error-summary,notification-banner}`, `patterns/{problem-with-the-service-pages,service-unavailable-pages,check-answers}`, `accessibility/` |
| **WAI-ARIA APG**               | `aria-practices/content/patterns/` | Keyboard interaction, focus management, ARIA roles/states for widgets                                                                                                                                          | `<pattern>/<pattern>-pattern.html` — e.g. `dialog-modal`, `alertdialog`, `alert`, `listbox`, `menu-button`, `disclosure`, `switch`, `tabs`, `tooltip`, `windowsplitter`                |
| **Nielsen Norman (distilled)** | `principles/nielsen-norman.md`     | 10 usability heuristics; response-time / feedback budgets (0.1s / 1s / 10s)                                                                                                                                    | read directly — it is short and curated                                                                                                                                                |
| **FT Visual Vocabulary**       | `chart-doctor/visual-vocabulary/`  | Chart choice: data-relationship taxonomy (Magnitude, Correlation, Change-over-Time, Ranking, Distribution, Deviation, Part-to-whole, Spatial, Flow) → chart type                                               | read `README.md` — the taxonomy is prose; each category gives a "use when…" definition + recommended chart types                                                                       |
| **Datawrapper (distilled)**    | `principles/datawrapper.md`        | Chart choice in plain language; practical rules of thumb (bar-is-safe-default, line-vs-column, circles hard to compare, size = quantity)                                                                       | read directly — short and curated; pairs with the FT clone                                                                                                                             |
| **web.dev (distilled)**        | `principles/web-dev.md`            | PWA/offline: service-worker update flow (`registerType: 'prompt'`), persistent storage (`navigator.storage.persist()`), quota/`estimate()`, installability — with the exact vite-plugin-pwa/Workbox API we use | read directly — short and curated; cites local `web-dev/`, `vite-plugin-pwa/`, `workbox/` clones                                                                                       |

## Close the loop

The council exists so we **don't re-derive the same decision twice**. When a consultation
settles a _recurring_ question (not a one-off), capture the resolution into our own
contract — `docs/architecture/10-interaction-and-feedback.md` for interaction/feedback,
`09-visual-design.md` for visual — via the `/doc-update` skill. Upstream canon informs;
the downstream contract records. A code site that embeds such a rule should cite our
contract, not the external source.

## Adding a seat

1. If it's an open-source repo, shallow-clone it into `/Users/oleh/code/reference/`
   (`git clone --depth 1 …`). If it's articles/blogs (not cloneable), **distill** the
   stable parts into `reference/principles/<source>.md` with attribution + links, like
   the NN/g note.
2. Add one row to **the roster** and one routing entry above.
3. Keep it lean — a seat earns its place only if it's authoritative for a decision the
   others don't cover well.
4. **Backfill once.** Reconcile the existing codebase against the new seat — its principles
   have never been applied before. This is the one event that reopens the one-off sweep
   (see _Scope_); after it, pointwise maintains the new seat like the rest. If the seat's
   domain is a **brand-new surface** with no pre-existing code to reconcile, the backfill is
   discharged by building that surface against the canon — no separate sweep.

Candidate future seats (not yet seated): **Shopify Polaris** (UX-writing depth).
