git:20260809.786f3c8 to git:20261016.0b768f9

96 added, 230 removed. Audit A to A.

---
name: meta-planning-web-planning
description: Frontend specification planning frameworks. Use when a spec touches UI components, forms, client state, or user-facing flows. Covers UI-state completeness (loading, error, empty, success), component boundaries, form validation contracts, state ownership, and measurable UI success criteria.
---
# Web Planning Frameworks
- > **Quick Guide:** Specify every state the UI can be in — loading, error, empty, and success are four different screens, and an unspecified one ships as a blank div. Reference the concrete component and form patterns the implementation must follow (file:line), bound the change to named directories, and write success criteria a reviewer can check with a yes/no: which element appears, what the validation rejects, what the user sees on a network error.
+ > **Quick Guide:** A UI spec is a contract about what the user sees in every state, and a state nobody specified still renders — as whatever the developer improvised. For each data-driven surface, specify loading, error, empty and success; for each UI piece, decide reuse, extend or create against a named component; for each form field, the rule, when it fires and the exact message; for each piece of state, one owner; and for each success criterion, what a reviewer will see. Apply a framework only where the feature touches its artifact — a feature with no form carries no form contract.
+ **Detailed Resources:**
+
+ - [examples/core.md](examples/core.md) — bad and good spec fragments for each pattern; read in order, the good ones are the web sections of a worked profile-editing spec
+
---
<critical_requirements>
- ## CRITICAL: Before Specifying Frontend Work
+ ## Before writing a frontend spec
- > **All specifications must be grounded in the codebase's real components, stores, and form patterns** — reference specific files with line numbers
+ **Specify loading, error, empty and success for every data-driven surface, or rule a state out in writing.** An unspecified state still renders — as a blank region, a spinner that never resolves, or whatever the developer guessed.
- **(You MUST specify every UI state the feature can render — loading, error, empty, and success — or explicitly rule one out)**
+ **Decide reuse, extend or create for each UI piece, naming by file and lines the existing component each decision was measured against.** A spec that says "a modal" gets a new modal; one that names the existing modal gets its focus handling and dismissal for nothing.
- **(You MUST reference the concrete component, form, and store patterns to follow, with file and line numbers)**
+ **Give each form field its rule, when the rule fires, and the exact message.** Timing and wording left open are decided form by form, so two forms in one product validate differently.
- **(You MUST specify validation per field — the rule, when it fires, and the exact message shown)**
+ **Bound the change to named files and directories, with a do-not-touch list naming each shared store, component and auth surface the feature only consumes.** Without one, the feature "fixes" a shared store or component on the way through and breaks its other consumers.
- **(You MUST bound the change to named files and directories, with an explicit do-not-touch list)**
+ **Name one owner for each piece of state the feature introduces.** Two owners drift apart after the first update, and state in the wrong owner is the costliest thing in the feature to move later.
- **(You MUST write success criteria as yes/no checks a reviewer can verify — never "works well" or "good UX")**
+ **Write each success criterion as something a reviewer can see or do in the UI, with how it is checked.** "Validation works" is met by any implementation; "an invalid email shows 'Enter a valid email address' below the field" is met by one.
</critical_requirements>
---
- **Auto-detection:** UI spec, component spec, frontend feature spec, form spec, modal spec, loading state, empty state, error state, client state design, frontend success criteria
-
- **When to use:**
-
- - Specifying new or changed UI components, pages, or flows
- - Specifying forms: fields, validation rules, submission behavior, error display
- - Specifying where client state lives and which store owns it
- - Specifying loading, error, empty, and success behavior
- - Defining measurable success criteria for user-facing work
-
- **When NOT to use:**
-
- - When implementing components (use the relevant web implementation skill)
- - For the API the UI calls (use the api planning skill)
- - For the planning PROCESS itself — research, scope fencing, success criteria structure — which the PM agent carries
+ **Auto-detection:** UI spec, frontend spec, component spec, page spec, form spec, modal spec, loading state, empty state, error state, skeleton screen, validation message, validation timing, error summary, client state ownership, URL state, do-not-touch list, UI acceptance criteria
- **Key patterns covered:**
+ **Applies to:**
- - UI-state completeness (loading, error, empty, success)
- - Pattern-reference discipline for components, forms, and stores
- - Form contracts: fields, validation, submission, feedback
- - State ownership and reuse boundaries
- - Scope fencing by directory
- - Measurable UI success criteria
+ - Specifying new or changed components, pages, dialogs and flows
+ - Specifying forms: fields, validation, submission, success and failure
+ - Deciding whether a store, a component or the URL owns each piece of client state
+ - Writing success criteria a reviewer checks against the rendered UI
- **Detailed Resources:**
+ **Handled elsewhere:**
- - [examples/core.md](examples/core.md) - Spec fragments and a worked example specification
+ - The spec's own structure — goal, context, requirement tiers, the list of files to modify and create, and the handoffs to testing and review — belongs to the specification format these sections sit inside.
+ - The endpoint the UI calls — its request and response shapes, status codes and auth are specified on the API side; this spec names the endpoint and maps each of its failures to what the user sees.
+ - How components are built — their internals, data wiring, memoisation and styling approach — is the implementation's; the spec states behaviour and the patterns to follow.
+ - Visual design — spacing, colour and type are the design system's, and the spec restates none of it.
---
- <philosophy>
-
- ## Philosophy
-
- **A UI spec is a contract about what the user sees, in every state.** The happy path is the easy fifth of the work; the loading spinner, the validation message, the empty list, and the failed request are where an unspecified feature quietly becomes four different developers' four different guesses.
-
- **When specifying frontend work:**
-
- - Find the closest existing component, form, and modal first — their patterns are the spec's vocabulary, referenced by file:line
- - Walk the feature through all four states — loading, error, empty, success — and write down what each renders
- - Specify validation as the user experiences it: the rule, when it fires, and the exact message
- - Name what must NOT change: the stores, shared components, and auth surfaces the feature merely touches
+ <decision_framework>
- **When NOT to specify:**
+ ## Which sections the spec needs
- - Don't specify implementation details (hook wiring, memoization) — behavior and patterns, not code
- - Don't design new UI primitives when the codebase's component library covers the need
- - Don't specify visual design beyond what the existing design system already decides
- - Don't add "should have" enhancements to the must-have list — scope creep starts in the spec
+ Each yes adds a section. A section the feature never touches is left out rather than filled with "N/A".
- **Core principles:**
+ - **Renders data, fetched or held locally** → UI states (Pattern 1), one block per surface
+ - **Includes a form** → form contract (Pattern 3)
+ - **Introduces or moves client state** → state ownership (Pattern 4)
+ - **Changes any UI** → component boundaries (Pattern 2), the shared-surface fence (Pattern 5) and observable criteria (Pattern 6)
- - **Four states or an explicit ruling-out**: an unhandled state ships as a blank screen
- - **Patterns by reference, not description**: "follow SettingsForm.tsx:45-89" beats "use proper form handling"
- - **State has one owner**: name the store or component that holds each piece; duplicated state is a spec defect
- - **Criteria are yes/no**: "invalid email shows an error message" is checkable; "validation works" is not
+ Inside a section that applies, the rule reverses: a state or field that cannot occur is ruled out in writing, because silence there reads as an oversight.
- </philosophy>
+ </decision_framework>
---
<patterns>
- ## Core Patterns
+ ## Core patterns
### Pattern 1: UI-State Completeness
- Every data-driven surface has four states. Specify each, or explicitly rule it out.
+ Every data-driven surface gets four states, each specified or ruled out. A surface rendering local data — a cart held in a store, recently viewed items — still gets an empty state; its loading and error are ruled out.
```markdown
- ## UI States
-
- For EACH data-driven surface the spec adds or changes:
-
- - [ ] Loading — what renders while data is in flight (skeleton, spinner, disabled control)
- - [ ] Error — what the user sees on failure, and whether retry is offered
- - [ ] Empty — what renders with zero items, and whether a call-to-action belongs there
- - [ ] Success — the populated state, including singular/plural and overflow behavior
- ```
-
- ```
- BAD: "Show the user's projects"
- GOOD: "Loading: 3 skeleton rows. Error: inline message with Retry button.
- Empty: 'No projects yet' with a Create button. Success: project cards,
- newest first, 20 per page."
+ - Loading — what renders, and after what delay it appears
+ - Error — the message, whether Retry is offered, and what stays on screen
+ - Empty — first use, and no results for the current filters, where they differ
+ - Success — one item, many, and overflow: pagination, truncation or scroll
```
- **Why this matters:** the unspecified states are the ones users hit at the worst moments — slow networks and failures. A spec that covers only success delegates the other three to chance.
-
- ---
+ Where the data refreshes after first load, also specify what stays visible during the refresh and what a failed refresh does to data already on screen.
- ### Pattern 2: Pattern-Reference Discipline
+ Full fragments: [examples/core.md](examples/core.md#pattern-1-ui-state-completeness)
- Reference the concrete implementation to follow, never a style in the abstract.
+ ### Pattern 2: Component Boundaries
- ```
- BAD: "Use proper form handling"
- GOOD: "Follow the form pattern from SettingsForm.tsx:45-89. Use the same validation
- approach, error display, and success messaging."
+ For each UI piece the feature adds, the spec decides reuse, extend or create, and names what it measured against by file and lines.
- BAD: "Open it in our usual modal"
- GOOD: "Use the ModalContainer pattern from components/modals/UpdateAllProjects.tsx:12-78 —
- overlay, positioning, and onClose handled by the container."
+ ```markdown
+ - [UI piece] — reuse | extend | create [file:lines] — [what it supplies, or why nothing fits]
+ - Edit dialog — reuse components/modals/modal-container.tsx:12-78 — focus trap, Escape, overlay
```
- **Rules the spec must state:**
-
- - Every referenced pattern names a file and line range that was actually read — an unverified reference is a guess
- - Why each pattern is the right reference, in one line
- - Which utilities and components must be reused rather than re-created (validation helpers, API client wrappers, shared inputs)
+ The references a frontend spec usually needs are the nearest component, form, dialog and store, the wrapper the codebase calls its API through, and the validation helpers its forms already share — each at lines someone actually read. The decision covers utilities as well as UI pieces: validation helpers, API client wrappers and shared inputs are named for reuse rather than re-created, because two copies of one email check soon disagree. Extending a shared component changes it for every consumer, so an "extend" moves that file into the modified list and says what its other consumers see.
- ---
+ Full fragments: [examples/core.md](examples/core.md#pattern-2-component-boundaries)
### Pattern 3: Form Contracts
- A form is specified field by field, then as a whole.
+ A form is specified field by field, then as a whole:
- | Element | Specify |
- | ----------- | --------------------------------------------------------------------------------- |
- | Fields | Name, input type, initial value and where it comes from |
- | Validation | Rule per field, when it fires (on blur, on submit), exact error message |
- | Submission | What is sent, to which endpoint, and the button's disabled-while-pending behavior |
- | Success | The message or navigation that confirms the save |
- | Failure | Network and server-validation errors — what is shown, what is preserved |
- | Dirty state | Whether unsaved changes warn on close/navigation |
+ - **Fields** — name, input type, initial value and where it comes from
+ - **Validation** — the rule, when it fires, the exact message and where it shows
+ - **Submission** — what is sent where, and what the submit control does while the request is pending
+ - **Success** — the message or navigation that confirms the save
+ - **Failure** — server field errors mapped back to their inputs, network failure, and entered values kept through both
+ - **Unsaved changes** — whether leaving with edits warns
- ```
- BAD: "Validate the email"
- GOOD: "email: required, format-validated on blur. Invalid: 'Enter a valid email
- address' below the field. Submit disabled while pending; server 422 maps
- field errors back to the matching inputs, values preserved."
- ```
+ **Validation timing is a product-wide convention, so the spec names the one the codebase already uses rather than choosing per form.** The two common conventions: validate on submit, then show an error summary that takes focus and links to each field; or flag a field when the user leaves it and clear the error as soon as the input is valid. Neither flags a field while it is first being typed into.
- ---
+ Full fragments: [examples/core.md](examples/core.md#pattern-3-form-contracts)
### Pattern 4: State Ownership
- Every piece of state has exactly one named owner.
+ Each piece of state the feature introduces or touches has exactly one named owner.
```markdown
- ## State Ownership
-
- For EACH piece of state the feature introduces or touches:
-
- - [ ] Owner named: which store, or which component's local state
- - [ ] Derived values computed, not stored — if it can be computed from existing state, it is
- - [ ] Server data cached through the codebase's existing data-fetching layer, not mirrored into a store
- - [ ] Existing store structures unchanged unless the spec explicitly changes them
+ - [ ] Owner named — a store, one component's local state, or the URL
+ - [ ] Derived values computed from existing state, never stored beside it
+ - [ ] Server data read through the existing data-access layer, not copied into a store
+ - [ ] Shareable view state — filters, sort, page, active tab — in the URL
+ - [ ] Existing store shapes unchanged unless the spec says otherwise
```
- **Why this matters:** state added in the wrong place is the hardest thing to move later. A spec that says "add updateProfile to UserStore, do not restructure it" prevents both the second store and the accidental rewrite.
-
- ---
-
- ### Pattern 5: Scope Fencing by Directory
+ The URL test is whether someone opening the link should see the same view. An open dialog and an unsaved edit fail it and stay out.
- Bound the change in file terms, both directions.
+ Full fragments: [examples/core.md](examples/core.md#pattern-4-state-ownership)
- ```markdown
- ## Scope Boundaries
+ ### Pattern 5: Fencing the Shared UI Surface
- **Files to Modify:** [each named file, with what changes in it]
- **Files to Create:** [each new file, with its purpose]
- **Files NOT to Touch:** [the stores, shared components, and auth surfaces the feature
- merely consumes — with why each is off-limits]
- ```
+ The spec's file list says what the feature changes; the frontend half of the fence names the shared surfaces it consumes and leaves alone, each with who depends on it, and a command that proves the fence held.
```
- BAD: "Don't break anything"
- GOOD: "Do not modify: the authentication flow (auth/), existing stores (stores/),
- shared components (components/shared/). Changes are limited to profile/."
+ Do not modify: [shared surface] — [who else depends on it]
+ Partly fenced: [file] — [the one change allowed]
+ Verify: git diff -- [fenced paths] is empty
```
- A verification command belongs with the fence: `git diff -- <excluded paths>` should be empty.
-
- ---
+ The usual candidates: shared components and their props, stores other features read, global styles and design tokens, routing configuration, and session handling.
- ### Pattern 6: Measurable UI Success Criteria
+ Full fragments: [examples/core.md](examples/core.md#pattern-5-fencing-the-shared-ui-surface)
- Each criterion is a yes/no check with a named verification method.
+ ### Pattern 6: Observable Success Criteria
- ```
- BAD: - Feature works well
- - Good user experience
- - No bugs
+ Each criterion names something a reviewer can see or do in the running UI, and how it is checked.
- GOOD: 1. Clicking "Edit Profile" opens the modal with current name, email, bio
- 2. Invalid email shows 'Enter a valid email address'; valid input clears it
- 3. Save updates the profile and shows the success message within 2 seconds
- 4. Network failure shows the retry message; entered values are preserved
- 5. All interactive elements reachable by keyboard; focus returns to the
- trigger button on close
- 6. No changes outside profile/ (git diff -- stores/ components/shared/ is empty)
+ ```markdown
+ 1. An invalid email shows "Enter a valid email address" below the field — component test
+ 2. Closing the dialog returns focus to "Edit profile" — keyboard check
```
- **Rules the spec must state:**
+ Accessibility criteria cover what the feature adds: each new control reachable and operable by keyboard, focus placed on open and returned on close, each input labelled, and status messages such as "Saved" announced to screen readers without taking focus. A performance criterion carries a number or is left out.
- - Each criterion names how it is verified: a test file, a manual check, or a command
- - Accessibility criteria are stated for what the feature adds — keyboard reachability, focus behavior, labels
- - Performance criteria carry a number when they exist at all ("under 2 seconds", not "fast")
+ Full fragments: [examples/core.md](examples/core.md#pattern-6-observable-success-criteria)
</patterns>
---
- <decision_framework>
-
- ## Decision Framework
-
- ### Which Spec Sections Does This Feature Need?
-
- ```
- Does the feature render data from an async source?
- ├─ YES → UI States section (Pattern 1) — all four states
- └─ Does it include a form?
- ├─ YES → Form Contract section (Pattern 3), field by field
- └─ Does it introduce or move client state?
- ├─ YES → State Ownership section (Pattern 4)
- └─ NO → Pattern references + scope fence + criteria may be the whole spec
- ```
-
- Always applicable: Pattern-reference discipline (Pattern 2), Scope fencing (Pattern 5), Measurable criteria (Pattern 6).
-
- ### Common Spec Failures
-
- | Failure | Consequence |
- | -------------------------------------- | ------------------------------------------------------------- |
- | Only the success state specified | Loading, error, and empty ship as blank or broken screens |
- | "Use proper form handling" | Each form invents its own validation timing and error display |
- | No do-not-touch list | The feature "fixes" a store and breaks its other consumers |
- | Criteria like "works well" | Nothing gates the merge; review becomes opinion |
- | Server data mirrored into a store | Two sources of truth; stale UI after every mutation |
- | Pattern reference without line numbers | The reference was never verified to exist |
-
- </decision_framework>
-
- ---
-
<red_flags>
- ## RED FLAGS
-
- **High Priority Issues (a spec with one of these is incomplete):**
-
- - A data-driven surface with no loading, error, or empty behavior specified
- - A form without per-field validation rules and messages
- - No do-not-touch list on a feature that consumes shared stores or components
- - Success criteria that cannot be answered yes/no
-
- **Medium Priority Issues:**
-
- - A new component where the referenced codebase pattern already provides one
- - State introduced without a named owner
- - Accessibility unmentioned on new interactive elements
- - A pattern reference to a file that was never read
+ ## Red flags
- **Common Mistakes:**
+ **Leaves the spec incomplete:**
- - Specifying the modal's content but not its close/cancel/focus behavior
- - Leaving "what happens to entered values on failure" undecided
- - Writing enhancement wishes into the must-have list
- - Describing visual design the design system already decides
+ - Only the success state written for a surface that fetches — the other three ship as whatever was quickest to build — specify each or rule it out.
+ - "With validation" in place of a per-field contract — each developer picks their own timing and wording — give each field its rule, timing and message.
+ - "Use our usual modal" with no path — nobody confirmed the pattern exists, and a second modal gets built — name the file and lines.
+ - A dialog specified by its content alone — close, cancel, Escape and where focus lands are left to chance — specify them, or reuse a container that owns them.
+ - Server data copied into a store — two sources of truth, and the copy goes stale after the first update made elsewhere — read it through the data-access layer.
+ - No do-not-touch list on a feature that consumes shared stores or components — a store gets "fixed" and its other consumers break — fence each shared surface with who depends on it.
+ - A criterion such as "works well" or "good UX" — nothing can fail it, so review turns into opinion — write what the reviewer will see.
- **Gotchas & Edge Cases:**
+ **Easy to miss:**
- - Empty and error states can coincide (failed load of an empty list) — decide which wins
- - A disabled submit button needs a reason the user can see
- - Optimistic updates need a rollback story in the spec, or must be explicitly out of scope
+ - A submit button disabled until the form is valid. A disabled button leaves the tab order and gives no reason, so keyboard and screen-reader users cannot find what is missing; keep it enabled and report errors on submit, or specify the visible reason it is unavailable. Disabling it while the request is pending is a separate decision, and prevents a double submit.
+ - A dialog action that removes the control which opened it. Focus has nowhere to return, so the spec names where it goes.
+ - A refresh that fails while data is already on screen. Keep the stale data with a notice, or replace it with the error state — left unspecified, both ship in different places.
+ - One "empty" covering both "nothing exists yet" and "no results for these filters". The first wants a create action, the second a way to clear the filters.
+ - A loading indicator shown immediately. Under about a second it only flashes, so the spec states the delay before it appears.
+ - An optimistic update with no rollback. The UI shows a save that did not happen; specify the rollback and its message, or rule optimism out.
+ - A filter, sort or page held in component state. A reload and the back button both lose it.
+ - An enhancement written into the must-have list. Scope creep starts in the spec: everything listed as must-have is built and reviewed as required, so a "should have" belongs in its own tier.
</red_flags>
-
- ---
-
- <critical_reminders>
-
- ## CRITICAL REMINDERS
-
- > **All specifications must be grounded in the codebase's real components, stores, and form patterns**
-
- **(You MUST specify every UI state the feature can render — loading, error, empty, and success — or explicitly rule one out)**
-
- **(You MUST reference the concrete component, form, and store patterns to follow, with file and line numbers)**
-
- **(You MUST specify validation per field — the rule, when it fires, and the exact message shown)**
-
- **(You MUST bound the change to named files and directories, with an explicit do-not-touch list)**
-
- **(You MUST write success criteria as yes/no checks a reviewer can verify)**
-
- **Failure to specify these contracts produces UIs whose error and empty states are accidents, whose forms each validate differently, and whose "done" nobody can verify.**
-
- </critical_reminders>