Skip to content

Commit c5f33a6

Browse files
committed
chore: add error-boundary building block
1 parent 9128fda commit c5f33a6

5 files changed

Lines changed: 47 additions & 20 deletions

File tree

.agents/skills/building-blocks/SKILL.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ Read the rule file for each block you are about to implement. The summaries belo
4444
| `compound-component` | `components/` | Dot-notation sub-components. Composition over configuration — no boolean prop toggles. | `rules/compound-component.md` |
4545
| `form` | `components/` | Context-driven form via `useForm` + `FormProvider` + field components. | `rules/form.md` |
4646
| `hoc` | `components/` | Component in → enhanced component out. For render-level decisions (auth gates, suspense wrappers). | `rules/hoc.md` |
47+
| `error-boundary` | `components/` | Scoped error handling for a feature component with its own query. | `rules/error-boundary.md` |
4748
| `page` | `pages/` | Route-level orchestrator. Only place where router coupling is acceptable. | `rules/page.md` |
4849
| `facade-hook` | `components/` | Private logic extraction for a single component. Defined below the component, not exported. | `rules/facade-hook.md` |
4950
| `named-effect` | `components/` | Named function expressions in `useEffect`. Intent visible at a glance. | `rules/named-effect.md` |
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
---
2+
title: Error Boundary
3+
category: Component Patterns
4+
layer: components/
5+
composedWith: query-options-factory
6+
---
7+
8+
## Error Boundary
9+
10+
Scoped runtime error handling for a feature component that runs its own query. Contains failure to the component's own DOM so the rest of the page keeps working. Catches both render errors and query fetch errors through a single wrapper.
11+
12+
### Constraints
13+
14+
- Only for components with their own **query hook**.
15+
- `ErrorBoundary` and `withErrorBoundary` live in `src/lib/components/ErrorBoundary/`. Use `withErrorBoundary` by default — do not hand-roll.
16+
- Add `throwOnError: true` on the query's `useQuery` call in `providers/`.
17+
- No retry for ErrorBoundary by default.
18+
19+
### Example
20+
21+
```tsx
22+
import { withErrorBoundary } from "@/lib/components/ErrorBoundary/with-error-boundary";
23+
24+
interface IProps {
25+
productId: string;
26+
}
27+
28+
const ProductRatingBase = ({ productId }: IProps) => {
29+
const { data } = useMarketingProductQuery(productId);
30+
// render with data
31+
};
32+
33+
const ProductRating = withErrorBoundary<IProps>(ProductRatingFallback)(
34+
ProductRatingBase
35+
);
36+
37+
export { ProductRating };
38+
```

.agents/skills/writing-spec/SKILL.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ Fill sections 7-10 of the template.
5353
- Traces to requirement IDs (R1, R2, …)
5454
- Includes target file paths
5555
- Is marked `[P]` (parallelizable) or `[S]` (sequential)
56-
2. Draft **Error & Edge Cases** using GIVEN/WHEN/THEN — cover failure modes, boundary conditions, concurrency
56+
2. Draft **Error & Edge Cases** using GIVEN/WHEN/THEN — cover failure modes (including fetch errors for data-fetching components), boundary conditions, concurrency
5757
3. Add **Open Questions** for anything unresolved that blocks a specific task
5858
4. Present for review
5959

docs/spec-driven-development.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -210,13 +210,13 @@ The building blocks catalog lives in `skills/building-blocks/`. It uses progress
210210

211211
### Block categories
212212

213-
| Category | Blocks | What they cover |
214-
| ------------------ | ----------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------ |
215-
| Data Fetching | `mutation-hook`, `query-options-factory`, `query-keys-factory`, `dto-model` | Server reads/writes, cache keys, API response types |
216-
| State Management | `store`, `provider` | Zustand stores, React Context dependency injection |
217-
| App Orchestration | `use-case-hook` | Feature-level operations composing mutations + notifications |
218-
| Component Patterns | `notification-hook`, `pure-component`, `compound-component`, `form`, `hoc`, `page`, `facade-hook`, `named-effect` | UI components, hooks, and composition patterns |
219-
| Data Modeling | `frontend-model`, `value-object` | Domain types, value-based logic grouping |
213+
| Category | Blocks | What they cover |
214+
| ------------------ | ----------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------ |
215+
| Data Fetching | `mutation-hook`, `query-options-factory`, `query-keys-factory`, `dto-model` | Server reads/writes, cache keys, API response types |
216+
| State Management | `store`, `provider` | Zustand stores, React Context dependency injection |
217+
| App Orchestration | `use-case-hook` | Feature-level operations composing mutations + notifications |
218+
| Component Patterns | `notification-hook`, `pure-component`, `compound-component`, `form`, `hoc`, `error-boundary`, `page`, `facade-hook`, `named-effect` | UI components, hooks, and composition patterns |
219+
| Data Modeling | `frontend-model`, `value-object` | Domain types, value-based logic grouping |
220220

221221
### How specs reference building blocks
222222

specs/lessons.md

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -41,15 +41,3 @@ Patterns captured after corrections. Review at session start.
4141
**Source:** Correction 2026-04-12 — `MarketingProductDto` leaked into `ProductDetails.tsx` component props.
4242

4343
---
44-
45-
## L004 — Missing `error-boundary` building block
46-
47-
**Rule:** Feature components embedded inside pages (e.g. `ProductRating` inside `ProductPage`) need scoped error handling so their failure doesn't blank the whole page — an "island" error boundary. The pattern should cover both React render errors and React Query errors (`useQuery({ throwOnError })` composed with `QueryErrorResetBoundary`) in one consistent wrapper. No typed block exists for this in `.agents/skills/building-blocks/SKILL.md`, so specs either skip scoped error UI or reinvent it per feature.
48-
49-
**Why it failed:** Spec 005 needed `ProductRating` to degrade gracefully if the marketing query failed, but no canonical pattern existed — the spec deferred error handling entirely rather than ship a one-off.
50-
51-
**How to apply:** When a spec introduces a feature-level component mounted inside a page, ask whether its failure should blank the page or render a scoped fallback. If scoped, flag the `island-error-boundary` gap and defer the error UI rather than inventing one. When the block is added, it should wrap `react-error-boundary` + `QueryErrorResetBoundary`, surface a `fallback` prop, and standardize the treatment of render vs. query errors.
52-
53-
**Source:** Spec 005 Q1 — rating component error UI deferred because no canonical island-error pattern existed.
54-
55-
---

0 commit comments

Comments
 (0)