@loomfsm/bundle-code 0.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.
Files changed (81) hide show
  1. package/LICENSE +201 -0
  2. package/agents/acceptance.md +141 -0
  3. package/agents/api-contract.md +89 -0
  4. package/agents/architect.md +52 -0
  5. package/agents/challenger-reviewer.md +104 -0
  6. package/agents/classifier.md +74 -0
  7. package/agents/code-analyzer.md +43 -0
  8. package/agents/context-doc-verifier.md +94 -0
  9. package/agents/dependency-auditor.md +42 -0
  10. package/agents/implementer.md +135 -0
  11. package/agents/logic-reviewer.md +132 -0
  12. package/agents/migration.md +55 -0
  13. package/agents/performance.md +95 -0
  14. package/agents/plan-conformance.md +127 -0
  15. package/agents/plan-grounding-check.md +106 -0
  16. package/agents/planner.md +143 -0
  17. package/agents/playwright.md +68 -0
  18. package/agents/research.md +52 -0
  19. package/agents/security.md +88 -0
  20. package/agents/style-reviewer.md +85 -0
  21. package/agents/test.md +206 -0
  22. package/agents/ui-consistency.md +75 -0
  23. package/dist/manifest.d.ts +2 -0
  24. package/dist/manifest.js +34 -0
  25. package/dist/manifest.js.map +1 -0
  26. package/dist/src/bundle.d.ts +2 -0
  27. package/dist/src/bundle.js +424 -0
  28. package/dist/src/bundle.js.map +1 -0
  29. package/dist/src/index.d.ts +5 -0
  30. package/dist/src/index.js +14 -0
  31. package/dist/src/index.js.map +1 -0
  32. package/dist/src/invariants.d.ts +10 -0
  33. package/dist/src/invariants.js +208 -0
  34. package/dist/src/invariants.js.map +1 -0
  35. package/dist/src/policy-resolver.d.ts +2 -0
  36. package/dist/src/policy-resolver.js +65 -0
  37. package/dist/src/policy-resolver.js.map +1 -0
  38. package/dist/src/sandbox-rules.d.ts +2 -0
  39. package/dist/src/sandbox-rules.js +40 -0
  40. package/dist/src/sandbox-rules.js.map +1 -0
  41. package/dist/test/bundle.test.d.ts +1 -0
  42. package/dist/test/bundle.test.js +289 -0
  43. package/dist/test/bundle.test.js.map +1 -0
  44. package/dist/test/sandbox-rules.test.d.ts +1 -0
  45. package/dist/test/sandbox-rules.test.js +73 -0
  46. package/dist/test/sandbox-rules.test.js.map +1 -0
  47. package/knowledge/references/api-design.md +188 -0
  48. package/knowledge/references/arch-patterns.md +106 -0
  49. package/knowledge/references/caching.md +190 -0
  50. package/knowledge/references/concurrency.md +195 -0
  51. package/knowledge/references/db-postgres.md +153 -0
  52. package/knowledge/references/e2e-flutter.md +56 -0
  53. package/knowledge/references/e2e-playwright.md +53 -0
  54. package/knowledge/references/error-handling.md +208 -0
  55. package/knowledge/references/next-app-router.md +231 -0
  56. package/knowledge/references/observability.md +169 -0
  57. package/knowledge/references/optimization-strategy.md +197 -0
  58. package/knowledge/references/perf-flutter.md +62 -0
  59. package/knowledge/references/perf-nestjs.md +59 -0
  60. package/knowledge/references/perf-python.md +50 -0
  61. package/knowledge/references/perf-react.md +52 -0
  62. package/knowledge/references/react19.md +176 -0
  63. package/knowledge/references/redis.md +175 -0
  64. package/knowledge/references/security-backend.md +219 -0
  65. package/knowledge/references/test-flutter.md +65 -0
  66. package/knowledge/references/test-nestjs.md +82 -0
  67. package/knowledge/references/test-python.md +76 -0
  68. package/knowledge/references/test-react.md +66 -0
  69. package/knowledge/references/test-strategy.md +175 -0
  70. package/knowledge/references/ui-flutter.md +56 -0
  71. package/knowledge/references/ui-web.md +51 -0
  72. package/package.json +34 -0
  73. package/schemas/agent-feedback.schema.json +80 -0
  74. package/schemas/category-vocab.json +170 -0
  75. package/schemas/classifier-output.schema.json +53 -0
  76. package/schemas/finding.schema.json +92 -0
  77. package/schemas/pipeline-state.schema.json +238 -0
  78. package/schemas/reviewer-output.schema.json +62 -0
  79. package/schemas/state-extension.schema.json +53 -0
  80. package/schemas/validator-output.schema.json +48 -0
  81. package/stack-candidates.yaml +248 -0
@@ -0,0 +1,231 @@
1
+ ---
2
+ tags: [nextjs, app-router, rsc, route-handlers, layouts, frontend]
3
+ stack_signals:
4
+ - language: [typescript, javascript]
5
+ - project_type: [frontend-app, monorepo]
6
+ summary: |
7
+ Next.js App Router stance — Server Components by default, Client only where
8
+ needed, layered cache (data/route/full-route/edge) decided per route. Each
9
+ file convention has a specific contract; mixing them creates subtle bugs.
10
+ when_to_load: |
11
+ Project uses Next.js ≥13 with App Router (app/ directory, not pages/).
12
+ Diff includes files under app/, 'use client'/'use server' directives,
13
+ loading.tsx, error.tsx, not-found.tsx, route.ts, layout.tsx, revalidate,
14
+ cacheTag, cacheLife, parallel/intercepted routes, or middleware.
15
+ agent_hints: [logic-reviewer, performance, ui-consistency, api-contract]
16
+ ---
17
+
18
+ # Next.js App Router — Senior Stance
19
+
20
+ ## When this applies
21
+ Load when project uses Next.js ≥13 with App Router (`app/` directory, not `pages/`). Reviewer auto-loads when diff includes files under `app/`, `'use client'`/`'use server'` directives, `loading.tsx`, `error.tsx`, `not-found.tsx`, `route.ts`, `layout.tsx`, `revalidate`, `cacheTag`, `cacheLife`, parallel/intercepted routes, or middleware. Complements `react19.md` (which covers RSC primitives) — this file is router-specific.
22
+
23
+ ## Default Stance
24
+ The App Router collapses concerns that used to live in separate places (data fetching, caching, layouts, error boundaries, middleware, route handlers). Each file convention has a specific contract; mixing them creates subtle bugs. Default to Server Components; mark Client only where you actually need it. Cache behavior is layered (data cache, route cache, full route cache, edge cache) — make caching decisions explicit per route, not by default.
25
+
26
+ ## Patterns (use these)
27
+
28
+ ### File conventions — know what each does
29
+ - `page.tsx` — the route's UI. Default Server Component.
30
+ - `layout.tsx` — wraps pages in this segment. Persists across navigation. Default Server Component.
31
+ - `template.tsx` — like layout but re-renders on navigation. Use when state should reset.
32
+ - `loading.tsx` — Suspense fallback for the route. Auto-wraps the page.
33
+ - `error.tsx` — error boundary for the route. Must be Client Component.
34
+ - `not-found.tsx` — rendered for `notFound()` calls.
35
+ - `route.ts` (or `route.js`) — HTTP handler. Cannot coexist with `page.tsx` in same segment.
36
+ - `middleware.ts` — runs before request, at the edge.
37
+
38
+ ### Server Components by default
39
+ A new component is a Server Component unless you mark it `'use client'`. Server Components:
40
+ - Run only on the server, never ship to the browser.
41
+ - Cannot use hooks (`useState`, `useEffect`, `useRef`, etc.).
42
+ - Can be `async` and fetch data directly.
43
+ - Cannot pass non-serializable values (functions, classes) to Client Components.
44
+
45
+ ### Client Components — at the leaves
46
+ - `'use client'` directive at top of file.
47
+ - Mark only what needs interactivity / browser APIs / hooks.
48
+ - Wrap a small interactive piece, leave the rest Server.
49
+ - Server Components can render Client Components, AND can pass them children prop (slot pattern) — Client renders its slot Server-rendered content.
50
+
51
+ ### Data fetching: where and how
52
+ - **Server Components**: `await fetch(...)` directly in the component. Next dedupes identical fetches per request, caches per default policy.
53
+ - **Server Actions**: `'use server'` functions. Can be invoked from Client Components for mutations. Auth check at the top.
54
+ - **Route Handlers** (`route.ts`): for non-RSC consumers — webhooks, JSON APIs, third-party callbacks.
55
+ - **Client Components**: still use TanStack Query / SWR for client-side data needs (real-time, optimistic, complex caching).
56
+
57
+ ### Caching layers (Next 14+)
58
+ Four layers, each with different invalidation:
59
+ 1. **Request Memoization** — per-request dedupe of identical fetches. Automatic.
60
+ 2. **Data Cache** — persistent across requests. `fetch` with `next: { revalidate: N }` or `cache: 'no-store'`.
61
+ 3. **Full Route Cache** — pre-rendered HTML + RSC payload. Static by default; opt out with dynamic functions (`cookies()`, `headers()`, `searchParams`).
62
+ 4. **Router Cache** — client-side, in-memory, soft-navigation cache.
63
+
64
+ Invalidation: `revalidatePath()`, `revalidateTag()`, `revalidate` time-based.
65
+
66
+ In Next 16 (Cache Components): `'use cache'` directive + `cacheLife` / `cacheTag`. New PPR (Partial Prerendering) model. If you're on 16, see those primitives — different from 14/15 cache.
67
+
68
+ ### Server Actions: secured at the function
69
+ ```ts
70
+ 'use server';
71
+ export async function deletePost(formData: FormData) {
72
+ const session = await getServerSession();
73
+ if (!session) throw new Error('unauthorized'); // NEVER skip
74
+ // ... rest
75
+ }
76
+ ```
77
+ - Auth check at top of EVERY action body.
78
+ - Validate inputs with a schema (Zod). FormData is untyped.
79
+ - Don't return huge data. Return success flag, errors, redirect target.
80
+
81
+ ### Loading + Error UX
82
+ - Co-locate `loading.tsx` and `error.tsx` per route segment.
83
+ - `loading.tsx` is wrapped in `<Suspense>` automatically — granular suspense by route.
84
+ - `error.tsx` MUST be `'use client'`. Receives `error` and `reset` props.
85
+ - `notFound()` → renders nearest `not-found.tsx`.
86
+
87
+ ### Streaming + Suspense
88
+ - Page can be partially streamed: layout renders first, slow data Suspends, finishes streaming when ready.
89
+ - Place `<Suspense>` around slow data sources. Loading UI shows while waiting.
90
+ - One coarse Suspense around everything → users wait for slowest piece. Multiple fine Suspense → progressive reveal.
91
+
92
+ ### Parallel and Intercepted Routes
93
+ - **Parallel** (`@slot/page.tsx`): render multiple pages in same layout simultaneously. Use for dashboards with independent regions.
94
+ - **Intercepted** (`(.)foo`, `(..)foo`): show one route in the context of another (e.g., photo modal over feed). Browser refresh shows full route.
95
+ - Powerful, but increases mental load. Use only when payoff is clear.
96
+
97
+ ### Middleware
98
+ - Runs on every matching request at the Edge runtime.
99
+ - Auth checks, redirects, A/B routing.
100
+ - Cannot do heavy work — runs on every request.
101
+ - Use `matcher` config to limit which routes run middleware.
102
+
103
+ ### Generating routes
104
+ - `generateStaticParams` for static generation.
105
+ - `dynamic = 'force-dynamic'` to opt out.
106
+ - `revalidate = 60` for ISR-like behavior.
107
+ Set explicitly per route — defaults change between Next versions.
108
+
109
+ ## Anti-Patterns (DO NOT)
110
+
111
+ ### `'use client'` on the root layout
112
+ Marks ENTIRE app tree as Client. You lose all Server Component benefits.
113
+ **Rule:** layout stays Server. Wrap interactive children in their own Client components.
114
+
115
+ ### Big `'use client'` boundary at the top of a route
116
+ Page is mostly server-renderable but one button needs `onClick` → marking the whole page Client → entire tree shipped to browser.
117
+ **Rule:** isolate the interactive piece. Server page → renders Client button only.
118
+
119
+ ### Server Action without auth check
120
+ **Why it bites:** action is callable directly via fetch from anywhere — components are not security boundaries.
121
+ **Rule:** every Server Action begins with explicit auth check. (Repeated from react19.md because it's the #1 issue.)
122
+
123
+ ### Reading `request` / cookies in a layout that's static
124
+ Layout uses `cookies()` → entire route segment opts out of static rendering → unexpectedly dynamic.
125
+ **Rule:** know what you're opting out of. If you need cookies, accept the dynamic cost; if not, isolate the cookie use.
126
+
127
+ ### `fetch` without `cache` option, then surprised by stale data
128
+ Default cache behavior changes between Next versions and per-route. Implicit defaults bite you.
129
+ **Rule:** explicit `cache: 'force-cache' | 'no-store'` and `next: { revalidate: N, tags: [...] }` per fetch. Don't rely on memory of defaults.
130
+
131
+ ### Mutation in Server Component
132
+ ```ts
133
+ async function Page() {
134
+ await deletePost(id); // BAD
135
+ return <div>...</div>;
136
+ }
137
+ ```
138
+ Server Components are GET-equivalent. Mutations go through Server Actions (POST) or route handlers.
139
+ **Rule:** mutation paths use Server Actions or route handlers. Server Components only read.
140
+
141
+ ### Shared `'use client'` utility with re-exports
142
+ File marked Client, re-exports a Server-only function. Imports cross the boundary in unexpected ways.
143
+ **Rule:** keep Client and Server utility files separate. Import boundary follows directive boundary.
144
+
145
+ ### `error.tsx` not marked Client
146
+ File is Server Component (default), but error boundaries must be Client. Build fails or runtime error occurs.
147
+ **Rule:** `error.tsx` always starts with `'use client'`.
148
+
149
+ ### One coarse `<Suspense>` wrapping everything
150
+ Slowest data source dictates user-visible wait time. No streaming benefit.
151
+ **Rule:** Suspense at meaningful UI region boundaries (sidebar, main, footer, slow card).
152
+
153
+ ### Middleware doing DB / heavy work
154
+ Middleware fires per request at the edge → can't reach app DB cheaply, adds 50-200ms per request.
155
+ **Rule:** middleware = lightweight redirects/auth checks. Heavy work in route handlers.
156
+
157
+ ### `searchParams` used in static page
158
+ Page uses `searchParams` → forces dynamic rendering → no static optimization → slower TTFB.
159
+ **Rule:** know that `searchParams` is dynamic. Either accept dynamic, or use `generateStaticParams` for known param sets.
160
+
161
+ ### Returning JSX from Server Action
162
+ Server Actions return data; UI is rendered by the page or component on response.
163
+ **Rule:** action returns plain JSON-serializable data. Component re-renders with that data.
164
+
165
+ ### `revalidate: 0` everywhere "to be safe"
166
+ Defeats Next's caching, every request hits backing source.
167
+ **Rule:** pick TTL based on data freshness needs.
168
+
169
+ ### Calling Client-only APIs inside Server Component
170
+ `window`, `document`, `localStorage` — runtime error or build error.
171
+ **Rule:** access via `'use client'` boundary only. Use `useEffect` for browser APIs.
172
+
173
+ ### Imports from Client Component into Server Component creating cycles
174
+ Client Component imports from a server-only module; server-only module imports from a Client Component. Bundler chokes or duplicates code.
175
+ **Rule:** clean dependency tree. Server depends on Server; Client may depend on Client + serializable Server exports.
176
+
177
+ ### Migrating Pages Router incrementally without strategy
178
+ Keeping `pages/` and `app/` simultaneously, sharing components without checking which directives propagate.
179
+ **Rule:** plan migration per-segment. Don't expect Pages Router middleware/_app to apply to App Router routes.
180
+
181
+ ## Decision Framework
182
+
183
+ | Need | Choice |
184
+ |---|---|
185
+ | Read DB and render | Server Component with `await` |
186
+ | Form submit | Server Action + `useActionState` |
187
+ | Optimistic UI | `useOptimistic` (Client Component, see react19.md) |
188
+ | Public JSON API | Route handler `route.ts` |
189
+ | Webhook receiver | Route handler with signature verification |
190
+ | Auth redirect | Middleware (lightweight) or in layout/page |
191
+ | Real-time data | Client Component + WebSocket / SSE / polling |
192
+ | Slow data + fast layout | Suspense around slow part; layout renders first |
193
+ | Data shared across pages | Layout component fetches; pages receive via children render |
194
+ | Mutating data + revalidating | Server Action calls `revalidatePath` / `revalidateTag` |
195
+ | Static page with occasional updates | `revalidate: N` (ISR) |
196
+ | Per-user dynamic page | `dynamic = 'force-dynamic'` or use of cookies/headers |
197
+ | Multiple regions in a dashboard | Parallel routes (`@slot`) |
198
+ | Modal that's also a real route | Intercepted routes |
199
+
200
+ ## Cost Model
201
+
202
+ | Pattern | Cost / Win |
203
+ |---|---|
204
+ | Server Component | 0 client JS for that component |
205
+ | `'use client'` on a leaf | +5-30KB JS bundle for that island |
206
+ | `'use client'` on root layout | Entire app shipped to browser |
207
+ | Server Action call | 1 round trip + serialization |
208
+ | Static route (full prerender) | Sub-100ms TTFB from CDN |
209
+ | `dynamic = 'force-dynamic'` | Every request hits the server; TTFB depends on backing data |
210
+ | ISR with revalidate=60 | First request after 60s rebuilds; users see brief stale |
211
+ | Middleware on every route | +5-50ms per request at the edge |
212
+ | Coarse Suspense | Wait for slowest data → entire page blocked |
213
+ | Granular Suspense | Progressive reveal; better perceived perf |
214
+
215
+ ## Red Flags in Diff
216
+
217
+ - `'use client'` added to `app/layout.tsx` or any top-level layout → flag (massive bundle impact).
218
+ - `'use client'` added to a component without any hooks / event handlers / browser APIs → flag (probably can stay Server).
219
+ - Server Action without explicit auth check at top of body → flag immediately.
220
+ - New `error.tsx` not starting with `'use client'` → flag.
221
+ - New `fetch(...)` in Server Component without explicit `cache` / `next.revalidate` config → flag (implicit-default risk).
222
+ - `cookies()` / `headers()` used in a layout that's expected to be static → flag (forces dynamic render of all child pages).
223
+ - Server Action returning JSX or a class instance → flag (must be JSON-serializable).
224
+ - New middleware doing DB / heavy work → flag.
225
+ - Whole route or page wrapped in single `<Suspense>` → flag (no streaming benefit).
226
+ - `revalidate: 0` everywhere → flag (caching defeated).
227
+ - New `searchParams` use in a page expected to be statically generated → flag.
228
+ - Server Component file imports from a `'use client'` file and uses non-serializable export → flag.
229
+ - Route handler `route.ts` AND `page.tsx` in same segment → flag (illegal).
230
+ - `redirect()` inside Server Action without idempotency consideration → flag.
231
+ - New parallel/intercepted route without `default.tsx` for the parallel slot when no match → flag (will crash).
@@ -0,0 +1,169 @@
1
+ ---
2
+ tags: [observability, logging, tracing, metrics, alerts, slo, opentelemetry]
3
+ stack_signals: []
4
+ summary: |
5
+ Observability design — logs for forensics, metrics for alerts, traces for
6
+ request paths. Every new endpoint, job, or external dependency emits at
7
+ least one metric, structured logs, and propagates trace context.
8
+ when_to_load: |
9
+ Task touches logging, structured logs, tracing (OpenTelemetry, distributed
10
+ tracing), metrics emission, health checks, alerting rules, dashboards, or
11
+ error reporting. Diff including new endpoints, new background jobs, new
12
+ external integrations, or any change that ships behavior-the-team-needs-to-watch
13
+ also qualifies.
14
+ agent_hints: [logic-reviewer, performance, challenger-reviewer]
15
+ ---
16
+
17
+ # Observability — Senior Stance
18
+
19
+ ## When this applies
20
+ Load when task touches: logging, structured logs, tracing (OpenTelemetry, distributed tracing), metrics emission, health checks, alerting rules, dashboards, error reporting. Reviewer auto-loads when diff includes new endpoints, new background jobs, new external integrations, or any change that ships behavior-the-team-needs-to-watch.
21
+
22
+ ## Default Stance
23
+ You can't fix what you can't see. Every new endpoint, job, or external dependency MUST emit at least one metric, one structured log on entry/exit, and propagate trace context. Logs are for forensics, metrics are for alerts, traces are for "where did this request go". The three are complementary, not interchangeable. Sampling is fine; not emitting at all is not.
24
+
25
+ ## Patterns (use these)
26
+
27
+ ### Structured logs
28
+ - JSON format. One event per line. Machine-parseable.
29
+ - Required fields: `timestamp`, `level`, `message`, `service`, `request_id` (or `trace_id`).
30
+ - Domain fields: `user_id`, `task_id`, `endpoint`, `duration_ms`, etc.
31
+ - NEVER log sensitive data: passwords, tokens, full credit card, full SSN. Hash or redact at log boundary.
32
+
33
+ ```json
34
+ {"ts":"2026-05-10T12:34:56Z","level":"info","msg":"user.created","service":"api","trace_id":"abc","user_id":"u_123","duration_ms":42}
35
+ ```
36
+
37
+ ### Trace context propagation
38
+ - Every request gets a `trace_id` at the edge. Pass it downstream via header (`traceparent` per W3C Trace Context, or X-Request-ID).
39
+ - Each service emits its span with its operation, duration, status.
40
+ - Log lines include `trace_id` so you can correlate log events with the trace.
41
+
42
+ ### Metric types
43
+ - **Counter** — monotonic increasing (`requests_total`, `errors_total`). Compute rate via `rate(counter[5m])` in Prometheus.
44
+ - **Gauge** — point-in-time value (`active_connections`, `queue_depth`). Goes up and down.
45
+ - **Histogram** — distribution (request duration, payload size). Compute p50, p95, p99 via `histogram_quantile`.
46
+
47
+ Naming: `<domain>_<entity>_<unit>`: `http_request_duration_seconds`, `db_query_duration_seconds`, `cache_hits_total`. Lowercase snake_case.
48
+
49
+ ### RED method (per request-driven service)
50
+ For every endpoint:
51
+ - **R**ate — requests per second.
52
+ - **E**rrors — error rate (4xx + 5xx, OR business error count).
53
+ - **D**uration — p50 / p95 / p99 latency.
54
+
55
+ Dashboard: one row per endpoint, columns R-E-D. Glance to see "what's broken".
56
+
57
+ ### USE method (per resource)
58
+ For every resource (CPU, memory, disk, connection pool, queue):
59
+ - **U**tilization — % busy.
60
+ - **S**aturation — queue depth / wait time.
61
+ - **E**rrors — count of errors talking to this resource.
62
+
63
+ ### Service Level Objectives (SLOs)
64
+ - Define a metric (e.g., "99.5% of requests < 500ms over 30 days").
65
+ - Track an error budget (1 - SLO target).
66
+ - Alert when error budget burn rate is high (will exhaust before period end).
67
+ - Don't alert on every breach — alert on burn-rate over a window.
68
+
69
+ ### Alerting hygiene
70
+ - **Symptom-based**, not cause-based. "p95 latency above 1s for 5 min" is a symptom alert. "CPU above 80%" is a cause alert (often false-positive).
71
+ - **Actionable** — every alert must have a runbook link explaining what the operator does next.
72
+ - **Escalation tiers** — page only for things that need immediate human action. Slack-channel alerts for things that need attention within hours.
73
+ - **No mystery alerts** — if oncall doesn't know why an alert fired, the alert is broken. Fix or delete.
74
+
75
+ ### Health checks
76
+ - **Liveness** — "is process up". Cheap, never depends on external services. Used by orchestrator (k8s) to restart.
77
+ - **Readiness** — "can serve traffic". May check DB connection, downstream service, cache. Used by load balancer to drain.
78
+ - Don't conflate them. Liveness failing = restart me; readiness failing = stop sending traffic.
79
+
80
+ ### Error reporting
81
+ - Capture error + stack trace + request context (user_id, request_id, path, params).
82
+ - Group by error fingerprint (Sentry, Honeybadger, etc.).
83
+ - Tag with deploy version → "this error started at deploy 4.2.0".
84
+ - Don't capture every error: validation errors and 4xx are noise. Capture 5xx and unexpected exceptions.
85
+
86
+ ## Anti-Patterns (DO NOT)
87
+
88
+ ### Logging without structure
89
+ `logger.info(\`User \${userId} did \${action} at \${time}\`)` → unparseable freeform string.
90
+ **Why it bites:** can't filter, group, or aggregate. Every grep is a one-off.
91
+ **Rule:** structured fields always. `logger.info('user.action', { user_id, action, ts })`.
92
+
93
+ ### Logging sensitive data
94
+ `logger.info('login attempt', { email, password })`. Tokens, secrets, full PII in logs = breach surface.
95
+ **Rule:** redact at log boundary. Reject log lines containing forbidden patterns in CI (regex check).
96
+
97
+ ### Excessive logging on hot paths
98
+ Every request → 50 log lines. At 1k QPS, you're emitting 50k lines/sec. Log pipeline backed up; storage cost spikes.
99
+ **Rule:** ONE log per request entry, ONE per exit (with duration). Detail logs at DEBUG level only, sampled or dynamically enabled.
100
+
101
+ ### Metric names that are unique per request
102
+ `requests_total{user_id="u_12345"}` → cardinality explosion. Prometheus can't handle 1M time-series.
103
+ **Rule:** metric labels are LOW cardinality (≤100 unique values). High-cardinality data goes in logs/traces, not metrics.
104
+
105
+ ### Alerting on every breach
106
+ "Latency exceeded threshold" alert fires once per minute when service is degraded. Operator drowns.
107
+ **Rule:** sustained breach (> 5 min) OR error budget burn rate. Alerts have hysteresis.
108
+
109
+ ### Cause-based alerts everywhere
110
+ "CPU > 80%" — but the service is fine, the autoscaler will handle it.
111
+ **Rule:** alert on user-impacting symptoms. CPU/memory only when no symptom-level alert exists for the failure mode.
112
+
113
+ ### No trace context across services
114
+ Service A has `trace_id=abc`, service B logs without it. Can't follow a request across services.
115
+ **Rule:** propagate trace context via header at every hop. Library / middleware ensures this; don't rely on per-handler discipline.
116
+
117
+ ### Logging plus printing
118
+ `console.log(...)` AND `logger.info(...)` for the same event. Or `print('debug')` left in.
119
+ **Rule:** one logger, configured per environment. No bare `print` / `console.log` in committed code.
120
+
121
+ ### Unmonitored "fire and forget" jobs
122
+ Background job runs, fails silently, no metric emitted. Bug ships when output dashboard shows zero new records for a day.
123
+ **Rule:** every job emits start/finish/duration, success/failure. Alert on missing successful run.
124
+
125
+ ### Health check that always returns 200
126
+ `GET /health → 200 OK` even when DB is down. Load balancer keeps sending traffic to broken instance.
127
+ **Rule:** readiness check actually verifies dependencies it serves with.
128
+
129
+ ### Sampling errors
130
+ 1% sampled error reporting: 99% of errors invisible.
131
+ **Rule:** sample successful traces aggressively (1-10%). Capture errors at 100% (or near-100%).
132
+
133
+ ## Decision Framework
134
+
135
+ | Need | Tool |
136
+ |---|---|
137
+ | "What happened in this specific request?" | Distributed trace + structured logs |
138
+ | "How is the system performing right now?" | Metrics dashboard (RED + USE) |
139
+ | "Wake me when something is broken" | Alerts on SLO burn / error budget |
140
+ | "Where's the error coming from?" | Error reporting (Sentry-class), grouped by fingerprint |
141
+ | New endpoint | Add: 1 entry log, 1 exit log, RED metrics, span |
142
+ | New background job | Add: start/finish logs, duration metric, success/fail counter, dead-letter queue with alert |
143
+ | New external dependency | Add: latency histogram, error counter, circuit-breaker state metric |
144
+ | Slow-query investigation | Trace → see which span is slow → check that span's logs |
145
+
146
+ ## Cost Model
147
+
148
+ | Item | Cost magnitude |
149
+ |---|---|
150
+ | Structured log line, indexed | $0.50-2 per GB ingested (varies by vendor) |
151
+ | Metric time-series with low cardinality | $0.01-0.10 per series per month |
152
+ | Distributed trace span | $0.50-2 per million spans (often sampled to 1-10%) |
153
+ | Error reported & grouped | $0.10-1 per event (Sentry pricing tier) |
154
+ | 1 mystery alert (waking oncall) | Hours of human time + trust erosion |
155
+
156
+ ## Red Flags in Diff
157
+
158
+ - New endpoint without entry/exit log lines or duration metric → flag.
159
+ - New `console.log` / `print` in non-test code → flag.
160
+ - New log line containing `password`, `token`, `secret`, `api_key` substrings → flag immediately (security + observability).
161
+ - New metric label using request-unique IDs (`user_id`, `request_id`) → flag (cardinality blowup).
162
+ - New alerting rule without runbook reference / linked doc → flag.
163
+ - New alert fires on instantaneous breach (no duration window) → flag (flap risk).
164
+ - New external HTTP/DB call without timeout AND without error metric → flag.
165
+ - Health check returning 200 statically → flag.
166
+ - New background job without success/failure metric → flag.
167
+ - Trace context not propagated across new service boundary (header not forwarded) → flag.
168
+ - New error swallowed silently (`try { ... } catch {}`) without log/metric → flag.
169
+ - Logging large payloads (full request body, full DB row) on hot path → flag (cost + PII risk).
@@ -0,0 +1,197 @@
1
+ ---
2
+ tags: [performance, optimization, profiling, latency, throughput, slo]
3
+ stack_signals: []
4
+ summary: |
5
+ Strategy-level performance discipline — measure before you optimize. Profile,
6
+ hypothesize, change one thing, measure again. Pairs with platform-specific
7
+ perf-*.md files.
8
+ when_to_load: |
9
+ Task touches performance-sensitive code, "make it faster" is in scope, a
10
+ perf regression is suspected, or a feature has explicit latency/throughput
11
+ requirements. Preemptive load when CLAUDE.md or task mentions SLO, latency
12
+ budget, "scale to N users", or similar.
13
+ agent_hints: [performance, logic-reviewer, challenger-reviewer]
14
+ ---
15
+
16
+ # Optimization Strategy — Senior Stance
17
+
18
+ ## When this applies
19
+ Load when task touches performance-sensitive code, when "make it faster" is in scope, when a perf regression is suspected, or when a feature has explicit latency/throughput requirements. Performance Agent loads this in addition to platform-specific perf-{stack}.md. Load preemptively when CLAUDE.md or task mentions SLO, latency budget, "scale to N users", or similar.
20
+
21
+ ## Default Stance
22
+ Don't optimize what you haven't measured. Most "obviously slow" code is fast enough; most "obviously fine" code has surprises. Profile first, hypothesize, change one thing, measure again. Optimization without measurement is decoration. Once you've measured, fix the biggest hot spot — the long tail rarely matters.
23
+
24
+ The order: **correct → tested → measured → optimized**. Skip steps and you're guessing.
25
+
26
+ ## Patterns (use these)
27
+
28
+ ### Measure before, measure after
29
+ - Establish a baseline: what's slow, by how much, under what load?
30
+ - Make the change.
31
+ - Re-measure under the same conditions.
32
+ - If you can't tell the difference, you didn't optimize anything.
33
+
34
+ ### Profile to find hot spots
35
+ Tools by stack:
36
+ - Node.js: `--prof` + processed with `--prof-process`, or `clinic.js`, or Chrome DevTools.
37
+ - Python: `cProfile` + `snakeviz` or `py-spy` (sampling, low overhead, prod-safe).
38
+ - JVM: `async-profiler`, `JFR`.
39
+ - Go: `pprof` (built-in).
40
+ - Browser: Chrome DevTools Performance tab; Lighthouse for page-level.
41
+ - DB: EXPLAIN ANALYZE; `pg_stat_statements`.
42
+
43
+ Look for: tall stack frames, repeated work per call, calls into expensive primitives (DB, network, parse).
44
+
45
+ ### Latency vs throughput
46
+ Different goals, different fixes:
47
+ - **Latency** (single-request time): reduce work in the request path. Cache, denormalize, precompute, prefetch.
48
+ - **Throughput** (aggregate ops/sec): reduce contention, parallelize, batch, queue.
49
+ A change that improves latency may hurt throughput (e.g., always-fresh cache lookup beats stale-while-revalidate for latency, but more DB load → worse throughput).
50
+
51
+ ### Big-O matters when N is large
52
+ - 1000 items in a list: O(N) vs O(N²) matters → microseconds vs milliseconds.
53
+ - 1M items: O(N) vs O(N²) matters → seconds vs minutes.
54
+ - 10 items: O(N²) is fine; readability beats cleverness.
55
+ Don't optimize O(N) → O(log N) when N=10. Don't tolerate O(N²) when N=10K.
56
+
57
+ ### Hot loop discipline
58
+ For code that runs millions of times per second:
59
+ - Avoid allocations inside the loop (object creation, array spread, string concat).
60
+ - Avoid closures/functions created per iteration.
61
+ - Hoist invariants out of the loop.
62
+ - Batch where possible.
63
+
64
+ For code that runs 100x: clarity beats micro-optimization.
65
+
66
+ ### Caching as last resort, not first
67
+ Cache is hard (invalidation, staleness, stampedes — see caching.md). Add a cache only when:
68
+ - The underlying call is measurably expensive.
69
+ - The data has clear invalidation semantics.
70
+ - You've designed how the cache empties.
71
+ - The hit rate justifies the complexity.
72
+
73
+ Often the right answer is "fix the slow query" (add index, denormalize, materialize) — not "cache around it".
74
+
75
+ ### Database-side optimization first
76
+ For data-heavy operations, the DB is usually the bottleneck. Before app-side caching:
77
+ - Add indexes for new query shapes.
78
+ - Rewrite N+1 as JOIN or batch loader.
79
+ - Use materialized views for expensive aggregations.
80
+ - Consider read replicas for read-heavy paths.
81
+
82
+ ### Bundle-size optimization (frontend)
83
+ - Measure with `webpack-bundle-analyzer`, `vite-plugin-visualizer`, `next build` output.
84
+ - Code-split routes (lazy / dynamic imports).
85
+ - Remove unused deps; replace heavy libs (moment → date-fns → native Intl).
86
+ - Tree-shake-friendly imports (`import { format } from 'date-fns'` not `import _ from 'date-fns'`).
87
+
88
+ ### Render performance (frontend)
89
+ - Profile with React DevTools Profiler / Vue Devtools.
90
+ - Look for unnecessary rerenders. Memo only after profiling identifies the cost.
91
+ - Move expensive work off render path: `useMemo` for compute, `useCallback` to stabilize refs, `useDeferredValue` for non-urgent updates.
92
+ - React Compiler (when enabled) handles most of this; manual memo becomes anti-pattern.
93
+
94
+ ### Networking
95
+ - Reduce round trips: batch where API allows.
96
+ - Compression: gzip / brotli for text responses.
97
+ - HTTP/2 multiplexing eliminates per-request connection overhead.
98
+ - CDN for static assets and edge-cacheable dynamic content.
99
+ - Connection pooling for outbound HTTP.
100
+
101
+ ## Anti-Patterns (DO NOT)
102
+
103
+ ### Optimize without measuring
104
+ "This loop is slow, let me optimize" — without profiling. Spend a day; benchmark says no improvement.
105
+ **Rule:** profile first. The hot spot is rarely where you think.
106
+
107
+ ### Micro-optimize cold paths
108
+ Code runs 5 times per day; spend a week making it 20% faster.
109
+ **Rule:** ROI matters. Optimize where the wall-clock time lives.
110
+
111
+ ### Cache everything
112
+ "Add cache to make it faster" → invalidation bugs ship → stale data shown to users → harder bug to fix than the original perf.
113
+ **Rule:** cache only what's measurably expensive AND has clear invalidation. Otherwise fix the underlying cost.
114
+
115
+ ### Premature parallelization
116
+ Parallel implementation is harder to debug, harder to read, harder to maintain. If serial is fast enough, leave it alone.
117
+ **Rule:** parallelize after measuring serial cost.
118
+
119
+ ### Optimize without context
120
+ Same code path: 50ms in dev, 5ms in prod (cached at scale). Optimizing dev path costs eng time, prod doesn't care.
121
+ **Rule:** measure under prod-shaped load.
122
+
123
+ ### Synthetic benchmarks unrepresentative of real load
124
+ Loop 1M times calling `f(0)` — JIT detects constant, eliminates the call. Benchmark says "f is free". Real callers vary input → JIT doesn't help → f is expensive.
125
+ **Rule:** realistic input distribution; warm-up; multiple runs.
126
+
127
+ ### Optimizing without acceptance criteria
128
+ "Make it faster" with no target. Spend forever; never know when to stop.
129
+ **Rule:** define the target. p95 < 200ms. Bundle < 100KB. Then stop when met.
130
+
131
+ ### "Faster" code that breaks invariants
132
+ Removed a defensive check "for perf"; turns out the check was load-bearing in an edge case.
133
+ **Rule:** measure, change, re-measure, AND re-test. Performance change must keep tests passing.
134
+
135
+ ### Optimizing the wrong layer
136
+ App caches DB result; DB query was actually fast; the slow part was the JSON serialization. Cache helps a little; fixing the serialization helps a lot.
137
+ **Rule:** profile points at the layer; fix at the layer the profile points to.
138
+
139
+ ### Memoizing pure functions that are already cheap
140
+ `useMemo(() => x + y, [x, y])` — overhead of memo > cost of `+`. Especially with React Compiler.
141
+ **Rule:** memo when profile shows it pays. Otherwise it's noise.
142
+
143
+ ### "10x faster" claims without measurement
144
+ PR description says "10x faster". No benchmark. No before/after.
145
+ **Rule:** include measurement in the PR. Numbers, not vibes.
146
+
147
+ ### Killing readability for micro-perf
148
+ `for (let i = 0, l = arr.length; i < l; ++i)` instead of `for (const x of arr)` — saves nanoseconds, costs reader 5 seconds. Hot loop? OK. Cold path? Don't.
149
+
150
+ ## Decision Framework
151
+
152
+ | Symptom | Investigation order |
153
+ |---|---|
154
+ | Slow request handler | Profile request path → identify slowest span → fix slowest |
155
+ | Slow page load | Lighthouse → bundle analysis → render profile → fix biggest |
156
+ | High DB latency | EXPLAIN slowest queries → indexes → denormalize → cache as last resort |
157
+ | OOM under load | Heap profile → leaks → unbounded data structures → caching with proper bounds |
158
+ | CPU pinned | Profile → hot function → algorithmic vs micro fix |
159
+ | Throughput plateau | Identify bottleneck (CPU? IO? DB pool? Lock contention?) → fix that one |
160
+ | Tail latency p99 high | Find: GC pauses? Cache miss? DB connection wait? Each has different fix |
161
+ | Slow boot/cold start | Lazy-load non-critical modules; warm pools; provisioned concurrency for serverless |
162
+
163
+ ## Cost Model
164
+
165
+ | Optimization | Typical effort | Typical gain |
166
+ |---|---|---|
167
+ | Add missing DB index | hours | 10-1000x query speedup |
168
+ | Fix N+1 with JOIN | hours | 10-100x for affected request |
169
+ | Add Redis cache for hot read | day | 5-10x latency, IF hit rate is high |
170
+ | Code-split a heavy route | hours | 30-70% initial bundle reduction |
171
+ | Replace heavy lib (moment → date-fns) | hours | 50-80% lib size reduction |
172
+ | Memoize expensive React component | minutes | 0% if not on hot path; 10-30% if it is |
173
+ | Refactor algorithm O(N²) → O(N log N) | day-week | massive at scale, zero at small N |
174
+ | Migrate to faster runtime / lib | weeks-months | 10-50%; high risk |
175
+ | Add connection pool | hours | reduces tail latency markedly under load |
176
+
177
+ | Anti-pattern | Cost when wrong |
178
+ |---|---|
179
+ | Cache hides slow query | Quality bug shipped via stale data; original problem still there |
180
+ | Premature parallelism | Code 5x harder to read, race conditions, no measured win |
181
+ | Micro-opt over readability | Slowed team velocity; bug-prone code; nano gain |
182
+ | No measurement before / after | Could be ZERO actual improvement, you have no idea |
183
+
184
+ ## Red Flags in Diff
185
+
186
+ - New `useMemo` / `useCallback` without a profile/comment justifying it (especially with React Compiler enabled) → flag.
187
+ - New cache layer added without a TTL OR without invalidation strategy → flag (see caching.md).
188
+ - New parallel code (Promise.all, asyncio.gather) without bound on concurrency → flag.
189
+ - New "optimization" PR without before/after benchmark numbers → flag.
190
+ - New micro-optimized code (manual loops, hand-rolled algorithms) replacing a clear stdlib call without measurement → flag.
191
+ - Removed validation / safety check labelled "for perf" → flag.
192
+ - Hardcoded "magic number" tunable (timeout, batch size) without comment about source → flag.
193
+ - Profile-driven changes affecting hot path without test coverage on the changed paths → flag (perf regression risk).
194
+ - New `setImmediate` / `setTimeout(0)` claims to "improve perf" → flag (almost always wrong fix).
195
+ - Heavy library added in a code path that already had a lighter alternative → flag.
196
+ - Heavy operation moved into render / hot loop → flag.
197
+ - New "fast path" with subtly different semantics from the slow path → flag (correctness drift risk).
@@ -0,0 +1,62 @@
1
+ ---
2
+ tags: [performance, flutter, dart, widget-rebuild, mobile]
3
+ stack_signals:
4
+ - language: [dart]
5
+ - project_type: [mobile, frontend-app]
6
+ summary: |
7
+ Flutter / Dart performance checklist — const constructors, build() scope,
8
+ setState() placement, list virtualization, image caching.
9
+ when_to_load: |
10
+ Task touches Flutter widgets, Dart code with perf concerns, or mobile-app
11
+ scale targets. Diff in *.dart with widget tree changes, setState() calls,
12
+ or list/scroll views.
13
+ agent_hints: [performance, logic-reviewer, ui-consistency]
14
+ ---
15
+
16
+ # Performance: Flutter / Dart
17
+
18
+ ## Widget Rebuilds
19
+ - Missing `const` constructors on stateless widgets and static children
20
+ - Large `build()` methods that should be split into smaller widgets
21
+ - `setState()` at too high a level (rebuilds entire subtree instead of targeted widget)
22
+ - Missing `const` keyword on widget constructors with no dynamic params
23
+ - Heavy computation inside `build()` — move to `initState()` or compute outside
24
+
25
+ ## Lists & Scrolling
26
+ - `ListView(children: [...])` with 20+ items — use `ListView.builder` instead
27
+ - Missing `itemExtent` or `prototypeItem` on large uniform lists
28
+ - `SingleChildScrollView` wrapping a `Column` with many children — use `ListView`
29
+ - Missing `cacheExtent` tuning for heavy list items
30
+ - `IntrinsicWidth`/`IntrinsicHeight` in lists — causes expensive two-pass layout
31
+
32
+ ## Animation & Painting
33
+ - Missing `RepaintBoundary` to isolate frequently repainting regions
34
+ - Using `Opacity` widget — use `FadeTransition` or `AnimatedOpacity` instead (Opacity forces offscreen buffer via saveLayer)
35
+ - `ShaderMask`, `ColorFilter`, `ClipPath` with non-default clipBehavior trigger expensive saveLayer calls
36
+ - First-run shader compilation jank — consider Impeller (default on iOS) or `--bundle-sksl-path` for Skia
37
+
38
+ ## Images & Assets
39
+ - No `cacheWidth`/`cacheHeight` on large images (decode full resolution for small display)
40
+ - Missing `CachedNetworkImage` — raw `Image.network` without caching
41
+ - Large images loaded without resize — use `ResizeImage` or server-side thumbnails
42
+ - SVG assets that could be compiled to code via `flutter_svg` or replaced with icons
43
+
44
+ ## State Management
45
+ - Riverpod/BLoC/Provider at too high a scope (rebuilds unrelated widgets)
46
+ - Missing `select()` / `Selector` — listening to entire state when only one field needed
47
+ - `FutureBuilder` / `StreamBuilder` recreating Future/Stream on every build (store in variable or initState)
48
+ - Missing `GlobalKey` cleanup — excessive GlobalKeys kept alive unnecessarily
49
+
50
+ ## Async & Resources
51
+ - Missing `dispose()` for controllers, streams, animation controllers
52
+ - `Timer.periodic` without cancel in `dispose()`
53
+ - Heavy work on main isolate (image processing, JSON parsing of large payloads) — use `compute()` or `Isolate.run()`
54
+ - Network request deduplication — multiple widgets triggering same fetch without caching
55
+
56
+ ## Platform & Size
57
+ - Unused packages in `pubspec.yaml` (inflates app size)
58
+ - Tree-shaking for icon fonts is default in release mode — verify not disabled
59
+ - Platform channels called in hot path without caching result
60
+
61
+ ## Profiling Note
62
+ Always profile in **profile or release mode** — debug mode has vastly different performance characteristics. Use DevTools Performance tab or `flutter run --profile`.