@pavp/wavefront 0.1.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  Claude Code agent framework for apps generated by **scaffold-nextjs-app**
4
4
  (Next.js 15 · React 19 · MUI 7 · React Query · Zustand · RHF + Zod · next-intl · Jest/RTL · Clean Architecture).
5
5
 
6
- 5 specialist agents + 19 self-contained skills + a 9-command orchestration loop, so you describe work items instead of writing boilerplate. Generates responsive, composed UI continuous with your app — from a Figma export, screenshot, description, or nothing — with a colocated test in every layer.
6
+ 5 specialist agents + 20 self-contained skills + a 9-command orchestration loop, so you describe work items instead of writing boilerplate. Generates responsive, composed UI continuous with your app — from a Figma export, screenshot, description, or nothing — with a colocated test in every layer.
7
7
 
8
8
  > Distilled from real scaffold code, pinned at commit `8edaa0b` (modules `todo`/`auth`, `about-view`, `test/`, `src/i18n`). Not from docs — from the source. E2E-validated against a generated app.
9
9
 
@@ -166,6 +166,7 @@ Design:
166
166
  - `design-intake` — normalize any design source (Figma export/image/HTML/reference/description/none) → design-spec mapped to `@/ui`+tokens
167
167
  - `responsive-layouts` — mandatory mobile-first responsive: theme breakpoints + responsive `sx` + `useResponsiveScreen`
168
168
  - `component-composition` — decompose into subcomponents; composition over inheritance; compound components (Modal pattern)
169
+ - `error-boundary` — recoverable error+retry state every data-fetching view renders when its query fails
169
170
 
170
171
  Meta:
171
172
  - `sync-audit` — detect skill drift vs scaffold `source_version` (used by `reviewer` audit mode)
@@ -217,10 +218,10 @@ This repo follows **Conventional Commits**, enforced by commitlint (`commit-msg`
217
218
 
218
219
  ## Roadmap (see `docs/plan.md` + `docs/roadmap.md`)
219
220
 
220
- - ✅ **MVP** — 5 agents + 19 skills, E2E-validated.
221
+ - ✅ **MVP** — 5 agents + 20 skills, E2E-validated.
221
222
  - ✅ **F4** — `sync-audit` drift detection (reviewer audit mode).
222
223
  - ✅ **F5** — quality-gate hooks (eslint/stylelint on edit, tsc on stop).
223
224
  - ✅ **F1** — standalone repo + global/local install + **npm distribution (F1d)**. Scaffold auto-injection (F1c) not pursued.
224
225
  - ✅ **F2** — orchestration loop (`/wavefront-*`) + parallel waves + change/fix flows.
225
226
  - ✅ **F6/F7** — design intake (incl. live Figma MCP) + component composition.
226
- - **F3** — per-layer agent subdivision — only open phase, gated on a measured win over the broad builder.
227
+ - **F3** — per-layer agent subdivision — **won't ship**: linear layer deps mean zero intra-module parallelism, and F2 waves already parallelize across modules. Broad builder stands.
@@ -13,7 +13,7 @@ The implementer that owns the full Clean Architecture chain for a feature module
13
13
 
14
14
  ## Operating rules
15
15
  - Always: read the knowledge skills before generating; mirror the `todo`/`auth` reference modules; use kebab-case + correct suffixes; respect layer direction (View → business hook → repository → gateway → api); type boundaries with interfaces; validate at the api boundary with Zod; build UI from `@/ui` + `@/styles/tokens`; create `index.ts` barrels; pass `dataSource` (default `'http'`) through repository/selector/hook signatures.
16
- - Always (UI): before writing views/components, read `design-system-inventory` (catalog + continuity) and `responsive-layouts`; if a design-spec exists, implement it. With NO design, follow the continuity rule — mirror the closest existing screen's components/layout/spacing. Always implement the four states (loading/empty/error/populated). Never hand-roll raw `<div>`/`<input>` to match a visual — translate to `@/ui` + tokens.
16
+ - Always (UI): before writing views/components, read `design-system-inventory` (catalog + continuity) and `responsive-layouts`; if a design-spec exists, implement it. With NO design, follow the continuity rule — mirror the closest existing screen's components/layout/spacing. Always implement the four states (loading/empty/error/populated). For the **error** state, every data-fetching view returns the error-boundary pattern (an error+retry component wired to the query's `refetch`) when its business hook reports `error` — never let a failed fetch render blank (see `error-boundary` skill). Never hand-roll raw `<div>`/`<input>` to match a visual — translate to `@/ui` + tokens.
17
17
  - Always (responsive — mandatory): every view/component is mobile-first responsive using the theme's custom breakpoints (`minMobile`/`minTablet`/`minDesktop`/`largeScreen`, NOT xs/sm/md) via `sx` objects (`{ minMobile: ..., minTablet: ... }`) or `useResponsiveScreen` for branching. No fixed pixel widths that overflow mobile; the four states are responsive too.
18
18
  - Always (composition — mandatory): decompose UI into focused subcomponents under `components/` (mirror todo: `*-form`, `*-item`, `*-list`, sections) — the view ORCHESTRATES + wires hooks, it does NOT hold all markup inline. Apply the `component-composition` "when to extract" decision rule: **anything rendered inside `.map()` is ALWAYS its own component** (a list extracts its item — never inline JSX in the map); also extract on distinct responsibility, reuse, own state/handlers, ~30–40+ line JSX blocks. Extraction is recursive — components have subcomponents too. Compose via children/props; for multi-part UI use the compound-component pattern. NO inheritance, NO boolean-prop explosion. Each component independently renderable + testable. Don't over-fragment trivial markup.
19
19
  - Always: after generating, run `tsc --noEmit` (or `yarn typecheck`) and `yarn lint`; fix what you generated until clean; then hand off to `tester` and `reviewer`.
@@ -39,6 +39,7 @@ The implementer that owns the full Clean Architecture chain for a feature module
39
39
  - `.claude/skills/design-intake/SKILL.md` — the design-spec to implement (when a design source was provided).
40
40
  - `.claude/skills/responsive-layouts/SKILL.md` — mandatory mobile-first responsive: theme breakpoints + `sx` responsive + `useResponsiveScreen`.
41
41
  - `.claude/skills/component-composition/SKILL.md` — decompose UI into subcomponents; composition over inheritance; compound components (Modal pattern).
42
+ - `.claude/skills/error-boundary/SKILL.md` — recoverable error state (error+retry component) every data view returns when its business hook reports an error.
42
43
 
43
44
  ## Workflow
44
45
  1. Read all knowledge skills.
@@ -33,7 +33,7 @@ Convention and architecture guardian for scaffold-nextjs-app projects. Reads cod
33
33
  - Structure: files live in the right layer dir; module shape matches `module-structure`; absolute `@/` imports.
34
34
  - Architecture: dependency direction (no View→API/gateway; goes through repository); dependency inversion; no side effects in render; business hook vs controller hook separated.
35
35
  - Imports: import-sort order; restricted-import rule (test bits from `@test/utils/test-utils`).
36
- - UI/responsive: UI from `@/ui` (not raw `@mui/material`); spacing/color via tokens (no literals); strings via next-intl; **responsive present** — no fixed pixel widths that overflow mobile, breakpoint usage via theme keys (`minMobile`/`minTablet`/`minDesktop`, not xs/sm/md); the four states (loading/empty/error/populated) handled.
36
+ - UI/responsive: UI from `@/ui` (not raw `@mui/material`); spacing/color via tokens (no literals); strings via next-intl; **responsive present** — no fixed pixel widths that overflow mobile, breakpoint usage via theme keys (`minMobile`/`minTablet`/`minDesktop`, not xs/sm/md); the four states (loading/empty/error/populated) handled — for a data-fetching view the **error** state must render a recoverable error+retry UI (error-boundary pattern wired to `refetch`); BLOCKER if a failed query renders blank.
37
37
  - Composition: UI decomposed into `components/` subcomponents (no monolithic view holding form+list+states inline); composition over inheritance; no boolean-prop explosion; each component independently testable. **Flag inline JSX inside `.map()`** — a mapped row/item must be its own component (e.g. list → item), not inline markup. Apply the "when to extract" rule (map / distinct responsibility / own state / ~30–40+ lines); also flag over-fragmentation of trivial markup.
38
38
  - Test coverage (per layer): flag any logic layer WITHOUT a colocated test — api, gateway, repository queries+mutations, store, selectors, business+controller hooks, helpers, each component, view. One happy-path test ≠ covered. Note missing unit vs integration.
39
39
  4. Tooling pass: run `yarn lint` and `yarn typecheck` if present; capture real errors.
package/agents/tester.md CHANGED
@@ -24,8 +24,8 @@ Mirror the scaffold (`src/modules/todo` has a colocated test in EVERY layer). Fo
24
24
  - **selectors** (each `*-selector.hook.test.ts`) — derived value correct.
25
25
  - **hooks** — business (`*-business.hook.test.ts`) AND controller (`*-controller.hook.test.ts`).
26
26
  - **helpers** (`*.helper.test.ts`) — pure unit tests.
27
- - **components** (each subcomponent `*.component.test.tsx`) — renders, props, interactions.
28
- - **view** (`*.view.test.tsx`) — composes parts, the four states render.
27
+ - **components** (each subcomponent `*.component.test.tsx`) — renders, props, interactions. Includes the **error-boundary** component (`error-boundary.component.test.tsx`): renders nothing on `error={null}`, shows message + retry on error, calls `onRetry` on click.
28
+ - **view** (`*.view.test.tsx`) — composes parts, the four states render — including the **error** state (a failing query shows the error+retry UI, not blank).
29
29
 
30
30
  **Unit vs integration:** unit = pure pieces in isolation (helpers, store, a presentational component with mock props). Integration = pieces wired through providers (`renderHookWithProviders` for a business hook hitting repository→gateway→api; a view rendered with real hooks). Cover both — unit for logic correctness, integration for the wiring.
31
31
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pavp/wavefront",
3
- "version": "0.1.0",
3
+ "version": "1.1.0",
4
4
  "description": "Claude Code frontend-engineering agent framework for scaffold-nextjs-app apps (Next.js/React/MUI/Clean Architecture). Installs agents, skills, and an orchestration loop into a project's .claude/.",
5
5
  "bin": {
6
6
  "wavefront": "bin/wavefront.js"
@@ -0,0 +1,117 @@
1
+ ---
2
+ name: error-boundary
3
+ description: Render a recoverable error state for data-driven views in scaffold-nextjs-app — an error+retry presentation component the view returns when its business hook reports an error. Read before building any view that fetches data.
4
+ source: scaffold-nextjs-app/src/modules/todo/components/error-boundary, src/modules/todo/views/todo-management
5
+ source_version: 8edaa0b
6
+ ---
7
+
8
+ # Error boundary (recoverable error state)
9
+
10
+ Every data-driven view must handle the **error** state, not just loading/empty/populated. The scaffold's pattern is NOT a React class `ErrorBoundary` — it's a small **presentation component** that takes the query `error` + an `onRetry` callback and renders a recoverable error UI. The view early-returns it when its business hook surfaces an error.
11
+
12
+ This is the "error" leg of the mandatory four states (loading | empty | error | populated). Without it, a failed fetch renders blank or a broken view.
13
+
14
+ ## The component
15
+
16
+ Colocate under `components/error-boundary/` (kebab dir, `*.component.tsx` suffix). Props: `error: Error | null` + `onRetry: () => void`. `memo` it, build from `@/ui` + tokens, give it a `displayName`.
17
+
18
+ ```tsx
19
+ 'use client';
20
+
21
+ import { memo, type MouseEvent, useCallback } from 'react';
22
+ import { Refresh } from '@mui/icons-material';
23
+
24
+ import tokens from '@/styles/tokens';
25
+ import { Box, Button, Typography } from '@/ui';
26
+
27
+ interface ErrorBoundaryProps {
28
+ error: Error | null;
29
+ onRetry: () => void;
30
+ }
31
+
32
+ export const ErrorBoundary = memo(({ error, onRetry }: ErrorBoundaryProps) => {
33
+ const handleRetry = useCallback(
34
+ (event?: MouseEvent<HTMLButtonElement>) => {
35
+ event?.preventDefault();
36
+ onRetry();
37
+ },
38
+ [onRetry],
39
+ );
40
+
41
+ if (!error) return null;
42
+
43
+ return (
44
+ <Box sx={{ p: tokens.spacing.scale6 }}>
45
+ <Typography color="error" variant="h6">
46
+ Error loading {/* entity, e.g. todos */}
47
+ </Typography>
48
+ <Typography color="error" variant="body2">
49
+ {error.message}
50
+ </Typography>
51
+ <Button startIcon={<Refresh />} sx={{ mt: tokens.spacing.scale4 }} onClick={handleRetry}>
52
+ Retry
53
+ </Button>
54
+ </Box>
55
+ );
56
+ });
57
+
58
+ ErrorBoundary.displayName = 'ErrorBoundary';
59
+ ```
60
+
61
+ ## Wiring it in the view
62
+
63
+ The business hook exposes `error` (from the repository query) + `refetch`. The view early-returns the boundary before rendering the populated UI:
64
+
65
+ ```tsx
66
+ const { todos, isLoading, error, refetch, /* ... */ } = useTodoManagementBusiness(dataSource);
67
+
68
+ if (error) {
69
+ return <ErrorBoundary error={error} onRetry={refetch} />;
70
+ }
71
+ // ...normal render
72
+ ```
73
+
74
+ - `onRetry` is the query's `refetch` — retry re-runs the failed fetch.
75
+ - The view's business hook MUST surface both `error` and `refetch` (they come from React Query's `useQuery` result via the repository → see [[repository-pattern]] and [[hook-patterns]]).
76
+
77
+ ## Colocated test (role-first)
78
+
79
+ ```tsx
80
+ import { fireEvent, renderWithProviders, screen } from '@test/utils';
81
+
82
+ import { ErrorBoundary } from './error-boundary.component';
83
+
84
+ describe('ErrorBoundary', () => {
85
+ const mockOnRetry = jest.fn();
86
+ beforeEach(() => mockOnRetry.mockClear());
87
+
88
+ it('renders nothing when error is null', () => {
89
+ const { container } = renderWithProviders(<ErrorBoundary error={null} onRetry={mockOnRetry} />);
90
+ expect(container.firstChild).toBeNull();
91
+ });
92
+
93
+ it('renders message + retry when error is provided', () => {
94
+ renderWithProviders(<ErrorBoundary error={new Error('boom')} onRetry={mockOnRetry} />);
95
+ expect(screen.getByText('boom')).toBeInTheDocument();
96
+ expect(screen.getByRole('button', { name: /retry/i })).toBeInTheDocument();
97
+ });
98
+
99
+ it('calls onRetry on click', () => {
100
+ renderWithProviders(<ErrorBoundary error={new Error('x')} onRetry={mockOnRetry} />);
101
+ fireEvent.click(screen.getByRole('button', { name: /retry/i }));
102
+ expect(mockOnRetry).toHaveBeenCalledTimes(1);
103
+ });
104
+ });
105
+ ```
106
+
107
+ ## Rules
108
+
109
+ - **Always:** every data-fetching view renders the error state via this component (or an equivalent error+retry UI). Never let a failed query render blank.
110
+ - `onRetry` wires to the query's `refetch` — the error must be recoverable, not a dead end.
111
+ - Build from `@/ui` + tokens (never raw `<div>` / hardcoded spacing); show `error.message`.
112
+ - `memo` + `displayName`; `error: null` ⇒ render nothing.
113
+ - Colocate the test; assert by role/text (see [[testing-jest-rtl]]).
114
+ - **Never:** swallow the error silently; render the populated view when `error` is set; hardcode the error copy's spacing/color (use tokens + `color="error"`).
115
+
116
+ ## Related
117
+ [[component-composition]] [[hook-patterns]] [[repository-pattern]] [[data-fetching-react-query]] [[testing-jest-rtl]] [[mui-design-tokens]]