summer-engine 3.3.0 → 3.4.1

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 (293) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/.claude-plugin/plugin.json +4 -2
  3. package/.codex-plugin/plugin.json +5 -3
  4. package/.cursor-plugin/plugin.json +4 -2
  5. package/.factory-plugin/plugin.json +4 -2
  6. package/.opencode/INSTALL.md +8 -8
  7. package/.opencode/plugins/summer.js +2 -2
  8. package/AGENTS.md +7 -7
  9. package/CHANGELOG.md +50 -0
  10. package/GEMINI.md +1 -1
  11. package/NOTICE +1 -1
  12. package/README.md +32 -35
  13. package/assets/api-docs.json.gz +0 -0
  14. package/assets/autopilot/run.sh +2 -2
  15. package/assets/navigation/web-routes.json +1 -1
  16. package/commands/gameskill.md +2 -2
  17. package/commands/summer.md +25 -25
  18. package/dist/cli/commands/install.d.ts +10 -0
  19. package/dist/cli/commands/install.js +55 -10
  20. package/dist/cli/commands/open.d.ts +1 -1
  21. package/dist/cli/commands/run.d.ts +1 -1
  22. package/dist/cli/commands/run.js +1 -1
  23. package/dist/cli/commands/skills.js +2 -2
  24. package/dist/cli/commands/tool.d.ts +7 -0
  25. package/dist/cli/commands/tool.js +36 -1
  26. package/dist/core/api-client.d.ts +5 -6
  27. package/dist/core/api-client.js +12 -15
  28. package/dist/core/auth.js +2 -2
  29. package/dist/core/capabilities/asset-import.d.ts +0 -1
  30. package/dist/core/capabilities/camera-view.d.ts +1 -1
  31. package/dist/core/capabilities/camera-view.js +2 -2
  32. package/dist/core/capabilities/capture-gameplay.d.ts +69 -0
  33. package/dist/core/capabilities/capture-gameplay.js +283 -0
  34. package/dist/core/capabilities/connect-signal.d.ts +1 -2
  35. package/dist/core/capabilities/connect-signal.js +1 -2
  36. package/dist/core/capabilities/console-read.d.ts +2 -2
  37. package/dist/core/capabilities/console-read.js +2 -2
  38. package/dist/core/capabilities/creator.js +1 -1
  39. package/dist/core/capabilities/doctor.d.ts +9 -1
  40. package/dist/core/capabilities/doctor.js +47 -4
  41. package/dist/core/capabilities/engine-receipt.d.ts +5 -7
  42. package/dist/core/capabilities/engine-receipt.js +7 -9
  43. package/dist/core/capabilities/engine-run.d.ts +4 -0
  44. package/dist/core/capabilities/engine-run.js +33 -5
  45. package/dist/core/capabilities/export-engine-version.d.ts +15 -0
  46. package/dist/core/capabilities/export-engine-version.js +24 -0
  47. package/dist/core/capabilities/export-game.d.ts +9 -1
  48. package/dist/core/capabilities/export-game.js +115 -39
  49. package/dist/core/capabilities/export-presets.d.ts +1 -1
  50. package/dist/core/capabilities/export-presets.js +1 -1
  51. package/dist/core/capabilities/fabricate-mesh.d.ts +2 -3
  52. package/dist/core/capabilities/fabricate-mesh.js +2 -3
  53. package/dist/core/capabilities/frame-quality.d.ts +4 -4
  54. package/dist/core/capabilities/frame-quality.js +4 -4
  55. package/dist/core/capabilities/grep.d.ts +1 -1
  56. package/dist/core/capabilities/grep.js +1 -1
  57. package/dist/core/capabilities/image-generation.d.ts +9 -0
  58. package/dist/core/capabilities/image-generation.js +19 -1
  59. package/dist/core/capabilities/navigation/open.d.ts +1 -1
  60. package/dist/core/capabilities/navigation/open.js +1 -1
  61. package/dist/core/capabilities/navigation/render-product-map.js +1 -1
  62. package/dist/core/capabilities/navigation/targets.js +2 -2
  63. package/dist/core/capabilities/placement-batch.d.ts +1 -1
  64. package/dist/core/capabilities/placement-batch.js +2 -2
  65. package/dist/core/capabilities/play-determinism.d.ts +1 -1
  66. package/dist/core/capabilities/play-determinism.js +1 -1
  67. package/dist/core/capabilities/publish-build.d.ts +15 -2
  68. package/dist/core/capabilities/publish-build.js +78 -28
  69. package/dist/core/capabilities/publish-download.d.ts +1 -2
  70. package/dist/core/capabilities/publish-download.js +2 -3
  71. package/dist/core/capabilities/replace-node.d.ts +1 -3
  72. package/dist/core/capabilities/replace-node.js +1 -3
  73. package/dist/core/capabilities/runtime-control.d.ts +5 -7
  74. package/dist/core/capabilities/runtime-control.js +7 -9
  75. package/dist/core/capabilities/scene-batch.d.ts +1 -1
  76. package/dist/core/capabilities/scene-batch.js +1 -1
  77. package/dist/core/capabilities/scene-kind.js +1 -1
  78. package/dist/core/capabilities/scene-readback.d.ts +3 -4
  79. package/dist/core/capabilities/scene-readback.js +3 -4
  80. package/dist/core/capabilities/scene-script.d.ts +2 -2
  81. package/dist/core/capabilities/scene-script.js +2 -2
  82. package/dist/core/capabilities/seeing/shot-store.d.ts +1 -1
  83. package/dist/core/capabilities/seeing/shot-store.js +1 -1
  84. package/dist/core/capabilities/summer-bundle.d.ts +2 -3
  85. package/dist/core/capabilities/summer-bundle.js +2 -3
  86. package/dist/core/capabilities/surface-snap.d.ts +1 -1
  87. package/dist/core/capabilities/surface-snap.js +1 -1
  88. package/dist/core/capabilities/tool-dispatch.js +11 -1
  89. package/dist/core/capabilities/ui-control.d.ts +1 -2
  90. package/dist/core/capabilities/ui-control.js +1 -2
  91. package/dist/core/capabilities/world-snapshot.d.ts +1 -1
  92. package/dist/core/capabilities/world-snapshot.js +1 -1
  93. package/dist/core/capability-skew.d.ts +2 -3
  94. package/dist/core/capability-skew.js +4 -5
  95. package/dist/core/config.js +2 -2
  96. package/dist/core/engine-install.d.ts +16 -0
  97. package/dist/core/engine-install.js +32 -2
  98. package/dist/core/feedback/client.js +1 -1
  99. package/dist/core/hosted-mcp.d.ts +115 -0
  100. package/dist/core/hosted-mcp.js +164 -0
  101. package/dist/core/launch-posture.d.ts +6 -3
  102. package/dist/core/launch-posture.js +18 -5
  103. package/dist/core/library-read.js +1 -2
  104. package/dist/core/templates.d.ts +1 -2
  105. package/dist/core/templates.js +1 -2
  106. package/dist/mcp/hosted-mount.d.ts +13 -59
  107. package/dist/mcp/hosted-mount.js +24 -30
  108. package/dist/mcp/server.d.ts +2 -2
  109. package/dist/mcp/server.js +5 -4
  110. package/dist/mcp/tools/creator-tools.js +14 -4
  111. package/dist/mcp/tools/debug-tools.js +1 -1
  112. package/dist/mcp/tools/generate-tools.js +19 -9
  113. package/dist/mcp/tools/navigation-tools.d.ts +1 -1
  114. package/dist/mcp/tools/navigation-tools.js +1 -1
  115. package/dist/mcp/tools/spatial-tools.js +1 -1
  116. package/dist/mcp/tools/visual-tools.js +2 -2
  117. package/dist/mcp/tools/with-engine.d.ts +1 -2
  118. package/dist/mcp/tools/with-engine.js +1 -2
  119. package/gemini-extension.json +3 -1
  120. package/hooks/session-start.sh +2 -2
  121. package/integrations/README.md +70 -76
  122. package/integrations/claude/README.md +0 -3
  123. package/integrations/codex/README.md +1 -2
  124. package/integrations/copilot-jetbrains/README.md +1 -1
  125. package/integrations/cursor/README.md +1 -2
  126. package/integrations/factory/README.md +15 -11
  127. package/integrations/mistral-vibe/README.md +1 -1
  128. package/integrations/opencode/README.md +1 -1
  129. package/integrations/roo-code/README.md +1 -1
  130. package/integrations/rovo-dev/README.md +1 -1
  131. package/integrations/trae/README.md +1 -1
  132. package/integrations/windsurf/README.md +3 -3
  133. package/library/references/collaborative-protocol/collaborative-protocol.md +5 -5
  134. package/library/references/gd-style/gd-style.md +4 -6
  135. package/library/references/godot-version/godot-version.md +3 -3
  136. package/library/references/headless-routing/headless-routing.md +2 -2
  137. package/library/references/mcp-fresh-chat-playbook/mcp-fresh-chat-playbook.md +2 -2
  138. package/library/references/mcp-tools-reference/mcp-tools-reference.md +18 -16
  139. package/library/references/product-map/product-map.md +1 -1
  140. package/library/references/public-surface-language/public-surface-language.md +1 -1
  141. package/library/references/summer-folder/summer-folder.md +1 -1
  142. package/library/skills/3d-lighting/SKILL.md +4 -4
  143. package/library/skills/README.md +11 -9
  144. package/library/skills/adaptive-music/SKILL.md +7 -7
  145. package/library/skills/ai-characters-in-games/SKILL.md +1 -1
  146. package/library/skills/ambient-bed/SKILL.md +12 -14
  147. package/library/skills/animated-loop/SKILL.md +42 -42
  148. package/library/skills/animation-tree/SKILL.md +6 -6
  149. package/library/skills/art-direction/SKILL.md +8 -11
  150. package/library/skills/asset-strategy/SKILL.md +3 -3
  151. package/library/skills/audio-direction/SKILL.md +8 -10
  152. package/library/skills/auto-fire-targeting/SKILL.md +4 -3
  153. package/library/skills/bounded-engine-runs/SKILL.md +95 -0
  154. package/library/skills/bounded-engine-runs/resource.yaml +29 -0
  155. package/library/skills/brainstorm-game/SKILL.md +5 -5
  156. package/library/skills/brainstorming/SKILL.md +9 -15
  157. package/library/skills/celeste-momentum-platforming/SKILL.md +8 -13
  158. package/library/skills/celeste-momentum-platforming/resource.yaml +1 -1
  159. package/library/skills/character-animation-wiring/SKILL.md +2 -2
  160. package/library/skills/character-model/SKILL.md +15 -26
  161. package/library/skills/character-movement/SKILL.md +1 -1
  162. package/library/skills/character-movement/references/movement-3d.md +1 -1
  163. package/library/skills/character-portrait/SKILL.md +4 -4
  164. package/library/skills/cinematic-cutscene/SKILL.md +38 -38
  165. package/library/skills/combat-basics/SKILL.md +1 -1
  166. package/library/skills/compatibility-renderer-traps/SKILL.md +78 -0
  167. package/library/skills/compatibility-renderer-traps/resource.yaml +28 -0
  168. package/library/skills/concept-art/SKILL.md +4 -4
  169. package/library/skills/create-asset-sheet/SKILL.md +12 -31
  170. package/library/skills/debug/SKILL.md +5 -4
  171. package/library/skills/debugging-game-feel/SKILL.md +1 -4
  172. package/library/skills/debugging-game-feel/resource.yaml +1 -1
  173. package/library/skills/design-level/SKILL.md +5 -5
  174. package/library/skills/design-mechanic/SKILL.md +6 -6
  175. package/library/skills/design-npc/SKILL.md +8 -15
  176. package/library/skills/diagnosing-perf-regressions/SKILL.md +5 -10
  177. package/library/skills/dispatching-parallel-agents/SKILL.md +1 -19
  178. package/library/skills/environment-kit/SKILL.md +5 -5
  179. package/library/skills/export-and-ship/SKILL.md +25 -24
  180. package/library/skills/fabricating-assets/SKILL.md +1 -1
  181. package/library/skills/facial-and-lipsync/SKILL.md +5 -5
  182. package/library/skills/fps-controller/SKILL.md +17 -16
  183. package/library/skills/fps-controller/resource.yaml +0 -2
  184. package/library/skills/game-feel/SKILL.md +6 -7
  185. package/library/skills/gameskill/SKILL.md +3 -3
  186. package/library/skills/gdscript-patterns/SKILL.md +3 -3
  187. package/library/skills/generate-motion/SKILL.md +5 -22
  188. package/library/skills/godogen-visual-proof-game-generation/SKILL.md +72 -90
  189. package/library/skills/godogen-visual-proof-game-generation/resource.yaml +0 -1
  190. package/library/skills/headless-scripting/SKILL.md +8 -8
  191. package/library/skills/headless-scripting/resource.yaml +1 -0
  192. package/library/skills/in-game-purchases/SKILL.md +123 -118
  193. package/library/skills/in-game-purchases/resource.yaml +2 -2
  194. package/library/skills/instantiate-asset-pack/SKILL.md +7 -3
  195. package/library/skills/investigating-bugs/SKILL.md +3 -11
  196. package/library/skills/investigating-bugs/condition-based-waiting.md +0 -10
  197. package/library/skills/investigating-bugs/root-cause-tracing.md +0 -8
  198. package/library/skills/lumera-single-image-scene-reconstruction/SKILL.md +15 -15
  199. package/library/skills/lumera-single-image-scene-reconstruction/resource.yaml +0 -1
  200. package/library/skills/make-game/SKILL.md +3 -5
  201. package/library/skills/make-game/references/2d-platformer.md +1 -1
  202. package/library/skills/multiplayer-project/SKILL.md +1 -1
  203. package/library/skills/multiplayer-testing/SKILL.md +4 -4
  204. package/library/skills/music-track/SKILL.md +40 -38
  205. package/library/skills/new-project/SKILL.md +4 -6
  206. package/library/skills/organic-model/SKILL.md +9 -9
  207. package/library/skills/pixel-art/SKILL.md +6 -6
  208. package/library/skills/play/SKILL.md +3 -4
  209. package/library/skills/playtesting-a-feature/SKILL.md +4 -6
  210. package/library/skills/procedural-animation/SKILL.md +10 -11
  211. package/library/skills/prop-model/SKILL.md +4 -4
  212. package/library/skills/psx-retro-rendering/SKILL.md +20 -20
  213. package/library/skills/psx-retro-rendering/resource.yaml +2 -3
  214. package/library/skills/realtime-wet-surfaces/SKILL.md +15 -15
  215. package/library/skills/realtime-wet-surfaces/resource.yaml +1 -2
  216. package/library/skills/remote-deploy/SKILL.md +20 -21
  217. package/library/skills/retarget/SKILL.md +19 -23
  218. package/library/skills/running-in-the-cloud/SKILL.md +5 -5
  219. package/library/skills/scene-composition/SKILL.md +5 -5
  220. package/library/skills/scene-hierarchy-design/SKILL.md +3 -3
  221. package/library/skills/scene-hierarchy-design/resource.yaml +0 -1
  222. package/library/skills/scene-scripting/SKILL.md +3 -3
  223. package/library/skills/scene-to-level/SKILL.md +28 -31
  224. package/library/skills/skill-create/SKILL.md +1 -1
  225. package/library/skills/skill-improve/SKILL.md +9 -9
  226. package/library/skills/skill-test/SKILL.md +10 -10
  227. package/library/skills/skintokens-auto-rigging/SKILL.md +18 -18
  228. package/library/skills/skintokens-auto-rigging/resource.yaml +3 -3
  229. package/library/skills/skybox-panorama/SKILL.md +8 -9
  230. package/library/skills/sound-effect/SKILL.md +19 -19
  231. package/library/skills/spatial-placement/SKILL.md +2 -2
  232. package/library/skills/spatial-placement/references/kit-placement-tools.md +12 -22
  233. package/library/skills/sprite-sheet/SKILL.md +10 -10
  234. package/library/skills/summer-analytics/SKILL.md +3 -3
  235. package/library/skills/summer-friends/SKILL.md +10 -10
  236. package/library/skills/summer-leaderboards/SKILL.md +6 -6
  237. package/library/skills/summer-match-results/SKILL.md +13 -13
  238. package/library/skills/summer-matchmaking/SKILL.md +11 -11
  239. package/library/skills/summer-parties/SKILL.md +9 -9
  240. package/library/skills/summer-player-data/SKILL.md +12 -12
  241. package/library/skills/summer-store/SKILL.md +6 -6
  242. package/library/skills/summer-world-chat/SKILL.md +9 -9
  243. package/library/skills/summer-world-saves/SKILL.md +7 -7
  244. package/library/skills/text-to-motion/rigging-for-text-motion.md +1 -1
  245. package/library/skills/tileable-texture/SKILL.md +1 -1
  246. package/library/skills/trailer-shot/SKILL.md +36 -36
  247. package/library/skills/tune-performance/SKILL.md +13 -13
  248. package/library/skills/tune-performance/resource.yaml +1 -1
  249. package/library/skills/ui-graphics/SKILL.md +7 -7
  250. package/library/skills/use-widget-asset/SKILL.md +7 -7
  251. package/library/skills/using-summer/SKILL.md +5 -7
  252. package/library/skills/vehicle-model/SKILL.md +6 -7
  253. package/library/skills/vehicle-model/resource.yaml +2 -2
  254. package/library/skills/verification-before-completion/SKILL.md +5 -6
  255. package/library/skills/vfx-dissolve/SKILL.md +4 -4
  256. package/library/skills/vfx-fire/SKILL.md +6 -6
  257. package/library/skills/vfx-fire/_building-blocks/additive-billboard-particles.md +3 -3
  258. package/library/skills/vfx-hit-spark/SKILL.md +4 -4
  259. package/library/skills/vfx-hit-spark/_building-blocks/additive-billboard-particles.md +3 -3
  260. package/library/skills/vfx-lightning/SKILL.md +5 -5
  261. package/library/skills/vfx-lightning/_building-blocks/additive-billboard-particles.md +3 -3
  262. package/library/skills/vfx-lightning/_building-blocks/trauma-shake-snippet.md +9 -3
  263. package/library/skills/vfx-magic-glow/SKILL.md +4 -4
  264. package/library/skills/vfx-magic-glow/_building-blocks/additive-billboard-particles.md +3 -3
  265. package/library/skills/vfx-muzzle-flash/SKILL.md +5 -5
  266. package/library/skills/vfx-muzzle-flash/_building-blocks/additive-billboard-particles.md +3 -3
  267. package/library/skills/vfx-muzzle-flash/_building-blocks/trauma-shake-snippet.md +9 -3
  268. package/library/skills/vfx-smoke/SKILL.md +5 -5
  269. package/library/skills/vfx-water-ripple/SKILL.md +2 -2
  270. package/library/skills/vfx-water-ripple/_building-blocks/additive-billboard-particles.md +3 -3
  271. package/library/skills/voice-line/SKILL.md +9 -9
  272. package/library/skills/web-compatibility-look/resource.yaml +1 -0
  273. package/library/skills/world-building-3d/references/spatial-tools.md +2 -2
  274. package/library/skills/writing-plans/SKILL.md +4 -4
  275. package/library/skills/writing-skills/SKILL.md +8 -8
  276. package/library/tools/capture-gameplay/resource.yaml +73 -0
  277. package/library/tools/creator-publish/resource.yaml +4 -4
  278. package/library/tools/creator-releases/resource.yaml +5 -5
  279. package/library/tools/generate-image/resource.yaml +15 -1
  280. package/library/tools/publish-build/resource.yaml +6 -2
  281. package/package.json +1 -1
  282. package/registry/generated/aliases.json +1 -0
  283. package/registry/generated/counts.json +3 -3
  284. package/registry/generated/gemini-extension.json +3 -1
  285. package/registry/generated/index.json +248 -135
  286. package/registry/generated/marketplace.claude.json +2 -2
  287. package/registry/generated/plugin.claude.json +4 -2
  288. package/registry/generated/plugin.codex.json +5 -3
  289. package/registry/generated/plugin.cursor.json +4 -2
  290. package/registry/generated/plugin.factory.json +4 -2
  291. package/registry/generated/skills-index.md +11 -9
  292. package/registry/generated/skills-registry.json +33 -6
  293. package/registry/schemas/domains.json +1 -1
@@ -21,7 +21,7 @@ This is the widest capability surface you have, and it is the one you are least
21
21
  <engine-binary> --headless --disable-crash-handler --path <project-dir> -s res://<script>.gd
22
22
  ```
23
23
 
24
- **Always pass `--disable-crash-handler` on an agent-driven run.** Summer's handler shells out to `atos` from inside a signal handler; when anything does go wrong it turns a clean failure into a hang or a cascade. Three agent-triggered crashes in eight minutes were traced to this on 2026-07-25.
24
+ **Always pass `--disable-crash-handler` on an agent-driven run.** Summer's handler shells out to `atos` from inside a signal handler; when anything does go wrong it turns a clean failure into a hang or a cascade of crashes.
25
25
 
26
26
  ### Lint before you boot
27
27
 
@@ -53,7 +53,7 @@ Exit codes lie in both directions here.
53
53
  <EXTREMELY-IMPORTANT>
54
54
  **Every engine launch you make is `--headless`.** The user did not ask you to open anything. A window that appears unasked steals focus, interrupts whatever they were doing, and is indistinguishable from a crash or a bug from their side. This is a product rule, not a convenience.
55
55
 
56
- The one exception is the verify instance, and it is safe precisely because it is not visible: `--summer-verify` opens a real rendering window that is **hidden, `NO_FOCUS`, and positioned at `(-32000,-32000)`** (`main/main.cpp:2363` and following). That is why it can produce real pixels without ever appearing. Never reproduce the "I need a renderer" reasoning by launching a plain windowed editor — use `--summer-verify`.
56
+ The one exception is the verify instance, and it is safe precisely because it is not visible: `--summer-verify` opens a real rendering window that is **hidden, `NO_FOCUS`, and positioned at `(-32000,-32000)`**. That is why it can produce real pixels without ever appearing. Never reproduce the "I need a renderer" reasoning by launching a plain windowed editor — use `--summer-verify`.
57
57
 
58
58
  If you genuinely believe the user needs to see the editor, ask them first.
59
59
  </EXTREMELY-IMPORTANT>
@@ -73,12 +73,12 @@ If neither is available (an older engine build that predates `engineBinaryPath`,
73
73
  | Linux | `~/.summer/engine/summer-linux-x86_64`, or `$SUMMER_ENGINE_BINARY` (see `running-in-the-cloud`) |
74
74
 
75
75
  <EXTREMELY-IMPORTANT>
76
- There is no `godot` binary on a Summer user's machine. `godot --headless`, `godot4`, `/usr/local/bin/godot` — none of these exist. A command built on that name fails with "command not found" on every single user, and no amount of retrying changes it. This skill exists partly because that exact instruction shipped in our own docs for months.
76
+ There is no `godot` binary on a Summer user's machine. `godot --headless`, `godot4`, `/usr/local/bin/godot` — none of these exist. A command built on that name fails with "command not found" for every user, and no amount of retrying changes it.
77
77
  </EXTREMELY-IMPORTANT>
78
78
 
79
79
  ### The script must extend `SceneTree` or `MainLoop`
80
80
 
81
- `-s` does not load an arbitrary script. `main/main.cpp` instantiates it as the process's main loop, and rejects anything that is not one.
81
+ `-s` does not load an arbitrary script. The engine instantiates it as the process's main loop, and rejects anything that is not one.
82
82
 
83
83
  ```gdscript
84
84
  extends SceneTree
@@ -92,7 +92,7 @@ func _initialize():
92
92
 
93
93
  Always call `quit()`. There is no other exit.
94
94
 
95
- **The trap:** a script extending `Node` (the reflex, since almost all GDScript does) is a wedge, not an error. The engine's rejection path calls `OS.alert()`, and on macOS that is an `NSAlert` `runModal` with **no headless guard** (`platform/macos/os_macos.mm:352-369`). The process blocks forever on a modal dialog that has no window to appear in. You get **no output at all** and a hung process that must be killed. Measured: five orphaned processes accumulated in one session before the cause was found.
95
+ **The trap:** a script extending `Node` (the reflex, since almost all GDScript does) is a wedge, not an error. The engine's rejection path calls `OS.alert()`, and on macOS that is an `NSAlert` `runModal` with **no headless guard**. The process blocks forever on a modal dialog that has no window to appear in. You get **no output at all** and a hung process that must be killed. Measured: five orphaned processes accumulated in one session before the cause was found.
96
96
 
97
97
  If a headless run produces zero output and never returns, check the first line of your script before you check anything else.
98
98
 
@@ -262,13 +262,13 @@ Measured on 0.5.55: `port 6550 -> 6551`, new token, editor still alive on 6550 a
262
262
 
263
263
  So on a shipped build: run `--import` against a throwaway copy where you can, warn the user before running it against their open project, and tell them to restart the editor afterwards if their agent connection drops.
264
264
 
265
- **Fixed on `main` but not yet in any shipped binary** (`_should_publish_discovery`, commit `420222554e`; tracked as SUM-161): batch-mode invocations — `--import`, `--summer-verify`, `--export-*`, `-s`, `--headless`, `--quit-after` — no longer publish. Once that ships, this warning applies only to older builds. Plain `--headless -s` never started the server on any build, so everything else in this skill is unaffected.
265
+ A later engine fix stops batch-mode invocations (`--import`, `--summer-verify`, `--export-*`, `-s`, `--headless`, `--quit-after`) from publishing. On a build with that fix this warning does not apply; if you cannot tell which build is installed, follow the precautions above. Plain `--headless -s` never started the server on any build, so everything else in this skill is unaffected.
266
266
  </EXTREMELY-IMPORTANT>
267
267
 
268
268
  Two lifecycle facts that decide the shape of every headless run:
269
269
 
270
270
  - **There is a hard work-completion floor between 3 and 5 seconds.** Fresh project, 8 small PNGs, editor killed at N seconds: at **3s, 0/8 imports landed in 10/10 cycles** — it boots, gets killed mid-scan, and accomplishes nothing. At **5s, 8/8 landed every cycle.** Never give an import a 3-second budget.
271
- - **`--headless --editor` is not a filesystem watcher.** It never notices files created after boot — 6 assets added 12s in, 45s wait, 0 imported. The rescan is driven by `NOTIFICATION_APPLICATION_FOCUS_IN` (`editor_node.cpp:1096`) and headless never fires a focus event. A long-lived headless editor serves a boot-time snapshot forever.
271
+ - **`--headless --editor` is not a filesystem watcher.** It never notices files created after boot — 6 assets added 12s in, 45s wait, 0 imported. The rescan is driven by `NOTIFICATION_APPLICATION_FOCUS_IN`, and headless never fires a focus event. A long-lived headless editor serves a boot-time snapshot forever.
272
272
 
273
273
  Both ends of the lifecycle are broken, differently: short runs do no work, long ones go stale. **Boot → act → exit is the only shape that works today.**
274
274
 
@@ -379,7 +379,7 @@ Four measured instances of the same shape:
379
379
 
380
380
  1. Write the script with `summer_write_file` (or your own shell) into the project.
381
381
  2. Get the binary from `summer_get_project_context` → `engineBinaryPath`.
382
- 3. Run it. **Read stdout** — GDScript errors go there, and a script that failed to parse still exits cleanly-looking.
382
+ 3. Run it. **Read stdout and stderr**: your `print()` output is on stdout, script errors are on stderr, and a script that failed to parse still exits 0.
383
383
  4. Verify the artifact exists on disk and check the `ResourceSaver.save()` return value. "The script ran" is not "the resource was written".
384
384
  5. If you wrote raw asset files, run `--import`.
385
385
  6. Delete throwaway scripts. Do not leave `probe_temp_3.gd` in a user's project.
@@ -18,6 +18,7 @@ compatibility:
18
18
  related:
19
19
  skills:
20
20
  - skill/scene-scripting
21
+ - skill/bounded-engine-runs
21
22
  source: official
22
23
  license: MIT
23
24
  status: stable
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: in-game-purchases
3
- description: "Sell items for Sparks and let short players buy Sparks in game via the Summer purchase sheet: checkout, Sparks purchase, results, testing, store policy."
3
+ description: "Sell items for Sparks in a game shop via the Summer purchase sheet: checkout, results, not enough Sparks, inventory unlocks, authority grants, store policy."
4
4
  license: MIT
5
5
  compatibility: [Cursor, Claude Code, Windsurf, Codex]
6
6
  category: gameplay
@@ -11,14 +11,19 @@ paths: ["**/*.gd"]
11
11
  # In-game purchases with Sparks
12
12
 
13
13
  Sparks are Summer's currency. A game never takes money, never holds a
14
- balance and never approves a payment. It asks Summer, Summer shows its own
15
- purchase sheet over the game, and the game reacts to the authoritative
16
- result. There are two calls and they follow the same shape:
14
+ balance and never approves a payment. It asks Summer for a checkout, Summer
15
+ shows its own purchase sheet over the game, and the game reacts to the
16
+ authoritative result.
17
17
 
18
18
  | The player wants to | Call | Validated by |
19
19
  | --- | --- | --- |
20
20
  | buy an item with Sparks they have | `Summer.client.store.request_checkout(lines)` | the Summer ledger (debit) |
21
- | buy Sparks because they are short | `Summer.client.store.request_sparks_purchase(minimum_sparks)` | the payment provider (Stripe or Apple), then the ledger credit |
21
+ | buy Sparks because they are short | no game call: the player gets Sparks in the Summer app | the Summer app |
22
+
23
+ The engine has no call that sells Sparks from inside the game. When a
24
+ checkout fails with `insufficient_funds`, tell the player they need more
25
+ Sparks and that they can get them in the Summer app, then let them press Buy
26
+ again.
22
27
 
23
28
  ## When to sell with Sparks
24
29
 
@@ -28,9 +33,9 @@ result. There are two calls and they follow the same shape:
28
33
  game.
29
34
  - Do not invent your own currency that is bought with money. *Why:* Summer
30
35
  can only refund, protect and pay you out for Sparks purchases.
31
- - Offer "Get Sparks" only when a purchase failed for lack of Sparks, or on an
32
- explicit shop button. *Why:* the Summer sheet interrupts play; surprise
33
- payment prompts read as pressure and get games reported.
36
+ - Open the shop only from an explicit shop or buy button. *Why:* the Summer
37
+ sheet interrupts play; surprise payment prompts read as pressure and get
38
+ games reported.
34
39
 
35
40
  ## Rules and the reason for each
36
41
 
@@ -38,66 +43,66 @@ result. There are two calls and they follow the same shape:
38
43
  `Summer.client.items` shows the player owns it. *Why:* the receipt proves
39
44
  the debit; the inventory projection is what the platform guarantees the
40
45
  player owns, including after a crash or on another device.
41
- 2. **Never send prices, amounts or providers.** The game passes an offer ID
42
- and a quantity, or a Sparks hint. *Why:* the platform prices everything; a
43
- client-supplied price is exactly what a cheater edits.
44
- 3. **One call is one purchase.** Every call creates a new identity. Never
45
- call again to "retry" an unknown outcome. *Why:* the first request may
46
- already be charged; the SDK and the Summer app recover it and deliver the
47
- result on the signal.
48
- 4. **Handle results only in the status signals, and deduplicate by
46
+ 2. **Never send prices or amounts.** The game passes an offer ID and a
47
+ quantity (`SummerCheckoutLine.create(offer_id, quantity)`). *Why:* the
48
+ platform prices everything; a client-supplied price is exactly what a
49
+ cheater edits.
50
+ 3. **One call is one purchase.** Every `request_checkout` creates a new
51
+ identity. Never call again to "retry" an unknown outcome
52
+ (`SummerCheckoutOperation.is_outcome_unknown()`). *Why:* the first request
53
+ may already be charged; the SDK recovers it and delivers the result on the
54
+ signal.
55
+ 4. **Handle results in `checkout_status_changed`, and deduplicate by
49
56
  `request_id` plus `version`.** Connect normally (not deferred) before
50
- requesting. *Why:* results can arrive after a restart or twice after a
51
- crash; a synchronous handler is how the SDK knows the result was delivered.
52
- 5. **Treat `manual_review` and pending as not done.** *Why:* the platform
57
+ requesting. To draw a checkout panel, read `get_checkout_statuses()` and
58
+ `has_pending_checkouts()` instead of keeping your own list. *Why:* results
59
+ can arrive after a restart or twice after a crash; a synchronous handler is
60
+ how the SDK knows the result was delivered.
61
+ 5. **Treat `manual_review` and `pending` as not done.** *Why:* the platform
53
62
  has not decided yet; showing success or failure would be a guess.
54
63
  6. **Check readiness and hide what is unavailable.**
55
- `check_checkout_readiness()` and `check_sparks_purchase_readiness()` return
56
- a `SummerResult`. *Why:* editor play, guest sessions and older Summer apps
57
- do not offer purchases; a dead button is worse than no button.
64
+ `check_checkout_readiness()` (buying) and `check_browse_readiness()`
65
+ (listing with `list_items()`) return a `SummerResult`. *Why:* editor play,
66
+ guest sessions and older Summer apps do not offer purchases; a dead button
67
+ is worse than no button.
58
68
 
59
69
  ## Result states
60
70
 
61
- Both signals deliver a status with `state`:
71
+ `checkout_status_changed(status: SummerCheckoutStatus)` delivers a status
72
+ whose `state` is one of:
62
73
 
63
74
  | State | Meaning | What the game does |
64
75
  | --- | --- | --- |
65
76
  | `awaiting_approval` | the Summer sheet is open | nothing; optionally dim the shop |
66
- | `pending` | payment or debit in progress | show "processing" |
67
- | `purchased` | done; Sparks debited (checkout) or credited (Sparks) | checkout: refresh items and unlock from inventory. Sparks: say "Sparks added", let the player buy again |
77
+ | `pending` | the debit is in progress | show "processing" |
78
+ | `purchased` | done; Sparks debited, `status.receipt` is set | refresh items and unlock from inventory |
68
79
  | `declined` | the player closed the sheet | return to the shop quietly |
69
- | `expired` | the sheet or payment timed out | same as declined |
70
- | `rejected` | refused; read `error_code` | checkout with `insufficient_funds`: offer "Get Sparks". Sparks: `provider_rejected`, say the payment did not go through |
80
+ | `expired` | the sheet timed out | same as declined |
81
+ | `rejected` | refused; read `error_code` | `insufficient_funds`: "Not enough Sparks. Get Sparks in the Summer app." Other codes: "The purchase was not completed." |
71
82
  | `manual_review` | platform is checking it | "We are confirming this purchase" |
72
- | `refunded` | Sparks only: a credited purchase was refunded or charged back (`full_refund`, `dispute_lost`) | nothing to undo; Summer already took the Sparks back. Refresh any balance display |
73
83
 
74
- A Sparks request can also fail to start with `purchase_limit` (the player
75
- reached the daily in-game Sparks limit) or `age_required` / `adult_required`
76
- (only adults with a saved age can buy Sparks). Show a short message and no
77
- retry button. *Why:* both limits are platform policy; retrying changes nothing.
84
+ `status.is_terminal()` tells you whether the state can still change. Unknown
85
+ future `error_code` values never mean success.
78
86
 
79
- The operation returned by each call also completes (`completed` signal) on
80
- `purchased` (ok) or a terminal failure with codes such as `checkout_declined`
81
- or `sparks_purchase_declined`. The status signal is the one to build UI on.
87
+ The operation returned by `request_checkout` also completes: ok on
88
+ `purchased`, a failure otherwise. The status signal is the one to build UI on.
82
89
 
83
90
  ## Minimal complete example
84
91
 
85
92
  ```gdscript
86
93
  extends Node
87
- ## A one-item shop. Buys `offer_id`; if the player is short, lets them buy
88
- ## Sparks through the Summer sheet, then they press Buy again.
94
+ ## A one-item shop. Buys `offer_id` and unlocks from inventory.
89
95
 
90
96
  @export var offer_id := "offer_sword"
91
97
  @export var definition_id := "item_sword"
92
- @export var price_sparks := 250 # display only; the platform charges its own price
93
98
 
94
99
  signal message(text: String)
95
100
  var _seen := {}
96
101
 
97
102
  func _ready() -> void:
98
- var store := Summer.client.store
99
- store.checkout_status_changed.connect(_on_checkout)
100
- store.sparks_purchase_status_changed.connect(_on_sparks)
103
+ if Summer.client == null:
104
+ return # not running as a player's game
105
+ Summer.client.store.checkout_status_changed.connect(_on_checkout)
101
106
  Summer.client.items.refreshed.connect(_sync_owned)
102
107
  _sync_owned()
103
108
 
@@ -108,13 +113,7 @@ func buy() -> void:
108
113
  var lines: Array[SummerCheckoutLine] = [SummerCheckoutLine.create(offer_id, 1)]
109
114
  Summer.client.store.request_checkout(lines) # result arrives in _on_checkout
110
115
 
111
- func get_sparks() -> void:
112
- if not Summer.client.store.check_sparks_purchase_readiness().ok:
113
- message.emit("Get Sparks from the Summer app.")
114
- return
115
- Summer.client.store.request_sparks_purchase(price_sparks) # hint only
116
-
117
- func _first_time(status) -> bool:
116
+ func _first_time(status: SummerCheckoutStatus) -> bool:
118
117
  var key := "%s:%d" % [status.request_id, status.version]
119
118
  if _seen.has(key):
120
119
  return false
@@ -129,7 +128,7 @@ func _on_checkout(status: SummerCheckoutStatus) -> void:
129
128
  Summer.client.items.refresh() # ownership comes from inventory
130
129
  &"rejected":
131
130
  if status.error_code == &"insufficient_funds":
132
- message.emit("Not enough Sparks.") # show a Get Sparks button -> get_sparks()
131
+ message.emit("Not enough Sparks. Get Sparks in the Summer app, then buy again.")
133
132
  else:
134
133
  message.emit("The purchase was not completed.")
135
134
  &"manual_review":
@@ -137,84 +136,91 @@ func _on_checkout(status: SummerCheckoutStatus) -> void:
137
136
  &"declined", &"expired":
138
137
  pass
139
138
 
140
- func _on_sparks(status: SummerSparksPurchaseStatus) -> void:
141
- if not _first_time(status):
142
- return
143
- match status.state:
144
- &"purchased":
145
- message.emit("%d Sparks added." % status.receipt.sparks_credited)
146
- &"rejected":
147
- message.emit("The payment did not go through.")
148
- &"manual_review":
149
- message.emit("We are confirming your payment.")
150
-
151
139
  func _sync_owned() -> void:
152
140
  if Summer.client.items.has_owned_definition(definition_id):
153
141
  message.emit("Sword unlocked.") # the only place the item is granted
154
142
  ```
155
143
 
156
- After Sparks are added the player presses Buy again. That is a new checkout
157
- and Summer shows it again. *Why:* the player confirms each spend separately.
158
-
159
144
  ## Multiplayer: granting an effect on a dedicated authority
160
145
 
161
- Buying Sparks only adds Sparks to the wallet. Anything the Sparks buy
162
- ("Grow now", "Summon rain") is a separate checkout, and on a server the
163
- authority must never grant it because a client says it paid. *Why:* a client
164
- message is the one thing a cheater controls.
165
-
166
- Model the effect as a consumable catalog item with instance ownership (each
167
- purchase is a separate item, not a stack). *Why:* a stackable (quantity) item
168
- shares one owned item ID across purchases, so the ID could not tell two
169
- purchases apart. The client checks out; the receipt's `owned_item_ids` are item instances in
170
- the player's Summer inventory. The client sends the authority only that owned
171
- item ID. The authority consumes it with
172
- `Summer.authority.items.consume(session, owned_item_id, idempotency_key)` and
173
- applies the effect only after a successful receipt. *Why:* the platform ledger
174
- consumes an instance item once, so the same item cannot be redeemed twice, in
175
- this world or any other.
146
+ Anything the Sparks buy ("Grow now", "Summon rain") is a checkout for an
147
+ item, and on a server the authority must never grant it because a client says
148
+ it paid. *Why:* a client message is the one thing a cheater controls.
149
+
150
+ Model the effect as a catalog item with instance ownership (each purchase is a
151
+ separate owned item, not a stack). *Why:* a stackable item shares one owned
152
+ item ID across purchases, so the ID could not tell two purchases apart.
153
+
154
+ The client checks out; the receipt's `items[i].owned_item_ids` are the owned
155
+ items. The client sends the authority only an owned item ID. The authority:
156
+
157
+ 1. reads that player's inventory with
158
+ `Summer.authority.items.inventory_for_session(session)` and checks the
159
+ item is there, owned, and of the right definition;
160
+ 2. checks its own record of redeemed item IDs, kept in the player's secret
161
+ player data (`Summer.authority.player_data`), so the item grants once per
162
+ player in every world;
163
+ 3. writes the new record, and applies the effect only after that write
164
+ succeeds.
176
165
 
177
166
  ```gdscript
178
- # On the dedicated authority. `redeemed` is saved with the world.
167
+ # On the dedicated authority.
168
+ const EFFECT_DEFINITION := "item_grow_now"
169
+
179
170
  func redeem(session: SummerSession, owned_item_id: String) -> void:
171
+ var inv := Summer.authority.items.inventory_for_session(session)
172
+ var read: SummerResult = await inv.get_result_or_completed_signal()
173
+ if not read.ok:
174
+ return # unavailable: the client may ask again later
175
+ var item: SummerInventoryItem = null
176
+ for candidate in inv.items:
177
+ if candidate.get_owned_item_id() == owned_item_id:
178
+ item = candidate
179
+ if item == null or not item.is_owned() or item.get_definition_id() != EFFECT_DEFINITION:
180
+ return # not owned by this player: never grant
181
+
182
+ var load_op := Summer.authority.player_data.load(session)
183
+ var loaded: SummerResult = await load_op.get_result_or_completed_signal()
184
+ if not loaded.ok:
185
+ return
186
+ var data: Dictionary = load_op.secret_record.data
187
+ var redeemed: Dictionary = data.get("redeemed", {})
180
188
  if redeemed.has(owned_item_id):
181
189
  return # a repeated message grants once
182
- var key := "redeem_" + owned_item_id.sha256_text().substr(0, 40)
183
- var op := Summer.authority.items.consume(session, owned_item_id, key)
184
- var result: SummerResult = await op.get_result_or_completed_signal()
185
- if not result.ok:
186
- # item_not_found: not owned or already used, never grant.
187
- # unavailable: retry later with the same key.
188
- return
189
- redeemed[owned_item_id] = op.command_id # record, apply, then save the world
190
+ redeemed[owned_item_id] = true
191
+ data["redeemed"] = redeemed
192
+ var save_id := "redeem_" + owned_item_id.sha256_text().substr(0, 40)
193
+ var commit := Summer.authority.player_data.commit_secret(session, save_id, data)
194
+ var saved: SummerResult = await commit.get_result_or_completed_signal()
195
+ if not saved.ok:
196
+ return # retryable: commit again with the same save_id
190
197
  apply_grow_now(session)
191
198
  ```
192
199
 
193
- - Derive the key from the owned item ID. *Why:* the same key returns the same
194
- receipt (`op.replayed` is true), so a retry after a timeout never consumes
195
- twice, and a different item with that key fails with `idempotency_conflict`.
196
- - Apply the effect when the receipt arrives even if `op.replayed` is true, as
197
- long as `redeemed` does not have the item. *Why:* a replay means the item was
198
- consumed earlier but your world may have crashed before saving the effect.
199
- - Save the world right after applying. *Why:* the receipt replay is tied to
200
- the authority process; after a restart as a different process the same key
201
- is a new consumption and the already-consumed item returns `item_not_found`.
200
+ - The save ID comes from the owned item ID. *Why:* resending the same save ID
201
+ with the same content returns the earlier commit (`commit.replayed`), so a
202
+ retry after a timeout records the redemption once.
203
+ - Apply the effect when the commit succeeds even if `commit.replayed` is true
204
+ and the effect is not in your world yet. *Why:* a replay means the
205
+ redemption was recorded earlier, but your world may have crashed before it
206
+ applied the effect.
207
+ - The engine has no call that uses up an owned item. The item stays in the
208
+ player's inventory after it is redeemed; the redeemed list is what stops a
209
+ second grant. Show redeemed items as used in your UI.
202
210
 
203
211
  ## Idempotency
204
212
 
205
- The SDK generates the request ID and replays it across transport failures and
206
- app restarts; the platform credits a Sparks purchase once per provider
207
- transaction and debits a checkout once per request. Your only job is the
208
- `request_id:version` dedupe above, so a replayed signal does not show a
209
- message or play an effect twice.
213
+ The SDK generates the checkout request ID and replays it across transport
214
+ failures and app restarts; the platform debits a checkout once per request.
215
+ Your only job is the `request_id:version` dedupe above, so a replayed signal
216
+ does not show a message or play an effect twice.
210
217
 
211
218
  ## Per-surface behaviour
212
219
 
213
- - **Summer Games app on iOS:** a native Summer sheet over the game. Sparks are
214
- bought with Apple In-App Purchase only.
215
- - **Summer desktop app:** an overlay sheet; Sparks are paid with Stripe in the
216
- browser, and the result returns to the game on the signal.
217
- - **Web:** the same sheet in the page, paid with Stripe.
220
+ - **Summer Games app on iOS:** a native Summer sheet over the game.
221
+ - **Summer desktop app:** an overlay sheet; the result returns to the game on
222
+ the signal.
223
+ - **Web:** the same sheet in the page.
218
224
  - **Editor play and local runs:** readiness is `unavailable`. Build the
219
225
  unavailable path first.
220
226
 
@@ -222,31 +228,30 @@ The game code is identical on every surface.
222
228
 
223
229
  ## Testing
224
230
 
225
- - Test against Summer staging, which runs payments in sandbox mode: Stripe
226
- sandbox cards on web and desktop, an Apple Sandbox tester account on iOS test
227
- builds. *Why:* no real money moves and results are identical to production.
231
+ - Test against Summer staging, which runs in sandbox mode. *Why:* no real
232
+ money moves and results are identical to production.
228
233
  - Walk every state: decline the sheet, let it expire, buy with too few
229
- Sparks, buy Sparks, then buy the item. Kill the game while a payment is
230
- pending and relaunch; the result must still arrive once.
234
+ Sparks, get Sparks in the Summer app, then buy the item. Kill the game while
235
+ a checkout is pending and relaunch; the result must still arrive once.
231
236
  - Verify the unlock comes from `items.refreshed`, not from the checkout
232
237
  signal.
233
238
 
234
239
  ## Store policy notes
235
240
 
236
241
  - Apple requires In-App Purchase for digital goods and currency sold inside
237
- iOS apps. Summer's iOS sheet handles this; never add your own payment links,
238
- web prices or "cheaper on the web" text inside the game. *Why:* it can get
239
- the Summer app removed from the App Store for every game.
242
+ iOS apps. Summer's iOS sheet and app handle this; never add your own payment
243
+ links, web prices or "cheaper on the web" text inside the game. *Why:* it
244
+ can get the Summer app removed from the App Store for every game.
240
245
  - Randomized paid rewards must disclose their odds before purchase (Apple and
241
246
  most regions require it).
242
247
  - Say what a purchase gives in plain words next to the price.
243
248
 
244
249
  ## Availability
245
250
 
246
- `request_checkout` ships today. `request_sparks_purchase` needs a Summer
247
- Engine and Summer app release that advertise `player.store.sparks_purchase@1`;
248
- until then `check_sparks_purchase_readiness()` reports `unavailable` and the
249
- example above falls back to "Get Sparks from the Summer app".
251
+ `request_checkout`, `list_items`, `Summer.client.items`,
252
+ `Summer.authority.items.inventory_for_session` and `Summer.authority.player_data`
253
+ ship in the engine today. Buying Sparks inside the game does not: players get
254
+ Sparks in the Summer app.
250
255
 
251
256
  ## See also
252
257
 
@@ -1,10 +1,10 @@
1
1
  id: skill/in-game-purchases
2
2
  kind: skill
3
3
  version: 1.0.0
4
- summary: "Sell items for Sparks and let short players buy Sparks in game via the Summer purchase sheet: checkout, Sparks purchase, results, testing, store policy."
4
+ summary: "Sell items for Sparks in a game shop via the Summer purchase sheet: checkout, results, not enough Sparks, inventory unlocks, authority grants, store policy."
5
5
  use_when:
6
6
  - "adding a shop, buy button or paid item to a Summer game and wiring request_checkout"
7
- - "a checkout was rejected with insufficient_funds and the player should be able to buy Sparks without leaving the game"
7
+ - "the player does not have enough Sparks (checkout rejected with insufficient_funds) and the game should tell them how to buy more Sparks"
8
8
  - "deciding whether an item should be sold for Sparks, or handling pending, declined, expired, rejected or manual_review purchase results"
9
9
  do_not_use_when:
10
10
  - "designing multiplayer state ownership unrelated to purchases (use multiplayer-state)"
@@ -27,7 +27,7 @@ For wiring individual widget slices (9-slice panels, progress bars, toggles), th
27
27
 
28
28
  - User wants a single slice → import it directly as a `Sprite2D` (or `use-widget-asset` if it's a UI widget).
29
29
  - User wants to **generate** a new pack → `create-asset-sheet`.
30
- - User wants to apply a slice as a material/texture on a 3D mesh → not the pack flow; see `asset_pipeline.json`.
30
+ - User wants to apply a slice as a material/texture on a 3D mesh → not the pack flow; see `asset-strategy`.
31
31
 
32
32
  ## Pack JSON shape
33
33
 
@@ -98,12 +98,15 @@ Delegate the actual wiring (percentage-to-pixel conversion, node-specific props)
98
98
 
99
99
  ## Examples
100
100
 
101
+ Both examples are `summer_batch` arguments; `scenePath` names the scene that receives the nodes.
102
+
101
103
  ### Example 1 — Standalone slice import
102
104
 
103
105
  Pack has one non-composite slice, `crate_wooden`, no widget metadata. Place it at scene origin:
104
106
 
105
107
  ```json
106
108
  {
109
+ "scenePath": "res://scenes/level.tscn",
107
110
  "ops": [
108
111
  {
109
112
  "op": "AddNode",
@@ -138,6 +141,7 @@ Pack has a composite parent `settings_panel` (filtered out) plus three children:
138
141
 
139
142
  ```json
140
143
  {
144
+ "scenePath": "res://ui/settings.tscn",
141
145
  "ops": [
142
146
  {
143
147
  "op": "AddNode",
@@ -224,7 +228,7 @@ Pack has a composite parent `settings_panel` (filtered out) plus three children:
224
228
 
225
229
  After this, hand `panel_bg` and `apply_button` to `use-widget-asset` to set `patch_margin_*` from their `widget.frameMargins`.
226
230
 
227
- Note the ops format: `position` is the **Godot string** `"Vector2(0, 0)"`, not a JSON object. `z_index` is a bare integer. This is the format Summer Engine expects — see `../../references/mcp-tools-reference/mcp-tools-reference.md`.
231
+ Note the ops format: `position` is the **engine variant string** `"Vector2(0, 0)"`, not a JSON object. `z_index` is a bare integer. This is the format Summer Engine expects — see `../../references/mcp-tools-reference/mcp-tools-reference.md`.
228
232
 
229
233
  ## Failure modes
230
234
 
@@ -240,4 +244,4 @@ Note the ops format: `position` is the **Godot string** `"Vector2(0, 0)"`, not a
240
244
 
241
245
  - `create-asset-sheet` — production side; how the pack was made.
242
246
  - `use-widget-asset` — wiring an individual widget slice (NinePatchRect, TextureProgressBar geometry).
243
- - `asset_pipeline.json` — broader find-or-generate -> import -> apply flow.
247
+ - `asset-strategy` — broader find-or-generate -> import -> apply flow.
@@ -230,9 +230,9 @@ If you catch yourself thinking:
230
230
 
231
231
  **ALL of these mean: STOP. Return to Phase 1.**
232
232
 
233
- **If 3+ fixes failed:** Question the architecture (see Phase 4.5)
233
+ **If 3+ fixes failed:** Question the architecture (Phase 4, step 5)
234
234
 
235
- ## the user's Signals You're Doing It Wrong
235
+ ## Signals From the User That You're Doing It Wrong
236
236
 
237
237
  **Watch for these redirections:**
238
238
  - "Is that not happening?" - You assumed without verifying
@@ -274,7 +274,7 @@ If systematic investigation reveals issue is truly environmental, timing-depende
274
274
  3. Implement appropriate handling (retry, timeout, error message)
275
275
  4. Add monitoring/logging for future investigation
276
276
 
277
- **But:** 95% of "no root cause" cases are incomplete investigation.
277
+ **But:** most "no root cause" cases are incomplete investigation.
278
278
 
279
279
  ## Supporting Techniques
280
280
 
@@ -288,11 +288,3 @@ These techniques are part of systematic debugging and available in this director
288
288
  - **verification-before-completion** - Verify fix worked before claiming success
289
289
  - **debug** - The Summer-specific triage loop (script errors → console → debugger → probe)
290
290
  - **playtesting-a-feature** - When the fix is a gameplay behaviour that has to be walked
291
-
292
- ## Real-World Impact
293
-
294
- From debugging sessions:
295
- - Systematic approach: 15-30 minutes to fix
296
- - Random fixes approach: 2-3 hours of thrashing
297
- - First-time fix rate: 95% vs 40%
298
- - New bugs introduced: Near zero vs common
@@ -79,8 +79,6 @@ async function waitFor<T>(
79
79
  }
80
80
  ```
81
81
 
82
- See `condition-based-waiting-example.ts` in this directory for complete implementation with domain-specific helpers (`waitForEvent`, `waitForEventCount`, `waitForEventMatch`) from actual debugging session.
83
-
84
82
  ## Common Mistakes
85
83
 
86
84
  **Bad: Polling too fast:** `setTimeout(check, 1)` - wastes CPU
@@ -105,11 +103,3 @@ await new Promise(r => setTimeout(r, 200)); // Then: wait for timed behavior
105
103
  1. First wait for triggering condition
106
104
  2. Based on known timing (not guessing)
107
105
  3. Comment explaining WHY
108
-
109
- ## Real-World Impact
110
-
111
- From debugging session (2025-10-03):
112
- - Fixed 15 flaky tests across 3 files
113
- - Pass rate: 60% → 100%
114
- - Execution time: 40% faster
115
- - No more race conditions
@@ -153,11 +153,3 @@ digraph principle {
153
153
  **Before operation:** Log before the dangerous operation, not after it fails
154
154
  **Include context:** Directory, cwd, environment variables, timestamps
155
155
  **Capture stack:** `new Error().stack` shows complete call chain
156
-
157
- ## Real-World Impact
158
-
159
- From debugging session (2025-10-03):
160
- - Found root cause through 5-level trace
161
- - Fixed at source (getter validation)
162
- - Added 4 layers of defense
163
- - 1847 tests passed, zero pollution