synorch 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (105) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/LICENSE +21 -0
  3. package/README.md +56 -0
  4. package/dist/application/doctor-service.d.ts +24 -0
  5. package/dist/application/doctor-service.d.ts.map +1 -0
  6. package/dist/application/doctor-service.js +508 -0
  7. package/dist/application/doctor-service.js.map +1 -0
  8. package/dist/application/project-discovery.d.ts +23 -0
  9. package/dist/application/project-discovery.d.ts.map +1 -0
  10. package/dist/application/project-discovery.js +741 -0
  11. package/dist/application/project-discovery.js.map +1 -0
  12. package/dist/application/skill-resolver.d.ts +25 -0
  13. package/dist/application/skill-resolver.d.ts.map +1 -0
  14. package/dist/application/skill-resolver.js +90 -0
  15. package/dist/application/skill-resolver.js.map +1 -0
  16. package/dist/application/structure-service.d.ts +11 -0
  17. package/dist/application/structure-service.d.ts.map +1 -0
  18. package/dist/application/structure-service.js +116 -0
  19. package/dist/application/structure-service.js.map +1 -0
  20. package/dist/cli.d.ts +3 -0
  21. package/dist/cli.d.ts.map +1 -0
  22. package/dist/cli.js +170 -0
  23. package/dist/cli.js.map +1 -0
  24. package/dist/domain/config.d.ts +210 -0
  25. package/dist/domain/config.d.ts.map +1 -0
  26. package/dist/domain/config.js +108 -0
  27. package/dist/domain/config.js.map +1 -0
  28. package/dist/domain/errors.d.ts +5 -0
  29. package/dist/domain/errors.d.ts.map +1 -0
  30. package/dist/domain/errors.js +9 -0
  31. package/dist/domain/errors.js.map +1 -0
  32. package/dist/domain/generation.d.ts +21 -0
  33. package/dist/domain/generation.d.ts.map +1 -0
  34. package/dist/domain/generation.js +2 -0
  35. package/dist/domain/generation.js.map +1 -0
  36. package/dist/domain/product.d.ts +3 -0
  37. package/dist/domain/product.d.ts.map +1 -0
  38. package/dist/domain/product.js +3 -0
  39. package/dist/domain/product.js.map +1 -0
  40. package/dist/domain/skill-packs.d.ts +39 -0
  41. package/dist/domain/skill-packs.d.ts.map +1 -0
  42. package/dist/domain/skill-packs.js +98 -0
  43. package/dist/domain/skill-packs.js.map +1 -0
  44. package/dist/domain/skill-sources.d.ts +20 -0
  45. package/dist/domain/skill-sources.d.ts.map +1 -0
  46. package/dist/domain/skill-sources.js +95 -0
  47. package/dist/domain/skill-sources.js.map +1 -0
  48. package/dist/infrastructure/bundled-skill-library.d.ts +10 -0
  49. package/dist/infrastructure/bundled-skill-library.d.ts.map +1 -0
  50. package/dist/infrastructure/bundled-skill-library.js +108 -0
  51. package/dist/infrastructure/bundled-skill-library.js.map +1 -0
  52. package/dist/infrastructure/file-system.d.ts +21 -0
  53. package/dist/infrastructure/file-system.d.ts.map +1 -0
  54. package/dist/infrastructure/file-system.js +72 -0
  55. package/dist/infrastructure/file-system.js.map +1 -0
  56. package/dist/infrastructure/serialization.d.ts +3 -0
  57. package/dist/infrastructure/serialization.d.ts.map +1 -0
  58. package/dist/infrastructure/serialization.js +11 -0
  59. package/dist/infrastructure/serialization.js.map +1 -0
  60. package/dist/templates/structure-templates.d.ts +4 -0
  61. package/dist/templates/structure-templates.d.ts.map +1 -0
  62. package/dist/templates/structure-templates.js +471 -0
  63. package/dist/templates/structure-templates.js.map +1 -0
  64. package/dist/templates/technology-skill-templates.d.ts +3 -0
  65. package/dist/templates/technology-skill-templates.d.ts.map +1 -0
  66. package/dist/templates/technology-skill-templates.js +73 -0
  67. package/dist/templates/technology-skill-templates.js.map +1 -0
  68. package/package.json +59 -0
  69. package/skill-sources/ingenium/NOTICE.md +11 -0
  70. package/skill-sources/ingenium/skills/db-schema-craft/SKILL.md +127 -0
  71. package/skill-sources/ingenium/skills/debug-detective/SKILL.md +67 -0
  72. package/skill-sources/ingenium/skills/design-system/SKILL.md +57 -0
  73. package/skill-sources/ingenium/skills/docs-sync/SKILL.md +68 -0
  74. package/skill-sources/ingenium/skills/dotnet-backend/SKILL.md +110 -0
  75. package/skill-sources/ingenium/skills/frontend-craft/SKILL.md +69 -0
  76. package/skill-sources/ingenium/skills/game-audio/SKILL.md +73 -0
  77. package/skill-sources/ingenium/skills/game-design/SKILL.md +92 -0
  78. package/skill-sources/ingenium/skills/godot-dev/SKILL.md +78 -0
  79. package/skill-sources/ingenium/skills/human-made-design/SKILL.md +73 -0
  80. package/skill-sources/ingenium/skills/java-backend/SKILL.md +98 -0
  81. package/skill-sources/ingenium/skills/jev/SKILL.md +150 -0
  82. package/skill-sources/ingenium/skills/motion-craft/SKILL.md +66 -0
  83. package/skill-sources/ingenium/skills/multiplayer-netcode/SKILL.md +63 -0
  84. package/skill-sources/ingenium/skills/node-backend/SKILL.md +113 -0
  85. package/skill-sources/ingenium/skills/node-backend/reference.md +144 -0
  86. package/skill-sources/ingenium/skills/perf-audit/SKILL.md +70 -0
  87. package/skill-sources/ingenium/skills/pixel-art-assets/SKILL.md +91 -0
  88. package/skill-sources/ingenium/skills/pixel-art-assets/scripts/px.py +169 -0
  89. package/skill-sources/ingenium/skills/pixel-game-dev/SKILL.md +79 -0
  90. package/skill-sources/ingenium/skills/project-onboard/SKILL.md +78 -0
  91. package/skill-sources/ingenium/skills/pwa-offline/SKILL.md +70 -0
  92. package/skill-sources/ingenium/skills/query-tuning/SKILL.md +137 -0
  93. package/skill-sources/ingenium/skills/react-modern/SKILL.md +92 -0
  94. package/skill-sources/ingenium/skills/refactor-safe/SKILL.md +63 -0
  95. package/skill-sources/ingenium/skills/release-prep/SKILL.md +57 -0
  96. package/skill-sources/ingenium/skills/safe-merge/SKILL.md +91 -0
  97. package/skill-sources/ingenium/skills/session-recap/SKILL.md +95 -0
  98. package/skill-sources/ingenium/skills/session-recap/scripts/extract_session.py +409 -0
  99. package/skill-sources/ingenium/skills/shader-vfx/SKILL.md +70 -0
  100. package/skill-sources/ingenium/skills/tailwind-v4-tokens/SKILL.md +165 -0
  101. package/skill-sources/ingenium/skills/task-conductor/SKILL.md +158 -0
  102. package/skill-sources/ingenium/skills/tauri-game-dev/SKILL.md +81 -0
  103. package/skill-sources/ingenium/skills/ui-ux-design/SKILL.md +96 -0
  104. package/skill-sources/ingenium/skills/vue-modern/SKILL.md +88 -0
  105. package/skill-sources/ingenium/skills/web-kickoff/SKILL.md +63 -0
@@ -0,0 +1,169 @@
1
+ #!/usr/bin/env python3
2
+ """px.py - pixel art pipeline helpers for the pixel-art-assets skill.
3
+
4
+ Requires Pillow: pip install pillow
5
+
6
+ Commands:
7
+ quantize Downscale to a true pixel grid, snap colors to a palette, binarize alpha
8
+ upscale Integer nearest-neighbor upscale (review previews; always ship the 1x)
9
+ palette List unique opaque colors in an image (audit against the style bible)
10
+ sheet Contact sheet from a folder of PNGs (set-consistency review)
11
+
12
+ Palette format: comma-separated hex ("1a1c2c,5d275d,b13e53") or a file with one
13
+ hex color per line (# prefix and 3-digit shorthand both accepted).
14
+ """
15
+ import argparse
16
+ import math
17
+ import sys
18
+ from pathlib import Path
19
+
20
+ try:
21
+ from PIL import Image
22
+ except ImportError:
23
+ sys.exit("Pillow is required: pip install pillow")
24
+
25
+
26
+ def parse_hex(h: str):
27
+ h = h.strip().lstrip("#")
28
+ if len(h) == 3:
29
+ h = "".join(ch * 2 for ch in h)
30
+ if len(h) != 6:
31
+ sys.exit(f"Bad hex color: {h!r}")
32
+ return tuple(int(h[i:i + 2], 16) for i in (0, 2, 4))
33
+
34
+
35
+ def load_palette(spec: str):
36
+ path = Path(spec)
37
+ if path.exists():
38
+ raw = path.read_text().replace(",", "\n").splitlines()
39
+ else:
40
+ raw = spec.split(",")
41
+ palette = [parse_hex(item) for item in raw if item.strip()]
42
+ if not palette:
43
+ sys.exit("Empty palette")
44
+ return palette
45
+
46
+
47
+ def nearest(color, palette):
48
+ r, g, b = color
49
+ return min(palette, key=lambda c: (c[0] - r) ** 2 + (c[1] - g) ** 2 + (c[2] - b) ** 2)
50
+
51
+
52
+ def out_path(inp: str, suffix: str, explicit: str | None):
53
+ return explicit or str(Path(inp).with_suffix("")) + suffix
54
+
55
+
56
+ def cmd_quantize(a):
57
+ img = Image.open(a.input).convert("RGBA")
58
+ if a.grid:
59
+ try:
60
+ w, h = (int(x) for x in a.grid.lower().split("x"))
61
+ except ValueError:
62
+ sys.exit("--grid expects WIDTHxHEIGHT, e.g. 32x32")
63
+ resample = Image.BOX if a.method == "box" else Image.NEAREST
64
+ img = img.resize((w, h), resample)
65
+ palette = load_palette(a.palette) if a.palette else None
66
+ px = img.load()
67
+ width, height = img.size
68
+ cache = {}
69
+ for y in range(height):
70
+ for x in range(width):
71
+ r, g, b, alpha = px[x, y]
72
+ if alpha < a.alpha_threshold: # binary alpha: no soft edges
73
+ px[x, y] = (0, 0, 0, 0)
74
+ continue
75
+ if palette:
76
+ key = (r, g, b)
77
+ if key not in cache:
78
+ cache[key] = nearest(key, palette)
79
+ r, g, b = cache[key]
80
+ px[x, y] = (r, g, b, 255)
81
+ out = out_path(a.input, ".px.png", a.output)
82
+ img.save(out)
83
+ mode = "palette-locked" if palette else "alpha-cleaned"
84
+ print(f"wrote {out} ({width}x{height}, {mode})")
85
+
86
+
87
+ def cmd_upscale(a):
88
+ img = Image.open(a.input).convert("RGBA")
89
+ img = img.resize((img.width * a.factor, img.height * a.factor), Image.NEAREST)
90
+ out = out_path(a.input, f".x{a.factor}.png", a.output)
91
+ img.save(out)
92
+ print(f"wrote {out} ({img.width}x{img.height})")
93
+
94
+
95
+ def cmd_palette(a):
96
+ img = Image.open(a.input).convert("RGBA")
97
+ counts = {}
98
+ for r, g, b, alpha in img.getdata():
99
+ if alpha >= 128:
100
+ counts[(r, g, b)] = counts.get((r, g, b), 0) + 1
101
+ print(f"{len(counts)} opaque colors in {a.input}")
102
+ ranked = sorted(counts.items(), key=lambda kv: -kv[1])
103
+ for (r, g, b), n in ranked[: a.top]:
104
+ print(f" #{r:02x}{g:02x}{b:02x} x{n}")
105
+ if len(ranked) > a.top:
106
+ print(f" ... and {len(ranked) - a.top} more")
107
+
108
+
109
+ def cmd_sheet(a):
110
+ files = sorted(Path(a.folder).glob("*.png"))
111
+ if not files:
112
+ sys.exit(f"no PNGs in {a.folder}")
113
+ images = [Image.open(f).convert("RGBA") for f in files]
114
+ cell_w = max(i.width for i in images) * a.scale + a.pad
115
+ cell_h = max(i.height for i in images) * a.scale + a.pad
116
+ cols = a.cols or max(1, math.ceil(math.sqrt(len(images))))
117
+ rows = math.ceil(len(images) / cols)
118
+ bg = (*parse_hex(a.bg), 255)
119
+ sheet = Image.new("RGBA", (cols * cell_w + a.pad, rows * cell_h + a.pad), bg)
120
+ for idx, img in enumerate(images):
121
+ img = img.resize((img.width * a.scale, img.height * a.scale), Image.NEAREST)
122
+ cx = a.pad + (idx % cols) * cell_w
123
+ cy = a.pad + (idx // cols) * cell_h
124
+ sheet.paste(img, (cx, cy), img)
125
+ sheet.save(a.output)
126
+ print(f"wrote {a.output} ({len(images)} assets, {cols}x{rows} grid)")
127
+
128
+
129
+ def main():
130
+ parser = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
131
+ sub = parser.add_subparsers(dest="command", required=True)
132
+
133
+ q = sub.add_parser("quantize", help="downscale + palette lock + binary alpha")
134
+ q.add_argument("input")
135
+ q.add_argument("--grid", help="target grid WIDTHxHEIGHT, e.g. 32x32 (omit to keep size)")
136
+ q.add_argument("--method", choices=["box", "nearest"], default="box",
137
+ help="box = average then snap (best for AI cleanup), nearest = crisp sampling")
138
+ q.add_argument("--palette", help="hex list or palette file; omit to only clean alpha")
139
+ q.add_argument("--alpha-threshold", type=int, default=128)
140
+ q.add_argument("--output", "-o")
141
+ q.set_defaults(func=cmd_quantize)
142
+
143
+ u = sub.add_parser("upscale", help="integer nearest-neighbor preview")
144
+ u.add_argument("input")
145
+ u.add_argument("--factor", "-f", type=int, default=8)
146
+ u.add_argument("--output", "-o")
147
+ u.set_defaults(func=cmd_upscale)
148
+
149
+ p = sub.add_parser("palette", help="audit unique opaque colors")
150
+ p.add_argument("input")
151
+ p.add_argument("--top", type=int, default=40)
152
+ p.set_defaults(func=cmd_palette)
153
+
154
+ s = sub.add_parser("sheet", help="contact sheet of a folder of PNGs")
155
+ s.add_argument("folder")
156
+ s.add_argument("--scale", type=int, default=4)
157
+ s.add_argument("--cols", type=int)
158
+ s.add_argument("--pad", type=int, default=8)
159
+ s.add_argument("--bg", default="202020")
160
+ s.add_argument("--output", "-o", default="contact-sheet.png")
161
+ s.set_defaults(func=cmd_sheet)
162
+
163
+ args = parser.parse_args()
164
+ args.func(args)
165
+
166
+
167
+ if __name__ == "__main__":
168
+ main()
169
+
@@ -0,0 +1,79 @@
1
+ ---
2
+ name: pixel-game-dev
3
+ description: Expert 2D pixel-art game development with web technologies (Phaser 3, PixiJS, Kaplay, Canvas) - framework selection, pixel-perfect rendering rules, game loop and entity state machines, Aseprite/Tiled asset pipeline, tilemaps, game feel (juice), performance and itch.io publishing. Use when building or improving a 2D or pixel-art game, choosing a web game framework, fixing blurry pixel art, or asking about game architecture, sprites, tilemaps or game feel. Türkçe tetikleyiciler - "oyun geliştir", "2d oyun yap", "pixel art oyun", "oyun motoru seç", "phaser oyunu", "platformer yap", "oyun mekaniği ekle", "pixel art bulanık görünüyor".
4
+ ---
5
+
6
+ # Pixel Game Dev (Web)
7
+
8
+ You are an expert 2D pixel-art game developer for the web. You build games that feel good, render crisp, and stay maintainable — and you keep the game playable after every single change.
9
+
10
+ Always communicate with the user in their own language.
11
+
12
+ ## Choosing the stack (decide fast, don't churn)
13
+
14
+ | Situation | Pick |
15
+ |---|---|
16
+ | Full engine: physics, tilemaps, animations, big community | **Phaser 3 + TypeScript + Vite** (default) |
17
+ | You want only a fast 2D renderer and will own the architecture | **PixiJS** |
18
+ | Game jam / prototype speed, tiny API | **Kaplay** |
19
+ | TS-first engine with clean OO design | **Excalibur** |
20
+ | Learning exercise or ultra-tiny scope | **Canvas 2D, vanilla** |
21
+
22
+ Default to Phaser 3 unless there's a stated reason otherwise. If the scope is really desktop/native (heavy simulation, console-like), recommend Godot instead — a Godot MCP may be available in this environment for that path.
23
+
24
+ ## Pixel-perfect rendering (the #1 source of "my art looks wrong")
25
+
26
+ - Pick a small **logical resolution** and integer-scale it up: 320×180 or 480×270 (both scale cleanly into 16:9 displays). All game logic works in logical pixels.
27
+ - **Nearest-neighbor everywhere**: Phaser `render: { pixelArt: true }`; PixiJS `TextureStyle.defaultOptions.scaleMode = 'nearest'`; raw canvas `ctx.imageSmoothingEnabled = false`; CSS `image-rendering: pixelated` on the canvas element.
28
+ - **Integer positions at render time**: round camera and sprite positions; sub-pixel positions cause shimmer and uneven pixel sizes. (Phaser: `roundPixels: true`.)
29
+ - **Atlas extrusion**: pack sprites with 1–2px extruded borders to prevent texture bleeding at seams.
30
+ - **Consistent pixel density**: never mix asset scales — a 16px-tile world with a 64px-detailed character reads wrong. Rotation and non-integer scaling break the pixel grid; use them only as deliberate effects.
31
+
32
+ ## Architecture
33
+
34
+ ```
35
+ src/
36
+ scenes/ # Boot → Preload → Menu → Game → (Pause/GameOver overlays)
37
+ entities/ # player, enemies - each with an explicit state machine
38
+ systems/ # input, audio, save, spawning - cross-cutting logic
39
+ ui/ # HUD, menus (screen-space layer)
40
+ config.ts # tuning constants in ONE place (speeds, gravity, timings)
41
+ assets/ # sprites/ tiles/ audio/ fonts/
42
+ ```
43
+
44
+ - **Entity state machines** over boolean soup: `idle | run | jump | fall | hurt` as explicit states with enter/exit — not `isJumping && !isHurt && canMove` chains.
45
+ - **Delta-time all movement** (`speed * dt`); clamp dt to survive tab-switch spikes. Fixed timestep for physics-critical logic.
46
+ - **Object pooling** for anything spawned repeatedly (bullets, particles, enemies).
47
+ - All tuning constants centralized — game feel iteration means changing numbers fast.
48
+
49
+ ## Asset pipeline
50
+
51
+ - **Aseprite** for sprites and animations → export sprite sheet + JSON; animation tags in Aseprite become named animations in-engine.
52
+ - **Tiled** or **LDtk** for maps → Phaser imports Tiled natively (collision layers, object layers for spawn points).
53
+ - Prototype with free packs: **Kenney.nl** (CC0), itch.io asset packs (check licenses before shipping).
54
+ - Audio: `.ogg` + `.m4a` fallback; generate SFX quickly with jsfxr/ChipTone; keep music streamed, SFX preloaded.
55
+
56
+ ## Game feel ("juice") — add after the mechanic works, not before
57
+
58
+ Squash & stretch on jump/land; hit-stop (40–80ms freeze on impact); screen shake (small and short — 2–4px, ~150ms); impact particles; hurt flash (1–2 frames white); a sound on every player-initiated interaction; camera with lerp + deadzone. For platformers specifically: **coyote time** (~80–120ms) and **jump input buffering** (~100ms) — these two turn "unfair" into "tight".
59
+
60
+ ## Performance
61
+
62
+ - One texture atlas per layer where possible → batched draw calls.
63
+ - Zero allocations in the update loop: reuse vectors/objects, no closures created per frame.
64
+ - Cull offscreen entities; pool instead of create/destroy.
65
+ - Profile before optimizing (Chrome Performance tab); the usual suspects are per-frame allocation (GC spikes) and unbatched draws.
66
+
67
+ ## Publishing
68
+
69
+ - Vite production build → **itch.io** (upload via butler for one-command deploys), GitHub Pages or Netlify.
70
+ - Set canvas scaling for fullscreen: integer zoom of the logical resolution, letterbox the remainder.
71
+ - Mobile: only ship touch controls you actually designed; test input latency on a real device.
72
+ - Save data: localStorage with a versioned schema (`{ v: 1, ... }`) and a migration path.
73
+
74
+ ## Working method
75
+
76
+ - **Day 1 playable**: one core mechanic on screen beats any design document. Build vertical slices, not systems in isolation.
77
+ - One system per iteration; the game must run after every change — broken-for-a-week rewrites kill projects.
78
+ - After each milestone, ask the user the playtest question: "does the core loop feel fun yet?" — if not, tune feel before adding content.
79
+
@@ -0,0 +1,78 @@
1
+ ---
2
+ name: project-onboard
3
+ description: Deeply analyze a codebase and produce or refresh its AI-collaboration foundation - a verified CLAUDE.md (exact build/test/run commands, architecture map, conventions, gotchas) so every future AI session starts with full context instead of rediscovering the project. Use when starting on an unfamiliar project, when CLAUDE.md is missing or stale, or when asked to make a repo AI-ready. Türkçe tetikleyiciler - "projeyi tanı", "projeyi analiz et", "claude md oluştur", "projeyi ai için hazırla", "projeyi claude'a tanıt", "onboarding yap".
4
+ ---
5
+
6
+ # Project Onboard
7
+
8
+ You make a repository AI-ready: analyze it deeply, then write (or surgically refresh) a CLAUDE.md that gives every future session the project's working knowledge in under a minute of reading. The value is in *verified* facts and *non-obvious* knowledge — not in restating what any glance at the repo reveals.
9
+
10
+ Always communicate with the user in their own language.
11
+
12
+ ## Phase 1 — Discover
13
+
14
+ - Manifests and lockfiles → stack, package manager (respect the lockfile: pnpm-lock → pnpm, not npm), workspace/monorepo layout.
15
+ - Scripts section, Makefile, justfile, taskfiles → candidate commands.
16
+ - CI workflows (`.github/workflows`, GitLab CI, Azure Pipelines) → the *authoritative* build/test/lint commands; CI is truth, READMEs drift.
17
+ - Entry points, top-level folder map (2–3 levels deep), config files (tsconfig, eslint, docker-compose, .env.example).
18
+ - Existing CLAUDE.md, README, docs — note what they claim; you will verify, not trust.
19
+
20
+ ## Phase 2 — Verify commands by running them
21
+
22
+ Run the safe ones and record exactly what works: lint, type-check, unit tests (a fast subset if the suite is slow), build if it is quick. Capture the real command, from the repo root or the correct subdirectory. A CLAUDE.md with guessed commands is worse than none — every command you write must have run successfully in this session or be explicitly marked unverified.
23
+
24
+ ## Phase 3 — Map the architecture from evidence
25
+
26
+ - Modules/packages and their single-sentence responsibility, with paths.
27
+ - Data flow for the core use case: entry → layers → persistence/external services.
28
+ - Key abstractions the codebase leans on (the base classes, the middleware chain, the store, the event bus) and where they live.
29
+ - Integration points: databases, queues, external APIs, auth provider.
30
+
31
+ Read real code to confirm — folder names lie ("utils" containing the business core is a classic).
32
+
33
+ ## Phase 4 — Detect conventions
34
+
35
+ - Formatting/linting: which tool is the enforcer; never fight it.
36
+ - Naming and file-organization patterns actually used (not aspirational ones from docs).
37
+ - Test layout and style: colocated vs `__tests__`, naming, fixture patterns.
38
+ - Import style (aliases like `@/`), error-handling idioms, commit message style from `git log --oneline -30`.
39
+
40
+ ## Phase 5 — Hunt gotchas (highest-value lines in the file)
41
+
42
+ - Codegen or generated files that must be regenerated, never edited.
43
+ - Required env vars and local services (docker compose, db) needed before anything runs.
44
+ - Order-dependent setup steps; platform quirks (Windows paths, case sensitivity).
45
+ - Slow/flaky test suites and the fast way to run the relevant subset.
46
+ - Anything that made *you* stumble during Phase 2 — that is exactly what goes here.
47
+
48
+ ## Phase 6 — Write CLAUDE.md
49
+
50
+ Rules: commands first; short factual bullets; only non-obvious information; no marketing prose; target ≤ 120 lines; link deeper docs instead of inlining them. If a CLAUDE.md already exists, update it surgically — preserve custom sections and the owner's voice; never clobber.
51
+
52
+ Template:
53
+
54
+ ```markdown
55
+ # <Project>
56
+
57
+ <One sentence: what this is.>
58
+
59
+ ## Commands
60
+ - Install: `...`
61
+ - Dev: `...`
62
+ - Test: `...` (fast subset: `...`)
63
+ - Lint/format: `...`
64
+ - Build: `...`
65
+
66
+ ## Architecture
67
+ - <module> (`path/`): <responsibility>
68
+ - Core flow: <entry> → <layer> → <persistence>
69
+
70
+ ## Conventions
71
+ - <only the ones a newcomer would get wrong>
72
+
73
+ ## Gotchas
74
+ - <the expensive-to-rediscover facts>
75
+ ```
76
+
77
+ Finish by reporting: what you verified by running, what you inferred from reading, and what remains unverified.
78
+
@@ -0,0 +1,70 @@
1
+ ---
2
+ name: pwa-offline
3
+ description: Progressive Web Apps and offline-first engineering - installability (manifest, maskable icons), service worker caching strategies per resource type (precache app shell, stale-while-revalidate APIs, cache-first assets), the service worker update problem solved properly, offline data with IndexedDB and an outbox pattern for writes, offline-capable web games, storage quotas/persistence and honest iOS caveats. Use when making an app or game installable or work offline, adding a service worker, fixing "users see the old version" update bugs, or syncing offline changes. Türkçe tetikleyiciler - "offline çalışsın", "pwa yap", "service worker ekle", "internetsiz çalışsın", "uygulama yüklenebilir olsun", "kullanıcılar eski sürümü görüyor", "cache stratejisi", "offline kayıt".
4
+ ---
5
+
6
+ # PWA & Offline-First
7
+
8
+ You make web apps installable and genuinely useful without a network — with a service worker you can *update reliably*. The update problem is where PWAs actually fail in production, so you design for it from the first line.
9
+
10
+ Always communicate with the user in their own language.
11
+
12
+ ## Phase 0 — Is a PWA the right call?
13
+
14
+ Strong yes: games (playable offline, installed on the home screen/desktop), tools used repeatedly (dashboards, editors, field apps), anything with flaky-network users. Weak case: content sites — good caching headers deliver most of the win without service worker complexity. State the verdict; a service worker is a liability you must maintain, not free progress. (Native shell instead? For desktop games see tauri-game-dev.)
15
+
16
+ ## Phase 1 — Installability
17
+
18
+ - `manifest.json`: `name`, `short_name`, `start_url` (with a `?source=pwa` param so analytics see installs), `display: standalone` (games often want `fullscreen`), `background_color`/`theme_color`, `orientation` for games (`landscape` when the game demands it).
19
+ - Icons: 192 + 512 PNG **plus maskable versions** (`purpose: maskable`, safe zone = inner 80% — test in a maskable preview; non-maskable icons get ugly white circles on Android).
20
+ - Detect standalone mode (`display-mode: standalone` media query) to hide "install" prompts in-app and adjust chrome.
21
+ - Polish that makes installed feel native: `overscroll-behavior: none` (no pull-to-refresh mid-game), `user-select: none` on game surfaces, `viewport-fit=cover` + safe-area insets for notches.
22
+
23
+ ## Phase 2 — Service worker: strategy per resource type
24
+
25
+ Use **Workbox via vite-plugin-pwa** (or the framework's PWA integration) rather than hand-rolling — lifecycle bugs are subtle and Workbox has eaten them for a decade. Configure per type:
26
+
27
+ | Resource | Strategy |
28
+ |---|---|
29
+ | App shell (HTML/JS/CSS build output) | **Precache** with revisioned URLs (the plugin does this) — atomic, versioned, offline-guaranteed |
30
+ | API GET data | **Network-first** (fresh when online, cache fallback offline) or **stale-while-revalidate** for lists that may lag |
31
+ | Images/fonts | **Cache-first** with expiration (maxEntries LRU + maxAgeSeconds) |
32
+ | POST/PUT/DELETE | **Never cached** — queue them (outbox, Phase 4) |
33
+ | Cross-origin/analytics | Network-only; don't let the SW swallow failures silently |
34
+
35
+ - Version caches by name and **delete stale caches on `activate`** — the plugin handles it; verify it happens.
36
+ - Navigation fallback to the shell for SPA routes; keep an `/offline.html` for genuinely uncached navigations.
37
+
38
+ ## Phase 3 — The update problem (where PWAs break trust)
39
+
40
+ Lifecycle truth: a new SW installs but **waits** until every tab of the old one closes — users can run week-old code indefinitely.
41
+
42
+ - The pattern that works: detect the waiting worker → show a small "Yeni sürüm hazır — Yenile" toast → on click, `messageSkipWaiting()` + reload on `controllerchange`. (vite-plugin-pwa exposes exactly these hooks.)
43
+ - Auto-`skipWaiting` without a reload is the classic footgun: new SW + old page = mixed versions, broken chunk loads (old hashed chunks purged from precache).
44
+ - For games: apply updates at the menu, **never mid-session**; version save-schema independently of app version.
45
+ - Always test the update path before shipping *any* SW: deploy A, load it, deploy B, confirm the toast → reload → B. An unupdatable PWA is a support nightmare with no fix but "clear site data".
46
+ - Escape hatch in production: a tiny `version.json` fetched network-only, compared at boot — your kill switch if the SW pipeline breaks.
47
+
48
+ ## Phase 4 — Offline data
49
+
50
+ - **IndexedDB** for structured data (use the `idb` wrapper; raw IDB API is hostile). localStorage only for tiny sync flags/settings — it is synchronous and blocks.
51
+ - **Outbox pattern** for writes: user action → write locally + append to an outbox queue → UI updates optimistically → a flusher sends queued ops when online (Background Sync API where available, `online` event + boot-time flush as the universal fallback).
52
+ - Idempotency: every queued op carries a client-generated ID so retries never double-apply server-side.
53
+ - Conflicts: default to last-write-wins with a server timestamp and *say so*; per-field merge only where the domain demands it (document editing → that's CRDT territory, scope consciously).
54
+ - Sync status is UI: pending/synced/failed indicators — invisible sync means users distrust offline (three-states rule from frontend-craft).
55
+
56
+ ## Phase 5 — Games offline
57
+
58
+ - Precache the entire asset set (atlases, audio, levels) versioned by the build — a game that half-loads offline is worse than one that says it can't.
59
+ - Saves are **local-first always** (IDB), cloud sync as an outbox layer on top; never block play on the network.
60
+ - Big downloads (music packs): cache on demand with a visible "available offline" toggle + progress, not silent hoarding.
61
+ - Check `navigator.storage.estimate()` and request `navigator.storage.persist()` — otherwise the browser may evict your game's data under pressure.
62
+
63
+ ## iOS honesty (say it up front)
64
+
65
+ Installs via Share → Add to Home Screen (no install prompt); no Background Sync (flush-on-open fallback carries it); storage evictable after long disuse (~7-day heuristics for some storage — persist() and warn); push exists on modern iOS but with quirks. iOS PWAs are good, not equal — set expectations.
66
+
67
+ ## Testing checklist
68
+
69
+ DevTools → offline: full app walk. Update path A→B (above). Lighthouse PWA pass. Real Android install + real iOS Add-to-Home-Screen. Airplane-mode cold start of the *installed* app. Storage pressure: what breaks first, and does the app say so?
70
+
@@ -0,0 +1,137 @@
1
+ ---
2
+ name: query-tuning
3
+ description: Diagnosing and fixing slow database access, engine-agnostic with per-engine commands (PostgreSQL, SQL Server, Oracle, MySQL) - measure first and find the actual offending query, read an execution plan and recognize the handful of shapes that matter (seq scan on a filtered table, nested loop over a large set, sort spilling to disk, wrong row estimates), index design that a query can actually use (leftmost prefix, covering indexes, why a function or an implicit cast kills an index), rewriting queries that cannot be indexed out of trouble, keyset instead of OFFSET pagination, and the ORM traps that cause most of it - JPA/Hibernate N+1 and lazy loading, EF Core tracking and cartesian explosion, Prisma/TypeORM includes - plus transaction scope, lock contention, deadlocks and connection pool sizing. Use when a query, endpoint, page or report is slow, when the database CPU is high, when an ORM emits too many queries, or when tuning indexes and transactions. Türkçe tetikleyiciler - "sorgu yavaş", "sayfa geç açılıyor", "n+1 problemi", "index eksik mi", "execution plan oku", "veritabanı cpu yüksek", "deadlock alıyoruz", "sorguyu optimize et", "orm çok sorgu atıyor", "pagination yavaşladı".
4
+ ---
5
+
6
+ # Query Tuning
7
+
8
+ Slow database access is almost never mysterious. It is one of a short list: a query with no usable index, an ORM firing a query per row, a transaction held open too long, or a page fetching far more data than it shows. Find which one — with a measurement, not a hunch — then fix that one.
9
+
10
+ Always communicate with the user in their own language.
11
+
12
+ ## Phase 1 — Measure, and find the real offender
13
+
14
+ Do not optimize anything until you can name the query and its cost. Guessing produces indexes nobody uses and rewrites that change nothing.
15
+
16
+ | Engine | Find the worst queries |
17
+ |---|---|
18
+ | **PostgreSQL** | `pg_stat_statements` ordered by `total_exec_time`, then `mean_exec_time`; `auto_explain` for slow-query plans; `pg_stat_user_tables` for seq-scan counts |
19
+ | **SQL Server** | Query Store (regressed and top-resource-consuming queries); `sys.dm_exec_query_stats`; `sys.dm_db_missing_index_details` as a hint, never as an order |
20
+ | **Oracle** | AWR / ASH reports, `V$SQL` by elapsed time, SQL Monitor for a live long-runner |
21
+ | **MySQL** | `performance_schema.events_statements_summary_by_digest`, the slow query log with `long_query_time` lowered |
22
+
23
+ From the application side: count the queries per request (Hibernate statistics, EF Core logging, Prisma `$on('query')`, an APM trace). **A slow endpoint that issues 340 fast queries is not a slow-query problem** — it is an N+1, and no index will save it.
24
+
25
+ Write down the baseline number before changing anything. "It feels faster" is not a result.
26
+
27
+ ## Phase 2 — Read the plan
28
+
29
+ Get a real plan with real timings, not an estimate:
30
+
31
+ - PostgreSQL: `EXPLAIN (ANALYZE, BUFFERS)` — and compare estimated versus actual rows.
32
+ - SQL Server: the actual execution plan, plus `SET STATISTICS IO, TIME ON` for logical reads.
33
+ - Oracle: `DBMS_XPLAN.DISPLAY_CURSOR` with `ALLSTATS LAST`.
34
+ - MySQL: `EXPLAIN ANALYZE`.
35
+
36
+ Read it outside-in and look for these shapes:
37
+
38
+ | What you see | What it means | Usual fix |
39
+ |---|---|---|
40
+ | Sequential/table scan on a large table with a selective filter | No usable index, or the index cannot be used | Add or fix the index; check Phase 3's index killers |
41
+ | Estimated rows wildly different from actual | Stale statistics or a bad predicate shape | Refresh statistics; simplify or rewrite the predicate |
42
+ | Nested loop driving thousands of iterations | Optimizer expected a small set and got a big one | Fix the estimate; a hash join is usually right for large sets |
43
+ | Sort or hash spilling to disk | Work memory too small, or you are sorting far more rows than you need | Index that provides the order; filter earlier; raise work memory |
44
+ | A filter applied *after* an expensive join | Predicate not pushed down | Move the condition, or restructure the query |
45
+ | High logical reads with few returned rows | Reading a lot to answer a little | Covering index, or better filtering |
46
+ | Lookup/bookmark per row after an index seek | The index found rows but not the columns | Include the needed columns in the index |
47
+
48
+ The number to trust is actual time and rows, not cost. Cost is the optimizer's opinion.
49
+
50
+ ## Phase 3 — Indexes the query can actually use
51
+
52
+ - **Leftmost prefix rule**: an index on `(tenant_id, status, created_at)` serves filters on `tenant_id`, on `tenant_id + status`, and on all three. It does nothing for a query filtering only on `status`.
53
+ - **Column order**: equality predicates first, then the range predicate, then the sort column. Only one range column can be used effectively.
54
+ - **Covering indexes**: adding the selected columns (`INCLUDE` in PostgreSQL/SQL Server) lets the query answer from the index alone — the single biggest win for a hot read.
55
+ - **Selectivity matters.** An index on a boolean or a two-value status is usually ignored; a *partial/filtered* index (`WHERE status = 'pending'`) on the small hot subset is excellent.
56
+
57
+ **Index killers — the query will not use your index if:**
58
+
59
+ - The column is wrapped in a function: `WHERE LOWER(email) = ?` (fix: an expression index, or store normalized).
60
+ - There is a date function on the column: `WHERE YEAR(created_at) = 2026` (fix: a range — `>= '2026-01-01' AND < '2027-01-01'`).
61
+ - There is an implicit type cast: a `varchar` column compared to a number, or a `bigint` column compared to a string. This one is invisible in the SQL and obvious in the plan.
62
+ - The pattern is leading-wildcard: `LIKE '%term'` (fix: full-text search or a trigram index).
63
+ - Collation or character set differs between the column and the compared value or the joined column.
64
+ - `OR` across different columns — often better as a `UNION ALL` of two indexable branches.
65
+ - The predicate is `column != x` or `NOT IN` over a low-selectivity column.
66
+
67
+ **Before adding an index, check whether one already covers it.** Duplicate and near-duplicate indexes slow down every write. After a release, verify usage (`pg_stat_user_indexes`, `sys.dm_db_index_usage_stats`) and drop what nothing touches. Missing-index recommendations from the engine are input, not decisions — they routinely suggest a wide index per query.
68
+
69
+ ## Phase 4 — Rewrite what indexes cannot fix
70
+
71
+ - **Select the columns you need.** `SELECT *` blocks covering indexes, moves useless bytes, and breaks when the schema changes.
72
+ - **`EXISTS` over `IN (subquery)`** for existence checks; `EXISTS` stops at the first match.
73
+ - **Filter and aggregate before joining**, not after. Joining two large tables and then filtering makes the database do work you throw away.
74
+ - **`OFFSET` pagination degrades linearly** — page 5,000 reads 100,000 rows to discard them. Use **keyset (seek) pagination**: `WHERE (created_at, id) < (:lastCreatedAt, :lastId) ORDER BY created_at DESC, id DESC LIMIT 20`. It is flat at any depth, and the ordering columns must be indexed together.
75
+ - **Do not `COUNT(*)` a huge table for every page.** Show "load more", cap the count, or use an approximate count.
76
+ - **Batch instead of looping.** One `WHERE id = ANY(...)`, one multi-row insert, one `MERGE`/upsert — a per-row round trip pays the network latency every time.
77
+ - **Push set work into SQL.** Filtering, grouping and joining a hundred thousand rows in application memory is slower and heavier than letting the database do what it was built for.
78
+ - **Window functions** replace self-joins and per-row subqueries for ranking, running totals and "latest per group".
79
+ - **A materialized view or a maintained summary table** is the right answer for an expensive report read far more often than the data changes — with a stated refresh strategy.
80
+
81
+ ## Phase 5 — The ORM layer (where most of it comes from)
82
+
83
+ **Universal**: log the generated SQL in development and count the statements per request. Almost every ORM performance bug is visible the first time you actually look at the output.
84
+
85
+ **JPA / Hibernate**
86
+
87
+ - N+1 comes from lazy associations touched in a loop. Fix with `JOIN FETCH`, an `@EntityGraph`, or a projection query — not by switching to `EAGER`, which makes every query fetch everything forever.
88
+ - Default to `LAZY` on `@ManyToOne` and `@OneToOne`; the JPA default for `@ManyToOne` is `EAGER` and it is wrong for almost every case.
89
+ - `JOIN FETCH` more than one collection at once produces a cartesian product — fetch one collection, or use multiple queries / `@BatchSize`.
90
+ - Pagination plus `JOIN FETCH` on a collection makes Hibernate fetch everything and paginate in memory (it warns, and people ignore the warning). Paginate ids first, then fetch.
91
+ - Prefer DTO projections (`select new`, interface projections) for read endpoints — no entities, no dirty checking, no lazy surprises.
92
+ - Turn off `open-in-view`. It hides lazy-loading problems until they reach production.
93
+ - Batch writes with `hibernate.jdbc.batch_size` plus ordered inserts, and use `@Version` for optimistic locking rather than long transactions.
94
+
95
+ **EF Core**
96
+
97
+ - `AsNoTracking()` on every read-only query. Tracking on a 500-row list is pure overhead.
98
+ - Project with `Select` into a DTO instead of loading entities and mapping in memory.
99
+ - Multiple `Include`s of collections cause cartesian explosion — `AsSplitQuery()`.
100
+ - Client-side evaluation: a `Where` the provider cannot translate can pull the table into memory. Modern EF throws instead, which is a gift — do not silence it with `AsEnumerable()` before the filter.
101
+ - Never `await` a query inside a `foreach` over entities — batch it.
102
+ - `ExecuteUpdateAsync`/`ExecuteDeleteAsync` for bulk changes rather than loading entities to modify them.
103
+
104
+ **Prisma / TypeORM / Drizzle**
105
+
106
+ - Prisma: `include` versus `select` matters — `select` only what the response needs; nested `include` on lists multiplies rows. Use `findMany` with `in` instead of a query per item, and `$transaction` for batches.
107
+ - TypeORM: `relations` on a paginated query is a classic cartesian bug; use `QueryBuilder` with explicit joins and `take`/`skip` carefully, or paginate ids first. Eager relations are almost always a mistake.
108
+ - Any ORM: repository methods that always fetch every relation "to be safe" are how a detail query becomes twelve.
109
+
110
+ ## Phase 6 — Transactions, locks and pools
111
+
112
+ - **Transactions wrap a unit of work, not a request.** Never hold one open across an HTTP call, a queue publish, or user think-time.
113
+ - Read-only transactions marked as such are cheaper; know your isolation level and why it is what it is.
114
+ - **Deadlocks** are usually inconsistent lock ordering across code paths — acquire rows in a deterministic order (for example ascending id), keep transactions short, and retry the loser with backoff.
115
+ - Long-running writes on hot rows serialize the system. Prefer optimistic locking (`@Version`, `rowversion`) over `SELECT ... FOR UPDATE` unless contention genuinely demands the pessimistic path.
116
+ - Watch for lock waits directly: `pg_locks` plus `pg_stat_activity`, `sys.dm_tran_locks`, `V$LOCK`, `SHOW ENGINE INNODB STATUS`.
117
+ - **Connection pool size is not "bigger is better".** More connections than the database has capacity for makes everything slower; a small pool with fast queries beats a large pool with slow ones. Set statement and idle-transaction timeouts so one stuck query cannot exhaust the pool. Check for pool exhaustion before blaming the query — the symptom is "everything is slow", not "this endpoint is slow".
118
+
119
+ ## Phase 7 — Verify and report
120
+
121
+ Re-run the same measurement from Phase 1 and state the before and after: query time, row counts, statements per request. Confirm the plan actually changed shape — an index that exists but is unused fixed nothing. Check that the change did not slow writes elsewhere, and remove any diagnostic logging you enabled.
122
+
123
+ Report in one short block: what was slow, why, what changed, measured improvement, and anything left on the table.
124
+
125
+ ## Rules
126
+
127
+ 1. Measure and name the offending query before changing anything; record the baseline.
128
+ 2. Count the queries per request first — an N+1 is not fixed by an index.
129
+ 3. Read an actual plan with real timings; trust actual rows and time, never cost.
130
+ 4. Change one thing at a time and re-measure.
131
+ 5. Check for an existing index before adding one; every index taxes writes.
132
+ 6. Never wrap an indexed column in a function, and never compare across mismatched types.
133
+ 7. `SELECT *` does not belong in application queries.
134
+ 8. Keyset pagination for anything deeper than a few pages.
135
+ 9. Transactions stay short and never contain a remote call; lock acquisition order is deterministic.
136
+ 10. Report before/after numbers, and revert any change that did not measurably help.
137
+
@@ -0,0 +1,92 @@
1
+ ---
2
+ name: react-modern
3
+ description: Modern React and its frameworks, current as of August 2026 (React 19.2, React Compiler 1.0, Next.js 16) - server-first mental model with Server Components, Actions/useActionState/useOptimistic forms, use() for async, Compiler instead of manual memoization, framework selection (Next.js 16 vs React Router 7 framework mode vs TanStack Start v1 vs Vite SPA), Next.js 16 Cache Components with stable PPR and Turbopack, plus a stale-habit anti-pattern list and a freshness protocol that verifies current versions before locking decisions. Carries an existing-codebase protocol for brownfield projects - detect installed versions, match the repo's paradigm, modernize only as an explicit opt-in. Use when building, upgrading or optimizing React or Next.js apps, choosing a React framework or asking about current React best practices. Türkçe tetikleyiciler - "react'ta en güncel yöntem", "eski react projesinde çalış", "react 19 özellikleri", "next.js projesi kur", "next.js best practice", "server component nasıl kullanılır", "react compiler", "react projemi optimize et", "hangi react framework'ü", "en güncel react".
4
+ ---
5
+
6
+ # React Modern (2026)
7
+
8
+ You build React the way it works *now*, not the way tutorials taught it in 2022. The 2026 mental model: **server-first, compiler-optimized, actions-driven.** Old habits are the main source of bad modern React — this skill exists to replace them *in the code you write going forward*; existing codebases are protected by the brownfield protocol below.
9
+
10
+ Always communicate with the user in their own language.
11
+
12
+ ## Freshness protocol
13
+
14
+ Knowledge here is current as of **August 2026**: React 19.2 (Compiler 1.0 stable since Oct 2025), Next.js 16.x (Turbopack default, Cache Components + PPR stable), React Router 7, TanStack Start 1.0 (March 2026). Before locking framework/version decisions on a new project or upgrade, verify the current state at react.dev/blog and nextjs.org/blog — if reality has moved past this skill, reality wins and say so.
15
+
16
+ ## Existing codebase protocol (brownfield safety — read before touching an old project)
17
+
18
+ The modern patterns in this skill describe **new code and greenfield decisions**. An existing project is governed by what it already is:
19
+
20
+ - **Detect reality first**: React and framework versions from package.json and the lockfile; the paradigm from the code itself (class components? Pages Router? Redux? CRA?). Five minutes of reading beats one wrong assumption.
21
+ - **Consistency beats modernity inside a codebase.** New code follows the repo's existing patterns — an Actions-and-RSC island pasted into a Pages Router app, or hooks idioms scattered through a class-component codebase, is a maintenance wound, not an upgrade.
22
+ - **Feature-gate by installed version**: no `use()`/Actions below React 19, no `"use cache"`/PPR below Next 16, Compiler adoption is a project decision (supports back to 17, but opt-in) — never emit code the installed versions cannot run.
23
+ - **Modernization is a proposal, never a side effect.** If upgrading would genuinely pay, say so separately — scope, win, risk, migration path — and execute it through the refactor-safe skill (behavior-preserving, incremental). Never modernize in passing while delivering a feature.
24
+
25
+ ## Stale habit → modern replacement (the core table)
26
+
27
+ | 2022 habit | 2026 way |
28
+ |---|---|
29
+ | `useEffect` + `useState` for data fetching | Server Components / framework loaders; on the client TanStack Query; `use()` for passed promises |
30
+ | `forwardRef` | `ref` is a normal prop now |
31
+ | `memo`/`useMemo`/`useCallback` sprinkled everywhere | **React Compiler** memoizes automatically; write plain code |
32
+ | Controlled-everything forms with submit handlers | **Actions**: `<form action={fn}>` + `useActionState` + `useFormStatus`; `useOptimistic` for instant feedback |
33
+ | Global provider/store by default | Server state stays on the server or in a query cache; global client stores only for genuinely global client state |
34
+ | SPA-by-default for everything | Framework decision below — SPA is one deliberate option, not the default |
35
+ | Waterfall `await` chains in loaders | Parallel fetches + Suspense streaming |
36
+
37
+ ## React 19 in practice
38
+
39
+ - **Actions**: any async function passed to `<form action>`, `formAction`, or transitions. `useActionState(fn, initial)` returns `[state, action, isPending]` — errors and pending become data, not choreography.
40
+ - **`useOptimistic`**: render the expected result immediately, reconcile on settle — the default for likes, toggles, list-adds.
41
+ - **`use(promise)`** suspends on a promise created *outside* render (server-passed or cached); `use(Context)` reads context conditionally. It does not replace a query cache.
42
+ - **Refs**: plain prop; ref callbacks may return cleanup functions.
43
+ - **Metadata**: `<title>`/`<meta>` hoist automatically from components (frameworks' metadata APIs still win for dynamic SEO).
44
+
45
+ ## React Compiler (default ON for new projects)
46
+
47
+ - Auto-memoizes components and hooks at build time; works back to React 17; stable 1.0.
48
+ - The contract: follow the Rules of React — pure render, no mutation of props/state, hooks called unconditionally. Enforce with `eslint-plugin-react-compiler`; code the compiler bails on is usually code with a real bug.
49
+ - Delete manual `memo`/`useMemo`/`useCallback` in compiled code paths unless profiling proves a hot spot the compiler missed — keeping both is noise.
50
+
51
+ ## Server Components mental model (RSC)
52
+
53
+ - **Server by default, client at interactivity boundaries.** A component becomes `'use client'` only because it needs state, effects, or browser APIs — and the boundary should sit as deep in the tree as possible (leaf-level islands, not page-level).
54
+ - Server components fetch data next to where it renders (async/await directly in the component), ship **zero JS** for themselves, and can render client components; props crossing the boundary must be serializable.
55
+ - Composition: server components pass *children* into client shells (`<ClientTabs>{serverContent}</ClientTabs>`) — interactivity wrapping server-rendered content.
56
+ - RSC is a framework feature (Next.js; TanStack Start and React Router are adopting selectively) — in a plain Vite SPA this model simply doesn't apply; don't cargo-cult it there.
57
+
58
+ ## Framework decision (2026)
59
+
60
+ | Situation | Pick |
61
+ |---|---|
62
+ | Full-stack app, SEO, content + app hybrid, team default | **Next.js 16** (App Router) |
63
+ | SSR + data mutations with web-standards flavor (Remix lineage) | **React Router 7, framework mode** |
64
+ | Client-heavy app wanting end-to-end type safety (routes, server functions) | **TanStack Start 1.0** (`createServerFn`, fully typed route tree) |
65
+ | Internal tool, dashboard, game shell — no SEO need | **Vite + React SPA** (see web-kickoff) |
66
+
67
+ State the choice and why in one paragraph; churn between frameworks mid-project is the real cost.
68
+
69
+ ## Next.js 16 best practices
70
+
71
+ - **Turbopack is the default bundler** — dev and build; webpack-specific config is legacy.
72
+ - **Cache Components model**: caching is now *explicit and opt-in*. `"use cache"` at the top of a page/layout/component caches its output; **PPR (stable)** serves the static shell instantly from CDN and streams dynamic holes as they resolve. Think in shells and holes: mark the cacheable frame, let personalized bits stream.
73
+ - The old implicit fetch-caching confusion is gone — do not carry Next 13/14 cache folklore forward; cache what you *declare*, nothing else.
74
+ - **Server Actions** for mutations: validate input with a schema (zod) at the top, mutate, then `revalidatePath`/`revalidateTag` — actions are public endpoints, treat them like API routes (auth checks inside the action).
75
+ - `proxy.ts` replaces middleware for network-boundary logic.
76
+ - Streaming discipline: `loading.tsx`/`Suspense` boundaries around slow subtrees; fetch in parallel (`Promise.all` or component-level fetches), never sequential awaits for independent data.
77
+ - Platform pieces: `next/image`, `next/font`, route-level `error.tsx`/`not-found.tsx`; metadata API for SEO.
78
+
79
+ ## Data and state (client side)
80
+
81
+ - Server state on the client → **TanStack Query v5** (the deka data-fetching skill covers patterns in depth — defer to it when installed).
82
+ - Forms → actions first; React Hook Form + zod only when complex live client validation UX demands it.
83
+ - Global client state → start with URL + component state; reach for zustand/jotai when prop-drilling genuinely hurts (see deka state-management when installed).
84
+
85
+ ## Performance, ranked by leverage
86
+
87
+ 1. Compiler on (free re-render elimination) → 2. RSC/PPR: ship less JS, stream the rest → 3. Suspense boundaries placed around actual slow spots → 4. `next/dynamic` for below-the-fold heavyweights → 5. Bundle/asset diet via the perf-audit skill. Measure before and after; folklore optimizations are how bundles grow.
88
+
89
+ ## Anti-patterns (2026 edition)
90
+
91
+ `'use client'` at the page root "to be safe"; useEffect data fetching in framework apps; forwardRef in new code; manual memo carpets under the Compiler; sequential awaits for independent data; Server Action bodies without validation/auth; carrying Next 13/14 implicit-caching assumptions into 16; global stores holding server data; SPA-reflexes (client routing everything) inside an RSC app.
92
+