Complete rewrite of CLAUDE.md with full binding rules (vision, architecture layers, component limits, state management, design system, AI integration, performance, security). New ARCHITECTURE.md covers folder structure, all data flows with ASCII diagrams, scoring pipeline, AI flow, unified result feed, and workspace routing. New CODE_REVIEW_CHECKLIST.md provides a 10-section review guide and pre-commit quick checklist. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
9.9 KiB
Code Review Checklist — Property Match
Use this checklist before every PR review and before considering any implementation "done". For architecture context see ARCHITECTURE.md. For coding rules see CLAUDE.md.
A. Component Size & Structure
-
Is the component too large?
- Page component: hard limit 300 lines, target 150–200
- Feature component: hard limit 250 lines, target 100–150
- Atom/shared component: hard limit 150 lines
- If over limit: extract sub-components, move constants to
*Constants.ts, move helpers to*Utils.ts(.tsxif JSX)
-
Is business logic inside JSX?
.filter(),.sort(),.reduce(), conditional rendering based on computed values — these belong inuseMemo, not inline in the return- The render body should read computed values, not compute them
-
Are hardcoded strings present?
- Status labels (
'ACTIVE','Aktiv','PROPERTY_MANAGER') must come fromenums.tsorconstants.ts - Route paths must come from
ROUTESconstants, not string literals - German UI labels for enums live in
constants.ts(e.g.,ASSET_TYPE_LABELS)
- Status labels (
-
Are hardcoded colors or thresholds present?
- Colors like
'#1a7a4a','#dc2626'directly insxprops → useDS_COLORSfromlib/ds.ts - Score thresholds like
score >= 80→ useSCORE_STRONGfromconstants.ts - Confidence thresholds like
conf >= 0.75→ useCONF_HIGHfromconstants.ts
- Colors like
-
Are duplicate result type labels/colors defined locally?
RESULT_TYPE_METAinlib/ds.tsis the single source of truthRESULT_TYPE_LABEL/RESULT_TYPE_COLORinline objects anywhere else → delete them
B. State & Data
-
Is server data stored in Zustand?
- Any
useEffect(() => setFoo(data), [data])pattern that mirrors React Query data into local/Zustand state → remove it, read directly from the query - Server data lives exclusively in the React Query cache
- Any
-
Is derived state stored in state?
const [count, setCount] = useState(0)+useEffectto keep it in sync → compute inline or useuseMemo- Exception: expensive computations may use
useMemo
-
Is the full Zustand store subscribed to without a selector?
const { a, b, c } = useMyStore()— re-renders on every store mutation- Use
useMyStore(s => s.a)for single fields,useShallowfor multiple fields
-
Is there a side effect in the render body?
queryClient.invalidateQueries(...)called during render → must be inuseEffect- Any
console.log, analytics call, or mutation triggered directly in render body →useEffect
-
Are all hooks called before conditional early returns?
- React rules of hooks: all
useState,useMemo,useCallback,useQuerycalls must appear before anyif (isLoading) return ...
- React rules of hooks: all
C. Type Safety
-
Are there
anycasts?as any,: any,// @ts-ignore— fix the type or useunknownwith a type guard- One exception: bridging untyped third-party code — must be isolated in a named adapter
-
Are domain types re-defined locally?
- Any interface or type that duplicates something in
src/domain/→ import from domain instead
- Any interface or type that duplicates something in
-
Are response types used?
- Service functions should return
ListResponse<T>orItemResponse<T>fromservices/types.ts
- Service functions should return
-
Do
.tsxfiles contain JSX and.tsfiles not?- Any file with JSX (
<Component />,<Box>,<Typography>) must have.tsxextension - Plain TypeScript utilities use
.ts
- Any file with JSX (
D. Layering & Architecture
-
Is business logic in a component?
- Scoring calculations, ranking, filtering rules, AI calls, data transformation → move to service or
features/layer - Components only render and dispatch
- Scoring calculations, ranking, filtering rules, AI calls, data transformation → move to service or
-
Is a provider called directly from a component or hook?
MockupPropertyProvider.getAll()called in a hook → must go throughpropertyService- All provider access is mediated by a service
-
Is an LLM API called directly?
- Any
fetchto an AI API, or directOpenAI/openrouterSDK calls outsidesrc/services/ai/→ move behindIAIService
- Any
-
Are there cross-workspace component imports?
- A supply component importing from
components/demand/or vice versa → usecomponents/shared/orcomponents/ui/instead
- A supply component importing from
-
Are the import direction rules respected?
services/must not import React hooksdomain/must not import anything fromsrc/lib/constants.tsmust not import from components or services- No circular imports
E. Loading, Error & Empty States
-
Does every data-dependent view handle loading?
- Every
useQuerythat drives a list or table must show a skeleton or spinner whileisLoading - Use the appropriate skeleton component (
FeedSkeleton,ReminderSkeleton, etc.)
- Every
-
Does every data-dependent view handle error?
isErrorfromuseQuerymust show<ErrorState message={error.message} />- Never leave an error state blank or silently swallowed
-
Does every list handle the empty state?
- Empty results after filtering →
<FeedEmptyState filtered={true} />with a reset action - Empty because no data exists →
<FeedEmptyState filtered={false} />
- Empty results after filtering →
F. Performance
-
Are list items wrapped in
React.memo?- Components rendered inside
.map()in feeds, tables, or grids → should bememo() - Without memo, every parent state change (e.g., filter toggle,
selectedId) re-renders the entire list
- Components rendered inside
-
Are callbacks to memoized children wrapped in
useCallback?- Passing
() => setSelectedId(id)inline as a prop to amemo()-wrapped child → the memo is broken - Wrap with
useCallbackand explicit dependency array
- Passing
-
Are multiple aggregations over the same list collapsed into one pass?
- Three separate
useMemocalls that each.filter()the same array → collapse into oneuseMemowith aforloop
- Three separate
-
Is
useMemoused for expensive filters and sorts?.filter()+.sort()over 25+ items in the render body →useMemo- Decision aggregates (matchReady count, criticalGaps count, etc.) in
Properties.tsx→ already inuseMemo
-
Are routes lazy-loaded?
- All page imports in
App.tsxmust useReact.lazy()— never static imports for route components
- All page imports in
G. Explainability & Trust
-
Does every score shown have a breakdown path?
- A match score badge with no way to see why it's that score → add
ScoreBreakdownPanelor link to MatchDetail
- A match score badge with no way to see why it's that score → add
-
Are confidence levels shown proactively?
- Data with
confidenceScore < CONF_MEDIUMorfreshness === 'STALE'→ must be visually flagged ConfidenceBadge,FreshnessIndicator,DataQualityBarexist for this purpose
- Data with
-
Are Future Availability signals clearly labeled as probabilistic?
FUTURE_AVAILABILITYresults must always showprobabilityScoreandsignalBasis- They must never look identical to confirmed availability listings
-
Does AI output show its confidence?
- AI-extracted need criteria → shown with confidence indicator
- AI match suggestions → shown with
MAISON_WORKbadge and probability
H. Security
-
Is the React Query cache cleared on logout?
- Logout flow must call
queryClient.clear()— otherwise stale data from previous user persists
- Logout flow must call
-
Are role checks using
permissions.tshelpers?user.role === 'PROPERTY_MANAGER'inline → usecanViewMatchCenter(user)fromlib/permissions.ts
-
Are workspace route guards in place?
- Every supply/demand/ops route wrapped in
<ProtectedRoute workspace={...} /> - Never guard with
if (user.workspace !== ...)inside a component body
- Every supply/demand/ops route wrapped in
-
Is sensitive data stored in Zustand?
- PII, tokens, and sensitive fields beyond current-user identity should not persist in client state
I. Backend-Readiness
-
Is the provider interface complete?
- Any new entity that creates a mock shortcut (array mutation inline, no interface) → add
I[Entity]ProviderandMockup[Entity]Provider
- Any new entity that creates a mock shortcut (array mutation inline, no interface) → add
-
Are async patterns consistent?
- All provider methods are
async— if a new provider method is synchronous, fix it before it gets called from a service
- All provider methods are
-
Are query keys stable and specific?
- Query key
['properties']is fine for list;['properties', id]for single item - Keys that include derived or computed objects (not primitives) will break cache lookup
- Query key
-
Are stale times centralized?
- No
staleTime: 60_000orstaleTime: 5 * 60 * 1000inline in hook files - Use
STALE_PROPERTIES,STALE_MATCHES, etc. fromsrc/lib/constants.ts
- No
J. AI & Model Governance
-
Is the AI call going through
IAIService?- Direct fetch to AI API anywhere outside
src/services/ai/→ move it
- Direct fetch to AI API anywhere outside
-
Is the AI response validated with Zod before use?
- Raw LLM string parsed inline with
JSON.parse()without a schema → add Zod validation
- Raw LLM string parsed inline with
-
Is the AI output logged via
aiMonitoringService?- Any AI call that influences a business decision (need parsing, match recommendation, market signal) → log it
-
Does the AI flow have a fallback?
- If
aiServicethrows or returnsconfidence < threshold, is there a manual entry path? - AI failures must never block the user workflow
- If
Quick Checklist — Pre-Commit
Before every commit, verify:
[ ] npx tsc --noEmit → zero errors
[ ] No new hardcoded colors, routes, or role strings
[ ] No business logic added to a JSX return block
[ ] No React Query data mirrored into Zustand or useState
[ ] All hooks called before early returns
[ ] New list-item components wrapped in React.memo
[ ] New provider methods are async and match the interface
[ ] New feature goes through Provider → Service → Hook → Component
[ ] Loading, error, and empty states handled for any new data-fetching view
[ ] File with JSX has .tsx extension