@chinhae/atw-cli 0.6.0 → 0.7.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 +128 -451
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +24 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/commands/channel/adapters/claude.d.ts +44 -2
- package/dist/commands/channel/adapters/claude.d.ts.map +1 -1
- package/dist/commands/channel/adapters/claude.js +124 -23
- package/dist/commands/channel/adapters/claude.js.map +1 -1
- package/dist/commands/channel/adapters/codex.d.ts +36 -6
- package/dist/commands/channel/adapters/codex.d.ts.map +1 -1
- package/dist/commands/channel/adapters/codex.js +150 -10
- package/dist/commands/channel/adapters/codex.js.map +1 -1
- package/dist/commands/channel/adapters/index.d.ts +33 -4
- package/dist/commands/channel/adapters/index.d.ts.map +1 -1
- package/dist/commands/channel/adapters/index.js +159 -6
- package/dist/commands/channel/adapters/index.js.map +1 -1
- package/dist/commands/channel/adapters/omp.d.ts +106 -0
- package/dist/commands/channel/adapters/omp.d.ts.map +1 -0
- package/dist/commands/channel/adapters/omp.js +405 -0
- package/dist/commands/channel/adapters/omp.js.map +1 -0
- package/dist/commands/channel/adapters/pi.d.ts +123 -0
- package/dist/commands/channel/adapters/pi.d.ts.map +1 -0
- package/dist/commands/channel/adapters/pi.js +387 -0
- package/dist/commands/channel/adapters/pi.js.map +1 -0
- package/dist/commands/channel/adapters/types.d.ts +16 -0
- package/dist/commands/channel/adapters/types.d.ts.map +1 -1
- package/dist/commands/channel/agent-loader.d.ts +1 -0
- package/dist/commands/channel/agent-loader.d.ts.map +1 -1
- package/dist/commands/channel/agent-loader.js +45 -5
- package/dist/commands/channel/agent-loader.js.map +1 -1
- package/dist/commands/channel/dev-parse-trace.d.ts +1 -1
- package/dist/commands/channel/dev-parse-trace.d.ts.map +1 -1
- package/dist/commands/channel/dev-parse-trace.js +25 -2
- package/dist/commands/channel/dev-parse-trace.js.map +1 -1
- package/dist/commands/channel/index.d.ts.map +1 -1
- package/dist/commands/channel/index.js +44 -12
- package/dist/commands/channel/index.js.map +1 -1
- package/dist/commands/channel/run.d.ts +39 -2
- package/dist/commands/channel/run.d.ts.map +1 -1
- package/dist/commands/channel/run.js +227 -68
- package/dist/commands/channel/run.js.map +1 -1
- package/dist/commands/channel/spawn.d.ts +7 -2
- package/dist/commands/channel/spawn.d.ts.map +1 -1
- package/dist/commands/channel/spawn.js +35 -1
- package/dist/commands/channel/spawn.js.map +1 -1
- package/dist/commands/channel/store/events.d.ts +2 -2
- package/dist/commands/channel/store/events.d.ts.map +1 -1
- package/dist/commands/channel/store/events.js +1 -1
- package/dist/commands/channel/store/events.js.map +1 -1
- package/dist/commands/channel/supervisor/shutdown.d.ts +2 -0
- package/dist/commands/channel/supervisor/shutdown.d.ts.map +1 -1
- package/dist/commands/channel/supervisor/shutdown.js +6 -1
- package/dist/commands/channel/supervisor/shutdown.js.map +1 -1
- package/dist/commands/channel/supervisor.d.ts +2 -2
- package/dist/commands/channel/supervisor.d.ts.map +1 -1
- package/dist/commands/channel/supervisor.js +26 -3
- package/dist/commands/channel/supervisor.js.map +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +6 -1
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/mem.d.ts +2 -1
- package/dist/commands/mem.d.ts.map +1 -1
- package/dist/commands/mem.js +162 -13
- package/dist/commands/mem.js.map +1 -1
- package/dist/commands/mode.d.ts.map +1 -1
- package/dist/commands/mode.js +5 -3
- package/dist/commands/mode.js.map +1 -1
- package/dist/commands/uninstall.d.ts +5 -3
- package/dist/commands/uninstall.d.ts.map +1 -1
- package/dist/commands/uninstall.js +40 -16
- package/dist/commands/uninstall.js.map +1 -1
- package/dist/commands/update.d.ts +34 -0
- package/dist/commands/update.d.ts.map +1 -1
- package/dist/commands/update.js +209 -11
- package/dist/commands/update.js.map +1 -1
- package/dist/commands/verify.d.ts +18 -0
- package/dist/commands/verify.d.ts.map +1 -0
- package/dist/commands/verify.js +75 -0
- package/dist/commands/verify.js.map +1 -0
- package/dist/configurators/index.d.ts.map +1 -1
- package/dist/configurators/index.js +3 -2
- package/dist/configurators/index.js.map +1 -1
- package/dist/configurators/omp.d.ts +22 -0
- package/dist/configurators/omp.d.ts.map +1 -1
- package/dist/configurators/omp.js +101 -2
- package/dist/configurators/omp.js.map +1 -1
- package/dist/configurators/shared.d.ts.map +1 -1
- package/dist/configurators/shared.js +39 -7
- package/dist/configurators/shared.js.map +1 -1
- package/dist/configurators/workflow.d.ts.map +1 -1
- package/dist/configurators/workflow.js +5 -1
- package/dist/configurators/workflow.js.map +1 -1
- package/dist/constants/paths.d.ts +4 -0
- package/dist/constants/paths.d.ts.map +1 -1
- package/dist/constants/paths.js +4 -0
- package/dist/constants/paths.js.map +1 -1
- package/dist/migrations/manifests/0.7.0.json +1378 -0
- package/dist/templates/atw/definition-of-done.md +65 -0
- package/dist/templates/atw/gitignore.txt +7 -0
- package/dist/templates/atw/index.d.ts +11 -0
- package/dist/templates/atw/index.d.ts.map +1 -1
- package/dist/templates/atw/index.js +22 -0
- package/dist/templates/atw/index.js.map +1 -1
- package/dist/templates/atw/scripts/common/active_task.py +2 -2
- package/dist/templates/atw/scripts/common/workflow_phase.py +33 -0
- package/dist/templates/atw/scripts/cross_model_review.py +3312 -0
- package/dist/templates/atw/scripts/decision_log.py +2002 -0
- package/dist/templates/atw/scripts/floor_guard.py +642 -0
- package/dist/templates/atw/scripts/task.py +25 -6
- package/dist/templates/atw/scripts/tickets.py +480 -10
- package/dist/templates/atw/workflow-general.md +6 -1
- package/dist/templates/atw/workflow.md +346 -26
- package/dist/templates/claude/settings.json +12 -0
- package/dist/templates/codex/hooks/session-start.py +2 -2
- package/dist/templates/common/bundled-skills/atw-askme/SKILL.md +1 -1
- package/dist/templates/common/bundled-skills/atw-askme-with-docs/SKILL.md +1 -1
- package/dist/templates/common/bundled-skills/atw-channel/SKILL.md +5 -2
- package/dist/templates/common/bundled-skills/atw-channel/references/command-reference.md +44 -5
- package/dist/templates/common/bundled-skills/atw-channel/references/progress-debugging.md +9 -0
- package/dist/templates/common/bundled-skills/atw-channel/references/workers.md +48 -4
- package/dist/templates/common/bundled-skills/atw-code-review/SKILL.md +78 -3
- package/dist/templates/common/bundled-skills/atw-codebase-design/SKILL.md +2 -2
- package/dist/templates/common/bundled-skills/atw-codebase-design/{DESIGN-IT-TWICE.md → design-it-twice.md} +3 -3
- package/dist/templates/common/bundled-skills/atw-cross-model-review/SKILL.md +361 -0
- package/dist/templates/common/bundled-skills/atw-cross-model-review/agents/openai.yaml +5 -0
- package/dist/templates/common/bundled-skills/atw-cross-model-review/references/four-tiers.md +42 -0
- package/dist/templates/common/bundled-skills/atw-cross-model-review/references/log-review-questions.md +38 -0
- package/dist/templates/common/bundled-skills/atw-cross-model-review/references/questions.md +54 -0
- package/dist/templates/common/bundled-skills/atw-cross-model-review/references/recheck-questions.md +42 -0
- package/dist/templates/common/bundled-skills/atw-cross-model-review/references/report-format.md +22 -0
- package/dist/templates/common/bundled-skills/atw-diagnosing-bugs/SKILL.md +13 -2
- package/dist/templates/common/bundled-skills/atw-domain-modeling/SKILL.md +2 -2
- package/dist/templates/common/bundled-skills/atw-handoff/SKILL.md +1 -1
- package/dist/templates/common/bundled-skills/atw-implement/SKILL.md +37 -1
- package/dist/templates/common/bundled-skills/atw-implement-all/SKILL.md +219 -24
- package/dist/templates/common/bundled-skills/atw-implement-spec/SKILL.md +237 -18
- package/dist/templates/common/bundled-skills/atw-improve-codebase-architecture/SKILL.md +2 -2
- package/dist/templates/common/bundled-skills/atw-init-repo/SKILL.md +1 -1
- package/dist/templates/common/bundled-skills/atw-init-repo/references/issue-tracker-atw-local.md +7 -1
- package/dist/templates/common/bundled-skills/atw-init-repo/references/issue-tracker-atw.md +7 -1
- package/dist/templates/common/bundled-skills/atw-map/SKILL.md +1 -1
- package/dist/templates/common/bundled-skills/atw-meta/SKILL.md +1 -1
- package/dist/templates/common/bundled-skills/atw-meta/references/customize-local/add-project-local-conventions.md +4 -3
- package/dist/templates/common/bundled-skills/atw-meta/references/customize-local/change-skills-or-commands.md +21 -16
- package/dist/templates/common/bundled-skills/atw-meta/references/customize-local/change-task-lifecycle.md +1 -1
- package/dist/templates/common/bundled-skills/atw-meta/references/customize-local/overview.md +1 -0
- package/dist/templates/common/bundled-skills/atw-meta/references/local-architecture/task-system.md +2 -1
- package/dist/templates/common/bundled-skills/atw-meta/references/platform-files/hooks-and-settings.md +1 -0
- package/dist/templates/common/bundled-skills/atw-noob-mode/SKILL.md +1 -1
- package/dist/templates/common/bundled-skills/atw-performance/SKILL.md +134 -0
- package/dist/templates/common/bundled-skills/atw-performance/agents/openai.yaml +3 -0
- package/dist/templates/common/bundled-skills/atw-performance/references/optimization-patterns.md +250 -0
- package/dist/templates/common/bundled-skills/atw-performance/references/performance-checklist.md +238 -0
- package/dist/templates/common/bundled-skills/atw-performance/references/performance-optimization.md +264 -0
- package/dist/templates/common/bundled-skills/atw-prototype/SKILL.md +3 -3
- package/dist/templates/common/bundled-skills/atw-prototype/{LOGIC.md → logic.md} +1 -1
- package/dist/templates/common/bundled-skills/atw-prototype/references/design-direction.md +1 -1
- package/dist/templates/common/bundled-skills/atw-prototype/references/style-explorer.md +1 -1
- package/dist/templates/common/bundled-skills/atw-prototype/{UI.md → ui.md} +4 -4
- package/dist/templates/common/bundled-skills/atw-reflect/SKILL.md +77 -0
- package/dist/templates/common/bundled-skills/atw-reflect/agents/openai.yaml +5 -0
- package/dist/templates/common/bundled-skills/atw-reflect/references/divergent-reviewer.md +38 -0
- package/dist/templates/common/bundled-skills/atw-reflect/references/judgment-reviewer.md +37 -0
- package/dist/templates/common/bundled-skills/atw-reflect/references/synthesizer.md +63 -0
- package/dist/templates/common/bundled-skills/atw-reflect/references/tooling-reviewer.md +48 -0
- package/dist/templates/common/bundled-skills/atw-security/SKILL.md +248 -0
- package/dist/templates/common/bundled-skills/atw-security/agents/openai.yaml +3 -0
- package/dist/templates/common/bundled-skills/atw-security/references/hardening-patterns.md +324 -0
- package/dist/templates/common/bundled-skills/atw-security/references/security-checklist.md +248 -0
- package/dist/templates/common/bundled-skills/atw-session-insight/SKILL.md +1 -1
- package/dist/templates/common/bundled-skills/atw-session-insight/references/cli-quick-reference.md +11 -2
- package/dist/templates/common/bundled-skills/atw-spec/SKILL.md +1 -1
- package/dist/templates/common/bundled-skills/atw-tdd/SKILL.md +25 -0
- package/dist/templates/common/bundled-skills/atw-tickets/SKILL.md +1 -1
- package/dist/templates/common/bundled-skills/atw-triage/SKILL.md +5 -5
- package/dist/templates/common/bundled-skills/atw-ui/SKILL.md +54 -1
- package/dist/templates/common/bundled-skills/atw-ui/references/accessibility-checklist.md +160 -0
- package/dist/templates/common/bundled-skills/atw-verify-skill/SKILL.md +58 -0
- package/dist/templates/common/bundled-skills/atw-verify-skill/agents/openai.yaml +5 -0
- package/dist/templates/common/bundled-skills/atw-verify-skill/references/feature-map-example/README.md +47 -0
- package/dist/templates/common/bundled-skills/atw-verify-skill/references/feature-map-example/create-note.md +39 -0
- package/dist/templates/common/bundled-skills/atw-verify-skill/references/feature-map-example/search.md +45 -0
- package/dist/templates/common/bundled-skills/atw-verify-skill/references/generate.md +107 -0
- package/dist/templates/common/bundled-skills/atw-verify-skill/references/health-check.md +100 -0
- package/dist/templates/common/bundled-skills/atw-verify-skill/references/verify-skill-template.md +99 -0
- package/dist/templates/common/bundled-skills/atw-what-happened/SKILL.md +49 -0
- package/dist/templates/common/bundled-skills/atw-what-happened/agents/openai.yaml +5 -0
- package/dist/templates/common/commands/finish-work.md +10 -1
- package/dist/templates/common/commands/start.md +3 -1
- package/dist/templates/common/skills/update-spec.md +40 -4
- package/dist/templates/copilot/hooks/session-start.py +2 -2
- package/dist/templates/dsh/DSH.md +2 -2
- package/dist/templates/markdown/spec/backend/quality-guidelines.md.txt +10 -0
- package/dist/templates/markdown/spec/frontend/quality-guidelines.md.txt +10 -0
- package/dist/templates/omp/config.yml +15 -0
- package/dist/templates/omp/extensions/atw/index.ts.txt +135 -18
- package/dist/templates/omp/index.d.ts +1 -0
- package/dist/templates/omp/index.d.ts.map +1 -1
- package/dist/templates/omp/index.js +3 -0
- package/dist/templates/omp/index.js.map +1 -1
- package/dist/templates/opencode/lib/session-utils.js +4 -2
- package/dist/templates/opencode/plugins/inject-workflow-state.js +5 -1
- package/dist/templates/pi/extensions/atw/index.ts.txt +80 -7
- package/dist/templates/shared-hooks/index.d.ts +8 -1
- package/dist/templates/shared-hooks/index.d.ts.map +1 -1
- package/dist/templates/shared-hooks/index.js +8 -0
- package/dist/templates/shared-hooks/index.js.map +1 -1
- package/dist/templates/shared-hooks/inject-workflow-state.py +131 -14
- package/dist/templates/shared-hooks/session-start.py +4 -2
- package/dist/templates/shared-hooks/show-notice.py +135 -0
- package/dist/templates/snow/SNOW.md +1 -1
- package/dist/utils/initialized.d.ts +9 -0
- package/dist/utils/initialized.d.ts.map +1 -0
- package/dist/utils/initialized.js +24 -0
- package/dist/utils/initialized.js.map +1 -0
- package/dist/utils/managed-removal.d.ts +2 -0
- package/dist/utils/managed-removal.d.ts.map +1 -1
- package/dist/utils/managed-removal.js +23 -3
- package/dist/utils/managed-removal.js.map +1 -1
- package/dist/utils/optional-skills.d.ts +2 -0
- package/dist/utils/optional-skills.d.ts.map +1 -1
- package/dist/utils/optional-skills.js +1 -1
- package/dist/utils/optional-skills.js.map +1 -1
- package/dist/utils/template-hash.d.ts.map +1 -1
- package/dist/utils/template-hash.js +3 -0
- package/dist/utils/template-hash.js.map +1 -1
- package/dist/utils/uninstall-scrubbers.d.ts +10 -0
- package/dist/utils/uninstall-scrubbers.d.ts.map +1 -1
- package/dist/utils/uninstall-scrubbers.js +50 -0
- package/dist/utils/uninstall-scrubbers.js.map +1 -1
- package/dist/utils/verify-entries.d.ts +51 -0
- package/dist/utils/verify-entries.d.ts.map +1 -0
- package/dist/utils/verify-entries.js +399 -0
- package/dist/utils/verify-entries.js.map +1 -0
- package/package.json +2 -2
- /package/dist/templates/common/bundled-skills/atw-codebase-design/{DEEPENING.md → deepening.md} +0 -0
- /package/dist/templates/common/bundled-skills/atw-domain-modeling/{ADR-FORMAT.md → adr-format.md} +0 -0
- /package/dist/templates/common/bundled-skills/atw-domain-modeling/{GLOSSARY-FORMAT.md → glossary-format.md} +0 -0
- /package/dist/templates/common/bundled-skills/atw-improve-codebase-architecture/{HTML-REPORT.md → html-report.md} +0 -0
- /package/dist/templates/common/bundled-skills/atw-triage/{AGENT-BRIEF.md → agent-brief.md} +0 -0
- /package/dist/templates/common/bundled-skills/atw-triage/{OUT-OF-SCOPE.md → out-of-scope.md} +0 -0
package/dist/templates/common/bundled-skills/atw-performance/references/performance-checklist.md
ADDED
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
# Performance Checklist
|
|
2
|
+
|
|
3
|
+
Quick reference checklist for web application performance. Use alongside the `atw-performance` skill.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
- [Core Web Vitals Targets](#core-web-vitals-targets)
|
|
8
|
+
- [TTFB Diagnosis](#ttfb-diagnosis)
|
|
9
|
+
- [Frontend Checklist](#frontend-checklist)
|
|
10
|
+
- [Backend Checklist](#backend-checklist)
|
|
11
|
+
- [Caching Strategies](#caching-strategies)
|
|
12
|
+
- [Measurement Commands](#measurement-commands)
|
|
13
|
+
- [Common Anti-Patterns](#common-anti-patterns)
|
|
14
|
+
|
|
15
|
+
## Core Web Vitals Targets
|
|
16
|
+
|
|
17
|
+
| Metric | Good | Needs Work | Poor |
|
|
18
|
+
|--------|------|------------|------|
|
|
19
|
+
| LCP (Largest Contentful Paint) | ≤ 2.5s | ≤ 4.0s | > 4.0s |
|
|
20
|
+
| INP (Interaction to Next Paint) | ≤ 200ms | ≤ 500ms | > 500ms |
|
|
21
|
+
| CLS (Cumulative Layout Shift) | ≤ 0.1 | ≤ 0.25 | > 0.25 |
|
|
22
|
+
|
|
23
|
+
## TTFB Diagnosis
|
|
24
|
+
|
|
25
|
+
When TTFB is slow (> 800ms), check each component in DevTools Network waterfall:
|
|
26
|
+
|
|
27
|
+
- [ ] **DNS resolution** slow → add `<link rel="dns-prefetch">` or `<link rel="preconnect">` for known origins
|
|
28
|
+
- [ ] **TCP/TLS handshake** slow → enable HTTP/2, consider edge deployment, verify keep-alive
|
|
29
|
+
- [ ] **Server processing** slow → profile backend, check slow queries, add caching
|
|
30
|
+
|
|
31
|
+
## Frontend Checklist
|
|
32
|
+
|
|
33
|
+
### Images
|
|
34
|
+
- [ ] Images use modern formats (WebP, AVIF)
|
|
35
|
+
- [ ] Images are responsively sized (`srcset` and `sizes`)
|
|
36
|
+
- [ ] Images and `<source>` elements have explicit `width` and `height` (prevents CLS in art direction)
|
|
37
|
+
- [ ] Below-the-fold images use `loading="lazy"` and `decoding="async"`
|
|
38
|
+
- [ ] Hero/LCP images use `fetchpriority="high"` and no lazy loading
|
|
39
|
+
|
|
40
|
+
### JavaScript
|
|
41
|
+
- [ ] Bundle size under 200KB gzipped (initial load)
|
|
42
|
+
- [ ] Code splitting with dynamic `import()` for routes and heavy features
|
|
43
|
+
- [ ] Tree shaking enabled (verify dependency ships ESM and marks `sideEffects: false`)
|
|
44
|
+
- [ ] No blocking JavaScript in `<head>` (use `defer` or `async`)
|
|
45
|
+
- [ ] Heavy computation offloaded to Web Workers (if applicable)
|
|
46
|
+
- [ ] `React.memo()` on expensive components that re-render with same props
|
|
47
|
+
- [ ] `useMemo()` / `useCallback()` only where profiling shows benefit
|
|
48
|
+
- [ ] Long tasks (> 50ms) broken up to keep the main thread available — main lever for INP
|
|
49
|
+
- [ ] `yieldToMain` pattern used inside long-running loops so input events can run between chunks
|
|
50
|
+
- [ ] Modern scheduling APIs used where available: `scheduler.yield()` (preferred), `scheduler.postTask()` with priorities, `isInputPending()` to yield only when needed
|
|
51
|
+
- [ ] `requestIdleCallback` for deferrable, non-urgent work (analytics flush, prefetch, warmup)
|
|
52
|
+
- [ ] Non-critical work deferred out of event handlers (e.g. analytics, logging) so the response to the interaction is not delayed
|
|
53
|
+
- [ ] Third-party scripts loaded with `async` / `defer`, audited for size, and fronted by a facade when heavy (chat widgets, embeds)
|
|
54
|
+
|
|
55
|
+
### CSS
|
|
56
|
+
- [ ] Critical CSS inlined or preloaded
|
|
57
|
+
- [ ] No render-blocking CSS for non-critical styles
|
|
58
|
+
- [ ] No CSS-in-JS runtime cost in production (use extraction)
|
|
59
|
+
|
|
60
|
+
### Fonts
|
|
61
|
+
- [ ] Limited to 2–3 font families, 2–3 weights each (every additional weight is another request)
|
|
62
|
+
- [ ] WOFF2 format only (smallest, universal support — skip WOFF/TTF/EOT)
|
|
63
|
+
- [ ] Self-hosted when possible (third-party font CDNs add DNS + TCP + TLS round-trips)
|
|
64
|
+
- [ ] LCP-critical fonts preloaded: `<link rel="preload" as="font" type="font/woff2" crossorigin>`
|
|
65
|
+
- [ ] `font-display: swap` (or `optional` for non-critical) to avoid FOIT blocking render
|
|
66
|
+
- [ ] Subsetted via `unicode-range` to ship only the glyphs each page needs
|
|
67
|
+
- [ ] Variable fonts considered when multiple weights/styles are required (one file replaces many)
|
|
68
|
+
- [ ] Fallback font metrics adjusted with `size-adjust`, `ascent-override`, `descent-override` to reduce CLS on font swap
|
|
69
|
+
- [ ] System font stack considered before any custom font
|
|
70
|
+
|
|
71
|
+
### Network
|
|
72
|
+
- [ ] Static assets cached with long `max-age` + content hashing
|
|
73
|
+
- [ ] API responses cached where appropriate (`Cache-Control`)
|
|
74
|
+
- [ ] HTTP/2 or HTTP/3 enabled
|
|
75
|
+
- [ ] Resources preconnected (`<link rel="preconnect">`) for known origins
|
|
76
|
+
- [ ] `fetchpriority` used on critical non-image resources (e.g., key `<link rel="preload">`, above-the-fold `<script>`) — not only on `<img>`
|
|
77
|
+
- [ ] No unnecessary redirects
|
|
78
|
+
|
|
79
|
+
### Rendering
|
|
80
|
+
- [ ] No layout thrashing (forced synchronous layouts)
|
|
81
|
+
- [ ] Animations use `transform` and `opacity` (GPU-accelerated)
|
|
82
|
+
- [ ] Long lists use virtualization (e.g., `react-window`)
|
|
83
|
+
- [ ] No unnecessary full-page re-renders
|
|
84
|
+
- [ ] Off-screen sections use `content-visibility: auto` with `contain-intrinsic-size` to skip layout/paint of non-visible areas
|
|
85
|
+
- [ ] No `unload` event handlers and no `Cache-Control: no-store` on HTML responses — preserves back/forward cache (bfcache) eligibility
|
|
86
|
+
|
|
87
|
+
## Backend Checklist
|
|
88
|
+
|
|
89
|
+
### Database
|
|
90
|
+
- [ ] No N+1 query patterns (use eager loading / joins)
|
|
91
|
+
- [ ] Queries have appropriate indexes
|
|
92
|
+
- [ ] List endpoints paginated (never `SELECT * FROM table`)
|
|
93
|
+
- [ ] Connection pooling configured
|
|
94
|
+
- [ ] Slow query logging enabled
|
|
95
|
+
|
|
96
|
+
#### Query plans
|
|
97
|
+
- [ ] `EXPLAIN ANALYZE` captured **before** the fix, not just after — it is the baseline
|
|
98
|
+
- [ ] `Seq Scan` on a large table understood: index missing, unusable, or genuinely not worth it
|
|
99
|
+
- [ ] Estimated vs actual `rows=` within an order of magnitude (if not, refresh statistics before touching indexes)
|
|
100
|
+
- [ ] No `Sort` node that a composite index could absorb
|
|
101
|
+
- [ ] Plan re-checked after the change — an index that did not change the plan gets reverted
|
|
102
|
+
|
|
103
|
+
#### Index strategy
|
|
104
|
+
- [ ] Composite index column order is equality first, then range/sort
|
|
105
|
+
- [ ] Index covers the query shape (filter + sort), not just one column in isolation
|
|
106
|
+
- [ ] Covering index considered for hot read paths (index-only scan avoids the heap fetch)
|
|
107
|
+
- [ ] Not indexing low-selectivity columns *for the dominant value*; a partial index still serves the rare-value query (`WHERE status = 'failed'`)
|
|
108
|
+
- [ ] Expression index used where the query applies a function (`lower(email)`)
|
|
109
|
+
- [ ] Full-text or trigram index used for leading-wildcard search, not a B-tree
|
|
110
|
+
- [ ] Write cost measured on write-heavy tables (every index taxes every `INSERT`/`UPDATE`)
|
|
111
|
+
- [ ] Unused and duplicate indexes dropped (they cost writes and buy nothing)
|
|
112
|
+
|
|
113
|
+
#### Connection pooling
|
|
114
|
+
- [ ] One pool per process, not per request or per module
|
|
115
|
+
- [ ] `instances × pool max` stays under the database's `max_connections`
|
|
116
|
+
- [ ] `connectionTimeoutMillis` set so exhaustion fails fast instead of queueing forever
|
|
117
|
+
- [ ] Exhaustion diagnosed before resizing: find what holds connections (long transactions, missing `await`, leaked clients)
|
|
118
|
+
- [ ] Serverless / autoscaling fronted by a multiplexing proxy (pgbouncer, RDS Proxy) rather than a larger pool
|
|
119
|
+
|
|
120
|
+
### API
|
|
121
|
+
- [ ] Response times < 200ms (p95)
|
|
122
|
+
- [ ] No synchronous heavy computation in request handlers
|
|
123
|
+
- [ ] Bulk operations instead of loops of individual calls
|
|
124
|
+
- [ ] Response compression (gzip/brotli)
|
|
125
|
+
- [ ] Appropriate caching (in-memory, Redis, CDN)
|
|
126
|
+
|
|
127
|
+
### Infrastructure
|
|
128
|
+
- [ ] CDN for static assets
|
|
129
|
+
- [ ] Server located close to users (or edge deployment)
|
|
130
|
+
- [ ] Horizontal scaling configured (if needed)
|
|
131
|
+
- [ ] Health check endpoint for load balancer
|
|
132
|
+
|
|
133
|
+
## Caching Strategies
|
|
134
|
+
|
|
135
|
+
The decision material (which layer, which invalidation strategy, what never to cache) lives in the `atw-performance` skill's `./performance-optimization.md`. This section covers the read/write patterns and the checklist.
|
|
136
|
+
|
|
137
|
+
### Read and write patterns
|
|
138
|
+
|
|
139
|
+
| Pattern | How it works | Use when | Watch out for |
|
|
140
|
+
|---|---|---|---|
|
|
141
|
+
| **Cache-aside** (lazy) | App checks cache, on miss reads origin and populates | Default choice; read-heavy, tolerant of a cold first hit | Every miss hits the origin, so it needs stampede protection |
|
|
142
|
+
| **Read-through** | Cache layer itself loads on miss | You want the load path in one place, not at every call site | Hides origin latency; a slow origin looks like a slow cache |
|
|
143
|
+
| **Write-through** | Write goes to cache and origin together, synchronously | Reads must never see a stale value after a write | Adds cache latency to every write |
|
|
144
|
+
| **Write-behind** (write-back) | Write hits cache, origin updated asynchronously | Write-heavy, and the origin is the bottleneck | Data loss window if the cache dies before the flush. Needs durability you can defend |
|
|
145
|
+
|
|
146
|
+
### Negative caching
|
|
147
|
+
|
|
148
|
+
Cache the *absence* of a result too. A key that misses on every lookup (a nonexistent user ID probed in a loop, a 404 asset) sends every request to the origin, which is a cache that only protects the happy path.
|
|
149
|
+
|
|
150
|
+
- Store an explicit "not found" sentinel with a **shorter** TTL than positive entries
|
|
151
|
+
- Keep the negative TTL short enough that a newly created record appears promptly
|
|
152
|
+
- Never let an origin *error* become a negative cache entry, or one failing minute becomes many
|
|
153
|
+
|
|
154
|
+
### Request coalescing (stampede protection)
|
|
155
|
+
|
|
156
|
+
One recompute, N waiters. Prevents a hot key's expiry from delivering the full concurrent load to the origin:
|
|
157
|
+
|
|
158
|
+
```typescript
|
|
159
|
+
const inFlight = new Map<string, Promise<unknown>>();
|
|
160
|
+
|
|
161
|
+
function loadOnce<T>(key: string, fetcher: () => Promise<T>): Promise<T> {
|
|
162
|
+
const existing = inFlight.get(key) as Promise<T> | undefined;
|
|
163
|
+
if (existing) return existing;
|
|
164
|
+
const p = fetcher().finally(() => inFlight.delete(key));
|
|
165
|
+
inFlight.set(key, p);
|
|
166
|
+
return p;
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
For a shared cache, the same idea needs a distributed lock, or `stale-while-revalidate` so waiters serve the stale value instead of blocking.
|
|
171
|
+
|
|
172
|
+
### Cache checklist
|
|
173
|
+
- [ ] The cached call was measured as expensive first (caching a fast call adds a hop and buys nothing)
|
|
174
|
+
- [ ] Read/write ratio justifies the cache (re-read far more often than written)
|
|
175
|
+
- [ ] Cache key includes every input the response varies on: tenant, viewer, locale, permissions, feature flags
|
|
176
|
+
- [ ] No per-user data cached under a key that does not identify the user
|
|
177
|
+
- [ ] One invalidation strategy chosen (TTL, event/tag, or versioned keys), not an accidental mix
|
|
178
|
+
- [ ] Acceptable staleness window written down, not implied by whatever TTL was typed
|
|
179
|
+
- [ ] Stampede protection on hot keys (coalescing, lock, or `stale-while-revalidate`)
|
|
180
|
+
- [ ] Negative results cached with a shorter TTL; origin errors never cached
|
|
181
|
+
- [ ] Eviction policy and memory ceiling set (an unbounded cache is a memory leak)
|
|
182
|
+
- [ ] Hit rate monitored — a cache nobody measures is an assumption, and a low hit rate is pure overhead
|
|
183
|
+
- [ ] Nothing cached whose staleness is a correctness bug (balances, permissions, inventory at checkout)
|
|
184
|
+
|
|
185
|
+
## Measurement Commands
|
|
186
|
+
|
|
187
|
+
### INP field data and DevTools workflow
|
|
188
|
+
|
|
189
|
+
1. **Field data first** — check [CrUX Vis](https://developer.chrome.com/docs/crux/vis) or your RUM tool for real-user INP before optimising
|
|
190
|
+
2. **Identify slow interactions** — open DevTools → Performance panel → record while interacting; look for long tasks triggered by clicks/keystrokes
|
|
191
|
+
3. **Test on mid-range Android** — INP issues often only surface on slower hardware; use a real device or DevTools CPU throttling (4×–6× slowdown)
|
|
192
|
+
|
|
193
|
+
```bash
|
|
194
|
+
# Lighthouse CLI
|
|
195
|
+
npx lighthouse https://localhost:3000 --output json --output-path ./report.json
|
|
196
|
+
|
|
197
|
+
# Bundle analysis
|
|
198
|
+
npx webpack-bundle-analyzer stats.json
|
|
199
|
+
# or for Vite:
|
|
200
|
+
npx vite-bundle-visualizer
|
|
201
|
+
|
|
202
|
+
# Check bundle size
|
|
203
|
+
npx bundlesize
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
```javascript
|
|
207
|
+
// Web Vitals in code
|
|
208
|
+
import { onLCP, onINP, onCLS } from 'web-vitals';
|
|
209
|
+
onLCP(console.log);
|
|
210
|
+
onINP(console.log);
|
|
211
|
+
onCLS(console.log);
|
|
212
|
+
|
|
213
|
+
// INP with interaction-level detail (attribution build)
|
|
214
|
+
import { onINP } from 'web-vitals/attribution';
|
|
215
|
+
onINP(({ value, attribution }) => {
|
|
216
|
+
const { interactionTarget, inputDelay, processingDuration, presentationDelay } = attribution;
|
|
217
|
+
console.log({ value, interactionTarget, inputDelay, processingDuration, presentationDelay });
|
|
218
|
+
});
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
## Common Anti-Patterns
|
|
222
|
+
|
|
223
|
+
| Anti-Pattern | Impact | Fix |
|
|
224
|
+
|---|---|---|
|
|
225
|
+
| N+1 queries | Linear DB load growth | Use joins, includes, or batch loading |
|
|
226
|
+
| Unbounded queries | Memory exhaustion, timeouts | Always paginate, add LIMIT |
|
|
227
|
+
| Missing indexes | Slow reads as data grows | Add indexes for filtered/sorted columns |
|
|
228
|
+
| Indexing without reading the plan | Write cost paid, read gain unproven | `EXPLAIN ANALYZE` before and after; revert if the plan is unchanged |
|
|
229
|
+
| Redundant / unused indexes | Every write pays for them | Audit usage stats, drop what nothing reads |
|
|
230
|
+
| Connection pool per request | Exhausts `max_connections` under load | One pool per process; proxy for serverless |
|
|
231
|
+
| Cache key missing the viewer | One user's data served to another | Key on tenant, viewer, locale, permissions |
|
|
232
|
+
| Unbounded cache | Memory leak wearing an optimization's clothing | Set eviction policy and a memory ceiling |
|
|
233
|
+
| Cache stampede on a hot key | Origin takes full concurrent load at expiry | Coalesce misses, or `stale-while-revalidate` |
|
|
234
|
+
| Layout thrashing | Jank, dropped frames | Batch DOM reads, then batch writes |
|
|
235
|
+
| Unoptimized images | Slow LCP, wasted bandwidth | Use WebP, responsive sizes, lazy load |
|
|
236
|
+
| Large bundles | Slow Time to Interactive | Code split, tree shake, audit deps |
|
|
237
|
+
| Blocking main thread | Poor INP, unresponsive UI | Chunk long tasks with `scheduler.yield()` / `yieldToMain`, offload to Web Workers |
|
|
238
|
+
| Memory leaks | Growing memory, eventual crash | Clean up listeners, intervals, refs |
|
package/dist/templates/common/bundled-skills/atw-performance/references/performance-optimization.md
ADDED
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
# Performance Optimization
|
|
2
|
+
|
|
3
|
+
Attached file of the `atw-performance` skill: the measure-first workflow, the where-to-start tree, the bottleneck tables, the anti-pattern catalogue, the keep-or-revert table and the budgets. Open the part you need; the skill's own rules (the seven questions, keep or revert, the decision log) come first.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
Measure before optimizing. Performance work without measurement is guessing — and guessing leads to premature optimization that adds complexity without improving what matters. Profile first, identify the actual bottleneck, fix it, measure again. Optimize only what measurements prove matters.
|
|
8
|
+
|
|
9
|
+
## When to Use
|
|
10
|
+
|
|
11
|
+
- Performance requirements exist in the spec (load time budgets, response time SLAs)
|
|
12
|
+
- Users or monitoring report slow behavior
|
|
13
|
+
- Core Web Vitals scores are below thresholds
|
|
14
|
+
- You suspect a change introduced a regression (it was fine and got slower after a change: that goes to the `atw-diagnosing-bugs` skill)
|
|
15
|
+
- Building features that handle large datasets or high traffic
|
|
16
|
+
|
|
17
|
+
**When NOT to use:** Don't optimize before you have evidence of a problem. Premature optimization adds complexity that costs more than the performance it gains.
|
|
18
|
+
|
|
19
|
+
## Core Web Vitals Targets
|
|
20
|
+
|
|
21
|
+
| Metric | Good | Needs Improvement | Poor |
|
|
22
|
+
|--------|------|-------------------|------|
|
|
23
|
+
| **LCP** (Largest Contentful Paint) | ≤ 2.5s | ≤ 4.0s | > 4.0s |
|
|
24
|
+
| **INP** (Interaction to Next Paint) | ≤ 200ms | ≤ 500ms | > 500ms |
|
|
25
|
+
| **CLS** (Cumulative Layout Shift) | ≤ 0.1 | ≤ 0.25 | > 0.25 |
|
|
26
|
+
|
|
27
|
+
## The Optimization Workflow
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
1. MEASURE → Establish baseline with real data
|
|
31
|
+
2. IDENTIFY → Find the actual bottleneck (not assumed)
|
|
32
|
+
3. FIX → Address the specific bottleneck
|
|
33
|
+
4. VERIFY → Measure again; keep or revert
|
|
34
|
+
5. GUARD → Add monitoring or tests to prevent regression
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### Step 1: Measure
|
|
38
|
+
|
|
39
|
+
Two complementary approaches — use both:
|
|
40
|
+
|
|
41
|
+
- **Synthetic (Lighthouse, DevTools Performance tab):** Controlled conditions, reproducible. Best for CI regression detection and isolating specific issues.
|
|
42
|
+
- **RUM (web-vitals library, CrUX):** Real user data in real conditions. Required to validate that a fix actually improved user experience.
|
|
43
|
+
|
|
44
|
+
**Frontend:**
|
|
45
|
+
```javascript
|
|
46
|
+
// Synthetic: Lighthouse in Chrome DevTools (or CI)
|
|
47
|
+
// Chrome DevTools → Performance tab → Record
|
|
48
|
+
// Chrome DevTools MCP → Performance trace
|
|
49
|
+
|
|
50
|
+
// RUM: Web Vitals library in code
|
|
51
|
+
import { onLCP, onINP, onCLS } from 'web-vitals';
|
|
52
|
+
|
|
53
|
+
onLCP(console.log);
|
|
54
|
+
onINP(console.log);
|
|
55
|
+
onCLS(console.log);
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
**Backend:**
|
|
59
|
+
```javascript
|
|
60
|
+
// Response time logging
|
|
61
|
+
// Application Performance Monitoring (APM)
|
|
62
|
+
// Database query logging with timing
|
|
63
|
+
|
|
64
|
+
// Simple timing
|
|
65
|
+
console.time('db-query');
|
|
66
|
+
const result = await db.query(...);
|
|
67
|
+
console.timeEnd('db-query');
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Where to Start Measuring
|
|
71
|
+
|
|
72
|
+
Use the symptom to decide what to measure first:
|
|
73
|
+
|
|
74
|
+
```
|
|
75
|
+
What is slow?
|
|
76
|
+
├── First page load
|
|
77
|
+
│ ├── Large bundle? --> Measure bundle size, check code splitting
|
|
78
|
+
│ ├── Slow server response? --> Measure TTFB in DevTools Network waterfall
|
|
79
|
+
│ │ ├── DNS long? --> Add dns-prefetch / preconnect for known origins
|
|
80
|
+
│ │ ├── TCP/TLS long? --> Enable HTTP/2, check edge deployment, keep-alive
|
|
81
|
+
│ │ └── Waiting (server) long? --> Profile backend, check queries and caching
|
|
82
|
+
│ └── Render-blocking resources? --> Check network waterfall for CSS/JS blocking
|
|
83
|
+
├── Interaction feels sluggish
|
|
84
|
+
│ ├── UI freezes on click? --> Profile main thread, look for long tasks (>50ms)
|
|
85
|
+
│ ├── Form input lag? --> Check re-renders, controlled component overhead
|
|
86
|
+
│ └── Animation jank? --> Check layout thrashing, forced reflows
|
|
87
|
+
├── Page after navigation
|
|
88
|
+
│ ├── Data loading? --> Measure API response times, check for waterfalls
|
|
89
|
+
│ └── Client rendering? --> Profile component render time, check for N+1 fetches
|
|
90
|
+
└── Backend / API
|
|
91
|
+
├── Single endpoint slow? --> Profile database queries, check indexes
|
|
92
|
+
├── All endpoints slow? --> Check connection pool, memory, CPU
|
|
93
|
+
└── Intermittent slowness? --> Check for lock contention, GC pauses, external deps
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### Step 2: Identify the Bottleneck
|
|
97
|
+
|
|
98
|
+
Common bottlenecks by category:
|
|
99
|
+
|
|
100
|
+
**Frontend:**
|
|
101
|
+
|
|
102
|
+
| Symptom | Likely Cause | Investigation |
|
|
103
|
+
|---------|-------------|---------------|
|
|
104
|
+
| Slow LCP | Large images, render-blocking resources, slow server | Check network waterfall, image sizes |
|
|
105
|
+
| High CLS | Images without dimensions, late-loading content, font shifts | Check layout shift attribution |
|
|
106
|
+
| Poor INP | Heavy JavaScript on main thread, large DOM updates | Check long tasks in Performance trace |
|
|
107
|
+
| Slow initial load | Large bundle, many network requests | Check bundle size, code splitting |
|
|
108
|
+
|
|
109
|
+
**Backend:**
|
|
110
|
+
|
|
111
|
+
| Symptom | Likely Cause | Investigation |
|
|
112
|
+
|---------|-------------|---------------|
|
|
113
|
+
| Slow API responses | N+1 queries, missing indexes, unoptimized queries | Check database query log |
|
|
114
|
+
| Memory growth | Leaked references, unbounded caches, large payloads | Heap snapshot analysis |
|
|
115
|
+
| CPU spikes | Synchronous heavy computation, regex backtracking | CPU profiling |
|
|
116
|
+
| High latency | Missing caching, redundant computation, network hops | Trace requests through the stack |
|
|
117
|
+
|
|
118
|
+
### Step 3: Fix the Bottleneck
|
|
119
|
+
|
|
120
|
+
Fix the one thing Step 2 identified, nothing else. The anti-patterns below are the usual suspects; each entry gives the rule and the signature to recognize it, and links to a worked fix in [optimization-patterns.md](optimization-patterns.md). Open the one you need when you reach that code, not before.
|
|
121
|
+
|
|
122
|
+
**Backend**
|
|
123
|
+
|
|
124
|
+
- **N+1 queries.** One query per row is the most common backend bottleneck. Fetch the relation in the same query (join/include) instead of in the loop. [Pattern](optimization-patterns.md#n1-queries-backend).
|
|
125
|
+
- **Unbounded data fetching.** Every list endpoint paginates with a limit and a stable order. [Pattern](optimization-patterns.md#unbounded-data-fetching).
|
|
126
|
+
- **Queries that ignore their index.** "Add an index" is the guess; `EXPLAIN ANALYZE` is the measurement. A `Seq Scan` where you expected an index, a `rows=` estimate off by an order of magnitude, and a `Sort` node above the scan each call for a different fix; a bad `rows=` estimate means stale statistics, so run `ANALYZE` rather than adding an index. Index for the shape of the query (equality columns first, then the range or sort column). A plain index will not help a query on a low-selectivity dominant value (a partial index serves the rare value), a leading wildcard (needs trigram or full-text), or a function applied to the column (index the expression, as in `WHERE lower(email) = ?`), and every index taxes every write. Re-run the plan afterwards; an index that did not change it is a revert. [Pattern](optimization-patterns.md#queries-that-ignore-their-index).
|
|
127
|
+
- **Connection pool exhaustion.** Signature: *every* endpoint slows at once, time is spent waiting for a connection rather than executing, and the database shows mostly idle sessions. One pool per process, sized so `instances × max` stays under the database's connection ceiling. Bigger is not faster; it relocates the queue to the database where it is harder to see. With unbounded instance counts (serverless, autoscaling), multiplex through a proxy (pgbouncer, RDS Proxy) instead of raising `max`. [Pattern](optimization-patterns.md#connection-pool-exhaustion).
|
|
128
|
+
- **Missing caching.** Cache what is expensive to produce and read far more often than it changes; caching an already-fast query adds a network hop and a staleness bug in exchange for nothing. Pick the layer deliberately (in-process, shared, CDN). Every input that changes the response belongs in the key (tenant, locale, permissions, feature flags): a key that omits the viewer is how one user's data gets served to another. Choose one invalidation strategy (TTL, event or tag based, versioned keys) and state the acceptable staleness window explicitly. Guard hot keys against the stampede: serve stale while one request recomputes, or coalesce concurrent misses behind a single in-flight promise. Never cache what must be fresh (balances, permissions, inventory at checkout). [Pattern](optimization-patterns.md#missing-caching-backend); request coalescing, write strategies, and negative caching in `./performance-checklist.md`.
|
|
129
|
+
|
|
130
|
+
**Frontend**
|
|
131
|
+
|
|
132
|
+
- **Missing image optimization.** Every image declares `width` and `height` (CLS). The LCP image gets `fetchpriority="high"`, modern formats (AVIF, WebP) through `<picture>`, and `srcset`/`sizes` for resolution switching; below-the-fold images get `loading="lazy"` and `decoding="async"`. [Pattern](optimization-patterns.md#missing-image-optimization-frontend).
|
|
133
|
+
- **Unnecessary re-renders.** An object or function created in render is a new reference every time and re-renders every child that receives it. Hoist constants; reserve `React.memo` and `useMemo` for work the profile shows is expensive, since overuse is its own cost. [Pattern](optimization-patterns.md#unnecessary-re-renders-react).
|
|
134
|
+
- **Large bundle size.** Modern bundlers tree-shake ESM named imports on their own; the real gains are route-level code splitting and lazy-loading heavy, rarely-used features behind `Suspense`. Profile before changing import styles. [Pattern](optimization-patterns.md#large-bundle-size).
|
|
135
|
+
|
|
136
|
+
### Step 4: Verify (Keep or Revert)
|
|
137
|
+
|
|
138
|
+
A fix is a hypothesis until you re-measure. This step decides whether it survives.
|
|
139
|
+
|
|
140
|
+
**Re-measure the way you measured the baseline:** same command, same conditions, same fixed budget (wall-clock, sample count, or request count). A baseline taken on a cold cache against a result taken on a warm one measures the cache, not your change.
|
|
141
|
+
|
|
142
|
+
**Change one thing at a time.** Three optimizations landed together produce one number, and you cannot attribute it. If they must ship together, measure each in isolation first.
|
|
143
|
+
|
|
144
|
+
**Beat the noise, not just the mean.** Repeat the measurement and compare the delta against run-to-run variance. A 3% gain inside ±5% variance is not a gain; it is a different sample.
|
|
145
|
+
|
|
146
|
+
Then decide, strictly:
|
|
147
|
+
|
|
148
|
+
| Result vs. baseline | Action |
|
|
149
|
+
|---|---|
|
|
150
|
+
| Past the threshold, tests green | **Keep.** Commit with the before/after numbers in the message. |
|
|
151
|
+
| Within noise (no measurable change) | **Revert.** |
|
|
152
|
+
| Worse | **Revert.** |
|
|
153
|
+
| Improved, but a test went red | **Revert.** A regression wearing a win's clothing. |
|
|
154
|
+
|
|
155
|
+
**"Neutral" is a revert, not a keep.** This is the step teams skip: the change is already written, throwing it away feels wasteful, so it lands unmeasured, and the codebase accretes complexity that never bought anything. Code you keep, you maintain forever. Make it pay for itself.
|
|
156
|
+
|
|
157
|
+
**Correctness gates the metric.** The suite stays green *and* the number moves. An "optimization" that wins by dropping work the product needed (skipping a validation, caching something that must be fresh, removing an `await` that was load-bearing) is a regression, not a win.
|
|
158
|
+
|
|
159
|
+
#### Log every attempt, including the reverted ones
|
|
160
|
+
|
|
161
|
+
Reverted work leaves no trace in git history, which is exactly why the same dead idea gets tried again next quarter. Keep a short ledger so a discarded idea stays discarded:
|
|
162
|
+
|
|
163
|
+
| Idea | Baseline → Result | Verdict | Why |
|
|
164
|
+
|---|---|---|---|
|
|
165
|
+
| Memoize the row component | INP 240ms → 235ms | reverted | Inside noise (±15ms). Rows weren't the bottleneck. |
|
|
166
|
+
| Virtualize the list | INP 240ms → 90ms | kept | Long tasks gone from the trace. |
|
|
167
|
+
| Preconnect to the API origin | LCP 2.8s → 2.8s | reverted | Already same-origin. |
|
|
168
|
+
|
|
169
|
+
In ATW this ledger is the task's decision log (`decision-log.md`), one row per attempt, written as the `atw-performance` skill says. What matters is that the next person (or the next agent) reads it before proposing an experiment, and doesn't re-run one that already failed.
|
|
170
|
+
|
|
171
|
+
### Step 5: Guard Against Regression
|
|
172
|
+
|
|
173
|
+
Guard the metric the user actually feels, not every available number. Use the
|
|
174
|
+
same LCP, INP, p95 latency, or other primary metric that justified the fix.
|
|
175
|
+
|
|
176
|
+
Use two complementary layers when the surface is user-facing:
|
|
177
|
+
|
|
178
|
+
- **Synthetic CI gate:** Catch reproducible regressions before merge with a
|
|
179
|
+
performance budget. Repeat noisy measurements or compare a median/trend so
|
|
180
|
+
normal run-to-run variance does not turn the gate into a flaky check.
|
|
181
|
+
- **Field monitoring:** Alert on a meaningful p75 movement in RUM data. Use
|
|
182
|
+
attributed `web-vitals` data to locate the cause; treat CrUX's rolling window
|
|
183
|
+
as confirmation rather than an immediate alert.
|
|
184
|
+
|
|
185
|
+
When either guard fires, return to Step 1 and establish a fresh baseline before
|
|
186
|
+
proposing another fix.
|
|
187
|
+
|
|
188
|
+
**Set budgets and enforce them:**
|
|
189
|
+
|
|
190
|
+
```
|
|
191
|
+
JavaScript bundle: < 200KB gzipped (initial load)
|
|
192
|
+
CSS: < 50KB gzipped
|
|
193
|
+
Images: < 200KB per image (above the fold)
|
|
194
|
+
Fonts: < 100KB total
|
|
195
|
+
API response time: < 200ms (p95)
|
|
196
|
+
Time to Interactive: < 3.5s on 4G
|
|
197
|
+
Lighthouse Performance score: ≥ 90
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
**Enforce in CI:**
|
|
201
|
+
```bash
|
|
202
|
+
# Bundle size check
|
|
203
|
+
npx bundlesize --config bundlesize.config.json
|
|
204
|
+
|
|
205
|
+
# Lighthouse CI
|
|
206
|
+
npx lhci autorun
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
## See Also
|
|
210
|
+
|
|
211
|
+
For detailed performance checklists, optimization commands, and anti-pattern reference, see `./performance-checklist.md`.
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
## Common Rationalizations
|
|
215
|
+
|
|
216
|
+
| Rationalization | Reality |
|
|
217
|
+
|---|---|
|
|
218
|
+
| "We'll optimize later" | Performance debt compounds. Fix obvious anti-patterns now, defer micro-optimizations. |
|
|
219
|
+
| "It's fast on my machine" | Your machine isn't the user's. Profile on representative hardware and networks. |
|
|
220
|
+
| "This optimization is obvious" | If you didn't measure, you don't know. Profile first. |
|
|
221
|
+
| "Users won't notice 100ms" | Research shows 100ms delays impact conversion rates. Users notice more than you think. |
|
|
222
|
+
| "The framework handles performance" | Frameworks prevent some issues but can't fix N+1 queries or oversized bundles. |
|
|
223
|
+
| "The query is slow, add an index" | Read the plan first. The index may already exist and be unusable, and every index taxes writes forever. |
|
|
224
|
+
| "Just cache it" | Caching an already-cheap call buys nothing and adds a staleness bug. Cache what is expensive *and* re-read far more than written. |
|
|
225
|
+
| "Raise the pool size, we're running out of connections" | A pool bigger than the database can serve moves the queue somewhere less visible. Find what holds connections. |
|
|
226
|
+
| "It didn't help much, but it doesn't hurt" | Neutral changes are a revert. You pay maintenance on them forever and got nothing back. |
|
|
227
|
+
| "We already wrote it, may as well keep it" | Sunk cost. The measurement doesn't care how long the change took to write. |
|
|
228
|
+
| "The improvement is obvious, no need to re-measure" | Then re-measuring is cheap and proves it. Unmeasured wins are how neutral complexity lands. |
|
|
229
|
+
|
|
230
|
+
## Red Flags
|
|
231
|
+
|
|
232
|
+
- Optimization without profiling data to justify it
|
|
233
|
+
- N+1 query patterns in data fetching
|
|
234
|
+
- An index added without a query plan before and after to justify it
|
|
235
|
+
- A cache key that omits an input the response depends on (tenant, locale, viewer)
|
|
236
|
+
- A cache with no stated staleness window and no invalidation strategy
|
|
237
|
+
- Connection pool size raised in response to exhaustion, without finding what holds connections
|
|
238
|
+
- List endpoints without pagination
|
|
239
|
+
- Images without dimensions, lazy loading, or responsive sizes
|
|
240
|
+
- Bundle size growing without review
|
|
241
|
+
- No performance monitoring in production
|
|
242
|
+
- `React.memo` and `useMemo` everywhere (overusing is as bad as underusing)
|
|
243
|
+
- Optimizations kept without a re-measurement that justifies them
|
|
244
|
+
- Several optimizations bundled into one measurement, so no single change can be attributed
|
|
245
|
+
- A "win" that required a test to be changed, skipped, or deleted
|
|
246
|
+
- The same failed optimization attempted more than once because nobody recorded the first attempt
|
|
247
|
+
|
|
248
|
+
## Verification
|
|
249
|
+
|
|
250
|
+
After any performance-related change:
|
|
251
|
+
|
|
252
|
+
- [ ] Before and after measurements exist (specific numbers)
|
|
253
|
+
- [ ] The result was re-measured the same way as the baseline (same command, same conditions)
|
|
254
|
+
- [ ] The improvement exceeds run-to-run variance, not just the mean
|
|
255
|
+
- [ ] Changes that didn't beat the baseline were reverted, not kept as neutral
|
|
256
|
+
- [ ] Attempts are logged, kept and reverted alike, so a dead idea isn't re-run
|
|
257
|
+
- [ ] The specific bottleneck is identified and addressed
|
|
258
|
+
- [ ] Core Web Vitals are within "Good" thresholds
|
|
259
|
+
- [ ] Bundle size hasn't increased significantly
|
|
260
|
+
- [ ] No N+1 queries in new data fetching code
|
|
261
|
+
- [ ] Any new index is justified by a query plan before and after, and its write cost was considered
|
|
262
|
+
- [ ] Any new cache states what it keys on and how it goes stale
|
|
263
|
+
- [ ] The measured user-facing metric has a synthetic budget or field monitor that can detect regression
|
|
264
|
+
- [ ] Existing tests still pass (optimization didn't break behavior)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: atw-prototype
|
|
3
|
-
description: "构建一次性原型来解答设计问题。用于验证状态模型或业务逻辑是否合理、探索界面应该长什么样(先用文字比较几个风格方向,要看画面时再做成首屏并排对比,由用户选定),或把选定方向做成能完整点一遍、带业务逻辑的可交互原型。原型只用于做决定,不是正式实现;按选定方向做正式界面、精修和评审交给 atw-ui
|
|
3
|
+
description: "构建一次性原型来解答设计问题。用于验证状态模型或业务逻辑是否合理、探索界面应该长什么样(先用文字比较几个风格方向,要看画面时再做成首屏并排对比,由用户选定),或把选定方向做成能完整点一遍、带业务逻辑的可交互原型。原型只用于做决定,不是正式实现;按选定方向做正式界面、精修和评审交给 atw-ui。设计方向还没定、想先验证状态模型或比一比几个风格方向时使用。"
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Prototype
|
|
@@ -11,8 +11,8 @@ description: "构建一次性原型来解答设计问题。用于验证状态模
|
|
|
11
11
|
|
|
12
12
|
先从用户的请求、相关代码判断要回答的是哪个问题;用户在场时可以直接问:
|
|
13
13
|
|
|
14
|
-
- **“这套逻辑 / 状态模型对不对?”** → [
|
|
15
|
-
- **“这个界面应该长什么样?”** 或 **“这个页面用起来对不对?”** → [
|
|
14
|
+
- **“这套逻辑 / 状态模型对不对?”** → [logic.md](logic.md)。做一个可分享的单文件 HTML:自由操作的按钮加按标签页划分的引导演练,把纸面上难以推演的状态组合跑一遍,让不写代码的人也能自己操作。
|
|
15
|
+
- **“这个界面应该长什么样?”** 或 **“这个页面用起来对不对?”** → [ui.md](ui.md)。分两段:先认品类、拆标杆,用文字写出三个风格明显不同的方向请用户选,用户要看画面时再把三个首屏放进对比页;需要时再把选定方向深化成带业务逻辑的可交互原型,业务逻辑按 logic.md 的方式写成独立的纯模块。
|
|
16
16
|
|
|
17
17
|
两个分支的产物差别很大,选错等于白做。问题确实模糊且用户不在时,按周边代码选:后端模块走逻辑,页面或组件走界面;并在原型顶部写明这个假设。
|
|
18
18
|
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
- “写接口之前,我想先感受一下它该长什么样。”
|
|
12
12
|
- 任何想要**按按钮、看状态变化**的场景。
|
|
13
13
|
|
|
14
|
-
如果问题是“这个界面应该长什么样”,分支选错了,改用 [
|
|
14
|
+
如果问题是“这个界面应该长什么样”,分支选错了,改用 [ui.md](ui.md)。如果问题是“这个页面连同业务规则用起来对不对”,走 ui.md 的第二段:逻辑仍按本文件第 2 步写成纯模块,外面换成选定方向的设计界面。
|
|
15
15
|
|
|
16
16
|
## 流程
|
|
17
17
|
|
|
@@ -75,7 +75,7 @@ macOS/Linux 通常使用 `python3`,Windows 通常使用 `py -3`;执行前确
|
|
|
75
75
|
|
|
76
76
|
生成器根据本轮项目和候选内容生成 `fingerprint`。候选内容变化后会形成新一轮存储身份,避免把旧选择误用于新作品。点“选择”时同时复制一句简短的文字:轮次、编号和方向名,有备注时再附一行,例如“<轮次>:选 03 <方向名>”,并在底部提示已复制的内容。改完备注后再点一次“已选择”重新复制;选另一个方向会直接替换当前选择。
|
|
77
77
|
|
|
78
|
-
用户粘贴复制的结果或直接说出选择后,Agent 按轮次和编号对照当前 manifest,确认对应本轮,再把选择与备注更新到设计说明,回到 [界面原型](../
|
|
78
|
+
用户粘贴复制的结果或直接说出选择后,Agent 按轮次和编号对照当前 manifest,确认对应本轮,再把选择与备注更新到设计说明,回到 [界面原型](../ui.md) 第一段接着做。浏览器本地保存不代表已经写回项目文件,也不会自动向 Agent 发送消息。
|
|
79
79
|
|
|
80
80
|
## 检查实际结果
|
|
81
81
|
|
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
界面原型分两段:
|
|
4
4
|
|
|
5
5
|
- **第一段:选方向。** 先认品类、拆标杆,用文字写出三个风格和结构都明显不同的方向请用户选;用户想看实际画面时,再把三个方向各做成一张首屏放进同一个对比页挑,并说出具体喜欢和不喜欢哪里。
|
|
6
|
-
- **第二段:深化成可交互原型。** 只在问题包含“用起来对不对”时才做:把选定方向做成完整页面,接上按 [
|
|
6
|
+
- **第二段:深化成可交互原型。** 只在问题包含“用起来对不对”时才做:把选定方向做成完整页面,接上按 [logic.md](logic.md) 写的纯逻辑模块,让人能把真实业务流程完整点一遍。
|
|
7
7
|
|
|
8
8
|
两段的产物都是用来做决定的,不是正式代码。选定之后的正式界面由 atw-ui 按设计说明重新实现。
|
|
9
9
|
|
|
10
|
-
如果问题只关于逻辑或状态、与外观无关,分支选错了,改用 [
|
|
10
|
+
如果问题只关于逻辑或状态、与外观无关,分支选错了,改用 [logic.md](logic.md)。
|
|
11
11
|
|
|
12
12
|
## 什么时候用
|
|
13
13
|
|
|
@@ -106,7 +106,7 @@
|
|
|
106
106
|
|
|
107
107
|
### 1. 先写逻辑模块
|
|
108
108
|
|
|
109
|
-
按 [
|
|
109
|
+
按 [logic.md](logic.md) 第 2 步,把业务规则写成一个纯模块(reducer、状态机或纯函数),不碰 DOM。这部分以后能整体搬进正式代码,是第二段最值得保留的产物。
|
|
110
110
|
|
|
111
111
|
### 2. 选定方向做成完整页面
|
|
112
112
|
|
|
@@ -118,7 +118,7 @@
|
|
|
118
118
|
|
|
119
119
|
### 3. 加一个状态抽屉
|
|
120
120
|
|
|
121
|
-
|
|
121
|
+
logic.md 里的“当前状态面板”和“引导演练”放进一个可开关的抽屉,供不写代码的人逐步走查业务规则:
|
|
122
122
|
|
|
123
123
|
- 抽屉视觉上明显不属于设计本身(例如高对比的面板),和浮动切换条一样,不参与设计评判。
|
|
124
124
|
- 抽屉里保持朴素:标签式的状态字段、标出刚变化的部分、按场景划分的演练按钮;不加动画。
|