@chinhae/atw-cli 0.5.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.
Files changed (257) hide show
  1. package/README.md +128 -451
  2. package/dist/cli/index.d.ts.map +1 -1
  3. package/dist/cli/index.js +24 -1
  4. package/dist/cli/index.js.map +1 -1
  5. package/dist/commands/channel/adapters/claude.d.ts +44 -2
  6. package/dist/commands/channel/adapters/claude.d.ts.map +1 -1
  7. package/dist/commands/channel/adapters/claude.js +124 -23
  8. package/dist/commands/channel/adapters/claude.js.map +1 -1
  9. package/dist/commands/channel/adapters/codex.d.ts +36 -6
  10. package/dist/commands/channel/adapters/codex.d.ts.map +1 -1
  11. package/dist/commands/channel/adapters/codex.js +150 -10
  12. package/dist/commands/channel/adapters/codex.js.map +1 -1
  13. package/dist/commands/channel/adapters/index.d.ts +33 -4
  14. package/dist/commands/channel/adapters/index.d.ts.map +1 -1
  15. package/dist/commands/channel/adapters/index.js +159 -6
  16. package/dist/commands/channel/adapters/index.js.map +1 -1
  17. package/dist/commands/channel/adapters/omp.d.ts +106 -0
  18. package/dist/commands/channel/adapters/omp.d.ts.map +1 -0
  19. package/dist/commands/channel/adapters/omp.js +405 -0
  20. package/dist/commands/channel/adapters/omp.js.map +1 -0
  21. package/dist/commands/channel/adapters/pi.d.ts +123 -0
  22. package/dist/commands/channel/adapters/pi.d.ts.map +1 -0
  23. package/dist/commands/channel/adapters/pi.js +387 -0
  24. package/dist/commands/channel/adapters/pi.js.map +1 -0
  25. package/dist/commands/channel/adapters/types.d.ts +16 -0
  26. package/dist/commands/channel/adapters/types.d.ts.map +1 -1
  27. package/dist/commands/channel/agent-loader.d.ts +1 -0
  28. package/dist/commands/channel/agent-loader.d.ts.map +1 -1
  29. package/dist/commands/channel/agent-loader.js +45 -5
  30. package/dist/commands/channel/agent-loader.js.map +1 -1
  31. package/dist/commands/channel/dev-parse-trace.d.ts +1 -1
  32. package/dist/commands/channel/dev-parse-trace.d.ts.map +1 -1
  33. package/dist/commands/channel/dev-parse-trace.js +25 -2
  34. package/dist/commands/channel/dev-parse-trace.js.map +1 -1
  35. package/dist/commands/channel/index.d.ts.map +1 -1
  36. package/dist/commands/channel/index.js +44 -12
  37. package/dist/commands/channel/index.js.map +1 -1
  38. package/dist/commands/channel/run.d.ts +39 -2
  39. package/dist/commands/channel/run.d.ts.map +1 -1
  40. package/dist/commands/channel/run.js +227 -68
  41. package/dist/commands/channel/run.js.map +1 -1
  42. package/dist/commands/channel/spawn.d.ts +7 -2
  43. package/dist/commands/channel/spawn.d.ts.map +1 -1
  44. package/dist/commands/channel/spawn.js +35 -1
  45. package/dist/commands/channel/spawn.js.map +1 -1
  46. package/dist/commands/channel/store/events.d.ts +2 -2
  47. package/dist/commands/channel/store/events.d.ts.map +1 -1
  48. package/dist/commands/channel/store/events.js +1 -1
  49. package/dist/commands/channel/store/events.js.map +1 -1
  50. package/dist/commands/channel/supervisor/shutdown.d.ts +2 -0
  51. package/dist/commands/channel/supervisor/shutdown.d.ts.map +1 -1
  52. package/dist/commands/channel/supervisor/shutdown.js +6 -1
  53. package/dist/commands/channel/supervisor/shutdown.js.map +1 -1
  54. package/dist/commands/channel/supervisor.d.ts +2 -2
  55. package/dist/commands/channel/supervisor.d.ts.map +1 -1
  56. package/dist/commands/channel/supervisor.js +26 -3
  57. package/dist/commands/channel/supervisor.js.map +1 -1
  58. package/dist/commands/init.d.ts.map +1 -1
  59. package/dist/commands/init.js +6 -1
  60. package/dist/commands/init.js.map +1 -1
  61. package/dist/commands/mem.d.ts +2 -1
  62. package/dist/commands/mem.d.ts.map +1 -1
  63. package/dist/commands/mem.js +162 -13
  64. package/dist/commands/mem.js.map +1 -1
  65. package/dist/commands/mode.d.ts.map +1 -1
  66. package/dist/commands/mode.js +5 -3
  67. package/dist/commands/mode.js.map +1 -1
  68. package/dist/commands/uninstall.d.ts +5 -3
  69. package/dist/commands/uninstall.d.ts.map +1 -1
  70. package/dist/commands/uninstall.js +40 -16
  71. package/dist/commands/uninstall.js.map +1 -1
  72. package/dist/commands/update.d.ts +34 -0
  73. package/dist/commands/update.d.ts.map +1 -1
  74. package/dist/commands/update.js +209 -11
  75. package/dist/commands/update.js.map +1 -1
  76. package/dist/commands/verify.d.ts +18 -0
  77. package/dist/commands/verify.d.ts.map +1 -0
  78. package/dist/commands/verify.js +75 -0
  79. package/dist/commands/verify.js.map +1 -0
  80. package/dist/configurators/index.d.ts.map +1 -1
  81. package/dist/configurators/index.js +3 -2
  82. package/dist/configurators/index.js.map +1 -1
  83. package/dist/configurators/omp.d.ts +22 -0
  84. package/dist/configurators/omp.d.ts.map +1 -1
  85. package/dist/configurators/omp.js +101 -2
  86. package/dist/configurators/omp.js.map +1 -1
  87. package/dist/configurators/shared.d.ts.map +1 -1
  88. package/dist/configurators/shared.js +39 -7
  89. package/dist/configurators/shared.js.map +1 -1
  90. package/dist/configurators/workflow.d.ts.map +1 -1
  91. package/dist/configurators/workflow.js +5 -1
  92. package/dist/configurators/workflow.js.map +1 -1
  93. package/dist/constants/paths.d.ts +4 -0
  94. package/dist/constants/paths.d.ts.map +1 -1
  95. package/dist/constants/paths.js +4 -0
  96. package/dist/constants/paths.js.map +1 -1
  97. package/dist/migrations/manifests/0.6.0.json +442 -0
  98. package/dist/migrations/manifests/0.7.0.json +1378 -0
  99. package/dist/templates/atw/definition-of-done.md +65 -0
  100. package/dist/templates/atw/gitignore.txt +7 -0
  101. package/dist/templates/atw/index.d.ts +11 -0
  102. package/dist/templates/atw/index.d.ts.map +1 -1
  103. package/dist/templates/atw/index.js +22 -0
  104. package/dist/templates/atw/index.js.map +1 -1
  105. package/dist/templates/atw/scripts/common/active_task.py +2 -2
  106. package/dist/templates/atw/scripts/common/workflow_phase.py +33 -0
  107. package/dist/templates/atw/scripts/cross_model_review.py +3312 -0
  108. package/dist/templates/atw/scripts/decision_log.py +2002 -0
  109. package/dist/templates/atw/scripts/floor_guard.py +642 -0
  110. package/dist/templates/atw/scripts/task.py +25 -6
  111. package/dist/templates/atw/scripts/tickets.py +480 -10
  112. package/dist/templates/atw/workflow-general.md +6 -1
  113. package/dist/templates/atw/workflow.md +346 -26
  114. package/dist/templates/claude/settings.json +12 -0
  115. package/dist/templates/codex/hooks/session-start.py +2 -2
  116. package/dist/templates/common/bundled-skills/atw-askme/SKILL.md +1 -1
  117. package/dist/templates/common/bundled-skills/atw-askme-with-docs/SKILL.md +1 -1
  118. package/dist/templates/common/bundled-skills/atw-channel/SKILL.md +5 -2
  119. package/dist/templates/common/bundled-skills/atw-channel/references/command-reference.md +44 -5
  120. package/dist/templates/common/bundled-skills/atw-channel/references/progress-debugging.md +9 -0
  121. package/dist/templates/common/bundled-skills/atw-channel/references/workers.md +48 -4
  122. package/dist/templates/common/bundled-skills/atw-code-review/SKILL.md +78 -3
  123. package/dist/templates/common/bundled-skills/atw-codebase-design/SKILL.md +2 -2
  124. package/dist/templates/common/bundled-skills/atw-codebase-design/{DESIGN-IT-TWICE.md → design-it-twice.md} +3 -3
  125. package/dist/templates/common/bundled-skills/atw-cross-model-review/SKILL.md +361 -0
  126. package/dist/templates/common/bundled-skills/atw-cross-model-review/agents/openai.yaml +5 -0
  127. package/dist/templates/common/bundled-skills/atw-cross-model-review/references/four-tiers.md +42 -0
  128. package/dist/templates/common/bundled-skills/atw-cross-model-review/references/log-review-questions.md +38 -0
  129. package/dist/templates/common/bundled-skills/atw-cross-model-review/references/questions.md +54 -0
  130. package/dist/templates/common/bundled-skills/atw-cross-model-review/references/recheck-questions.md +42 -0
  131. package/dist/templates/common/bundled-skills/atw-cross-model-review/references/report-format.md +22 -0
  132. package/dist/templates/common/bundled-skills/atw-diagnosing-bugs/SKILL.md +13 -2
  133. package/dist/templates/common/bundled-skills/atw-domain-modeling/SKILL.md +2 -2
  134. package/dist/templates/common/bundled-skills/atw-handoff/SKILL.md +1 -1
  135. package/dist/templates/common/bundled-skills/atw-implement/SKILL.md +37 -1
  136. package/dist/templates/common/bundled-skills/atw-implement-all/SKILL.md +219 -24
  137. package/dist/templates/common/bundled-skills/atw-implement-spec/SKILL.md +237 -18
  138. package/dist/templates/common/bundled-skills/atw-improve-codebase-architecture/SKILL.md +2 -2
  139. package/dist/templates/common/bundled-skills/atw-init-repo/SKILL.md +1 -1
  140. package/dist/templates/common/bundled-skills/atw-init-repo/references/issue-tracker-atw-local.md +7 -1
  141. package/dist/templates/common/bundled-skills/atw-init-repo/references/issue-tracker-atw.md +7 -1
  142. package/dist/templates/common/bundled-skills/atw-map/SKILL.md +1 -1
  143. package/dist/templates/common/bundled-skills/atw-meta/SKILL.md +1 -1
  144. package/dist/templates/common/bundled-skills/atw-meta/references/customize-local/add-project-local-conventions.md +4 -3
  145. package/dist/templates/common/bundled-skills/atw-meta/references/customize-local/change-skills-or-commands.md +21 -16
  146. package/dist/templates/common/bundled-skills/atw-meta/references/customize-local/change-task-lifecycle.md +1 -1
  147. package/dist/templates/common/bundled-skills/atw-meta/references/customize-local/overview.md +1 -0
  148. package/dist/templates/common/bundled-skills/atw-meta/references/local-architecture/task-system.md +2 -1
  149. package/dist/templates/common/bundled-skills/atw-meta/references/platform-files/hooks-and-settings.md +1 -0
  150. package/dist/templates/common/bundled-skills/atw-noob-mode/SKILL.md +1 -1
  151. package/dist/templates/common/bundled-skills/atw-performance/SKILL.md +134 -0
  152. package/dist/templates/common/bundled-skills/atw-performance/agents/openai.yaml +3 -0
  153. package/dist/templates/common/bundled-skills/atw-performance/references/optimization-patterns.md +250 -0
  154. package/dist/templates/common/bundled-skills/atw-performance/references/performance-checklist.md +238 -0
  155. package/dist/templates/common/bundled-skills/atw-performance/references/performance-optimization.md +264 -0
  156. package/dist/templates/common/bundled-skills/atw-prototype/SKILL.md +3 -3
  157. package/dist/templates/common/bundled-skills/atw-prototype/{LOGIC.md → logic.md} +1 -1
  158. package/dist/templates/common/bundled-skills/atw-prototype/references/design-direction.md +91 -58
  159. package/dist/templates/common/bundled-skills/atw-prototype/references/direction-persuasion.md +25 -0
  160. package/dist/templates/common/bundled-skills/atw-prototype/references/style-explorer.md +1 -1
  161. package/dist/templates/common/bundled-skills/atw-prototype/ui.md +194 -0
  162. package/dist/templates/common/bundled-skills/atw-reflect/SKILL.md +77 -0
  163. package/dist/templates/common/bundled-skills/atw-reflect/agents/openai.yaml +5 -0
  164. package/dist/templates/common/bundled-skills/atw-reflect/references/divergent-reviewer.md +38 -0
  165. package/dist/templates/common/bundled-skills/atw-reflect/references/judgment-reviewer.md +37 -0
  166. package/dist/templates/common/bundled-skills/atw-reflect/references/synthesizer.md +63 -0
  167. package/dist/templates/common/bundled-skills/atw-reflect/references/tooling-reviewer.md +48 -0
  168. package/dist/templates/common/bundled-skills/atw-security/SKILL.md +248 -0
  169. package/dist/templates/common/bundled-skills/atw-security/agents/openai.yaml +3 -0
  170. package/dist/templates/common/bundled-skills/atw-security/references/hardening-patterns.md +324 -0
  171. package/dist/templates/common/bundled-skills/atw-security/references/security-checklist.md +248 -0
  172. package/dist/templates/common/bundled-skills/atw-session-insight/SKILL.md +1 -1
  173. package/dist/templates/common/bundled-skills/atw-session-insight/references/cli-quick-reference.md +11 -2
  174. package/dist/templates/common/bundled-skills/atw-spec/SKILL.md +1 -1
  175. package/dist/templates/common/bundled-skills/atw-tdd/SKILL.md +25 -0
  176. package/dist/templates/common/bundled-skills/atw-tickets/SKILL.md +1 -1
  177. package/dist/templates/common/bundled-skills/atw-triage/SKILL.md +5 -5
  178. package/dist/templates/common/bundled-skills/atw-ui/SKILL.md +99 -31
  179. package/dist/templates/common/bundled-skills/atw-ui/references/accessibility-checklist.md +160 -0
  180. package/dist/templates/common/bundled-skills/atw-ui/references/layout-and-viewport.md +7 -5
  181. package/dist/templates/common/bundled-skills/atw-ui/references/media.md +117 -20
  182. package/dist/templates/common/bundled-skills/atw-ui/references/motion.md +47 -23
  183. package/dist/templates/common/bundled-skills/atw-ui/references/play.md +18 -0
  184. package/dist/templates/common/bundled-skills/atw-ui/references/reviewer-manual.md +73 -0
  185. package/dist/templates/common/bundled-skills/atw-ui/references/scroll-narrative.md +98 -0
  186. package/dist/templates/common/bundled-skills/atw-ui/references/tools.md +46 -0
  187. package/dist/templates/common/bundled-skills/atw-ui/references/visual-language.md +40 -24
  188. package/dist/templates/common/bundled-skills/atw-ui/references/visual-review.md +26 -82
  189. package/dist/templates/common/bundled-skills/atw-ui/scripts/shoot.mjs +1721 -0
  190. package/dist/templates/common/bundled-skills/atw-verify-skill/SKILL.md +58 -0
  191. package/dist/templates/common/bundled-skills/atw-verify-skill/agents/openai.yaml +5 -0
  192. package/dist/templates/common/bundled-skills/atw-verify-skill/references/feature-map-example/README.md +47 -0
  193. package/dist/templates/common/bundled-skills/atw-verify-skill/references/feature-map-example/create-note.md +39 -0
  194. package/dist/templates/common/bundled-skills/atw-verify-skill/references/feature-map-example/search.md +45 -0
  195. package/dist/templates/common/bundled-skills/atw-verify-skill/references/generate.md +107 -0
  196. package/dist/templates/common/bundled-skills/atw-verify-skill/references/health-check.md +100 -0
  197. package/dist/templates/common/bundled-skills/atw-verify-skill/references/verify-skill-template.md +99 -0
  198. package/dist/templates/common/bundled-skills/atw-what-happened/SKILL.md +49 -0
  199. package/dist/templates/common/bundled-skills/atw-what-happened/agents/openai.yaml +5 -0
  200. package/dist/templates/common/commands/finish-work.md +10 -1
  201. package/dist/templates/common/commands/start.md +3 -1
  202. package/dist/templates/common/skills/update-spec.md +40 -4
  203. package/dist/templates/copilot/hooks/session-start.py +2 -2
  204. package/dist/templates/dsh/DSH.md +2 -2
  205. package/dist/templates/markdown/spec/backend/quality-guidelines.md.txt +10 -0
  206. package/dist/templates/markdown/spec/frontend/quality-guidelines.md.txt +10 -0
  207. package/dist/templates/omp/config.yml +15 -0
  208. package/dist/templates/omp/extensions/atw/index.ts.txt +135 -18
  209. package/dist/templates/omp/index.d.ts +1 -0
  210. package/dist/templates/omp/index.d.ts.map +1 -1
  211. package/dist/templates/omp/index.js +3 -0
  212. package/dist/templates/omp/index.js.map +1 -1
  213. package/dist/templates/opencode/lib/session-utils.js +4 -2
  214. package/dist/templates/opencode/plugins/inject-workflow-state.js +5 -1
  215. package/dist/templates/pi/extensions/atw/index.ts.txt +80 -7
  216. package/dist/templates/shared-hooks/index.d.ts +8 -1
  217. package/dist/templates/shared-hooks/index.d.ts.map +1 -1
  218. package/dist/templates/shared-hooks/index.js +8 -0
  219. package/dist/templates/shared-hooks/index.js.map +1 -1
  220. package/dist/templates/shared-hooks/inject-workflow-state.py +131 -14
  221. package/dist/templates/shared-hooks/session-start.py +4 -2
  222. package/dist/templates/shared-hooks/show-notice.py +135 -0
  223. package/dist/templates/snow/SNOW.md +1 -1
  224. package/dist/utils/initialized.d.ts +9 -0
  225. package/dist/utils/initialized.d.ts.map +1 -0
  226. package/dist/utils/initialized.js +24 -0
  227. package/dist/utils/initialized.js.map +1 -0
  228. package/dist/utils/managed-removal.d.ts +2 -0
  229. package/dist/utils/managed-removal.d.ts.map +1 -1
  230. package/dist/utils/managed-removal.js +23 -3
  231. package/dist/utils/managed-removal.js.map +1 -1
  232. package/dist/utils/optional-skills.d.ts +2 -0
  233. package/dist/utils/optional-skills.d.ts.map +1 -1
  234. package/dist/utils/optional-skills.js +1 -1
  235. package/dist/utils/optional-skills.js.map +1 -1
  236. package/dist/utils/template-hash.d.ts.map +1 -1
  237. package/dist/utils/template-hash.js +3 -0
  238. package/dist/utils/template-hash.js.map +1 -1
  239. package/dist/utils/uninstall-scrubbers.d.ts +10 -0
  240. package/dist/utils/uninstall-scrubbers.d.ts.map +1 -1
  241. package/dist/utils/uninstall-scrubbers.js +50 -0
  242. package/dist/utils/uninstall-scrubbers.js.map +1 -1
  243. package/dist/utils/verify-entries.d.ts +51 -0
  244. package/dist/utils/verify-entries.d.ts.map +1 -0
  245. package/dist/utils/verify-entries.js +399 -0
  246. package/dist/utils/verify-entries.js.map +1 -0
  247. package/package.json +2 -2
  248. package/dist/templates/common/bundled-skills/atw-prototype/UI.md +0 -174
  249. package/dist/templates/common/bundled-skills/atw-prototype/references/visual-language.md +0 -88
  250. package/dist/templates/common/bundled-skills/atw-prototype/references/visual-review.md +0 -63
  251. package/dist/templates/common/bundled-skills/atw-ui/references/imagery.md +0 -69
  252. /package/dist/templates/common/bundled-skills/atw-codebase-design/{DEEPENING.md → deepening.md} +0 -0
  253. /package/dist/templates/common/bundled-skills/atw-domain-modeling/{ADR-FORMAT.md → adr-format.md} +0 -0
  254. /package/dist/templates/common/bundled-skills/atw-domain-modeling/{GLOSSARY-FORMAT.md → glossary-format.md} +0 -0
  255. /package/dist/templates/common/bundled-skills/atw-improve-codebase-architecture/{HTML-REPORT.md → html-report.md} +0 -0
  256. /package/dist/templates/common/bundled-skills/atw-triage/{AGENT-BRIEF.md → agent-brief.md} +0 -0
  257. /package/dist/templates/common/bundled-skills/atw-triage/{OUT-OF-SCOPE.md → out-of-scope.md} +0 -0
@@ -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 |
@@ -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
- - **“这套逻辑 / 状态模型对不对?”** → [LOGIC.md](LOGIC.md)。做一个可分享的单文件 HTML:自由操作的按钮加按标签页划分的引导演练,把纸面上难以推演的状态组合跑一遍,让不写代码的人也能自己操作。
15
- - **“这个界面应该长什么样?”** 或 **“这个页面用起来对不对?”** → [UI.md](UI.md)。分两段:先做几个风格明显不同的方向放进对比页,由用户选定;需要时再把选定方向深化成带业务逻辑的可交互原型,业务逻辑按 LOGIC.md 的方式写成独立的纯模块。
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
- 如果问题是“这个界面应该长什么样”,分支选错了,改用 [UI.md](UI.md)。如果问题是“这个页面连同业务规则用起来对不对”,走 UI.md 的第二段:逻辑仍按本文件第 2 步写成纯模块,外面换成选定方向的设计界面。
14
+ 如果问题是“这个界面应该长什么样”,分支选错了,改用 [ui.md](ui.md)。如果问题是“这个页面连同业务规则用起来对不对”,走 ui.md 的第二段:逻辑仍按本文件第 2 步写成纯模块,外面换成选定方向的设计界面。
15
15
 
16
16
  ## 流程
17
17