@superblocksteam/vite-plugin-file-sync 2.0.165-next.0 → 2.0.166-next.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 (218) hide show
  1. package/dist/ai-service/agent/prompts/api-prompts.d.ts.map +1 -1
  2. package/dist/ai-service/agent/prompts/api-prompts.js +3 -1
  3. package/dist/ai-service/agent/prompts/api-prompts.js.map +1 -1
  4. package/dist/ai-service/agent/prompts/build-base-system-prompt.d.ts.map +1 -1
  5. package/dist/ai-service/agent/prompts/build-base-system-prompt.js +43 -7
  6. package/dist/ai-service/agent/prompts/build-base-system-prompt.js.map +1 -1
  7. package/dist/ai-service/agent/tool-result-metrics.d.ts +12 -0
  8. package/dist/ai-service/agent/tool-result-metrics.d.ts.map +1 -1
  9. package/dist/ai-service/agent/tool-result-metrics.js +29 -0
  10. package/dist/ai-service/agent/tool-result-metrics.js.map +1 -1
  11. package/dist/ai-service/agent/tools/apis/attachment-file-inputs.d.ts +17 -0
  12. package/dist/ai-service/agent/tools/apis/attachment-file-inputs.d.ts.map +1 -0
  13. package/dist/ai-service/agent/tools/apis/attachment-file-inputs.js +208 -0
  14. package/dist/ai-service/agent/tools/apis/attachment-file-inputs.js.map +1 -0
  15. package/dist/ai-service/agent/tools/apis/get-sdk-api-docs.d.ts +6 -1
  16. package/dist/ai-service/agent/tools/apis/get-sdk-api-docs.d.ts.map +1 -1
  17. package/dist/ai-service/agent/tools/apis/get-sdk-api-docs.js +53 -19
  18. package/dist/ai-service/agent/tools/apis/get-sdk-api-docs.js.map +1 -1
  19. package/dist/ai-service/agent/tools/apis/sdk-root-documentation.d.ts +20 -0
  20. package/dist/ai-service/agent/tools/apis/sdk-root-documentation.d.ts.map +1 -0
  21. package/dist/ai-service/agent/tools/apis/sdk-root-documentation.js +284 -0
  22. package/dist/ai-service/agent/tools/apis/sdk-root-documentation.js.map +1 -0
  23. package/dist/ai-service/agent/tools/apis/spreadsheet-csv.d.ts +77 -0
  24. package/dist/ai-service/agent/tools/apis/spreadsheet-csv.d.ts.map +1 -0
  25. package/dist/ai-service/agent/tools/apis/spreadsheet-csv.js +627 -0
  26. package/dist/ai-service/agent/tools/apis/spreadsheet-csv.js.map +1 -0
  27. package/dist/ai-service/agent/tools/apis/test-api.d.ts +1 -0
  28. package/dist/ai-service/agent/tools/apis/test-api.d.ts.map +1 -1
  29. package/dist/ai-service/agent/tools/apis/test-api.js +51 -9
  30. package/dist/ai-service/agent/tools/apis/test-api.js.map +1 -1
  31. package/dist/ai-service/agent/tools/app-environment/update-app-environment.d.ts +1 -1
  32. package/dist/ai-service/agent/tools/build-manage-checklist.d.ts +1 -1
  33. package/dist/ai-service/agent/tools/build-read-file.d.ts +50 -6
  34. package/dist/ai-service/agent/tools/build-read-file.d.ts.map +1 -1
  35. package/dist/ai-service/agent/tools/build-read-file.js +153 -40
  36. package/dist/ai-service/agent/tools/build-read-file.js.map +1 -1
  37. package/dist/ai-service/agent/tools/get-logs.d.ts +2 -2
  38. package/dist/ai-service/agent/tools/index.d.ts +3 -2
  39. package/dist/ai-service/agent/tools/index.d.ts.map +1 -1
  40. package/dist/ai-service/agent/tools/index.js +3 -2
  41. package/dist/ai-service/agent/tools/index.js.map +1 -1
  42. package/dist/ai-service/agent/tools/integrations/execute-request.d.ts +15 -15
  43. package/dist/ai-service/agent/tools/read-state-trackers.d.ts +33 -0
  44. package/dist/ai-service/agent/tools/read-state-trackers.d.ts.map +1 -0
  45. package/dist/ai-service/agent/tools/read-state-trackers.js +67 -0
  46. package/dist/ai-service/agent/tools/read-state-trackers.js.map +1 -0
  47. package/dist/ai-service/agent/tools/report-security-findings.d.ts +6 -6
  48. package/dist/ai-service/agent/tools/report-security-findings.d.ts.map +1 -1
  49. package/dist/ai-service/agent/tools/report-security-findings.js +4 -2
  50. package/dist/ai-service/agent/tools/report-security-findings.js.map +1 -1
  51. package/dist/ai-service/agent/tools/subagent-isolation.d.ts +4 -15
  52. package/dist/ai-service/agent/tools/subagent-isolation.d.ts.map +1 -1
  53. package/dist/ai-service/agent/tools/subagent-isolation.js +4 -44
  54. package/dist/ai-service/agent/tools/subagent-isolation.js.map +1 -1
  55. package/dist/ai-service/agent/tools.d.ts.map +1 -1
  56. package/dist/ai-service/agent/tools.js +17 -64
  57. package/dist/ai-service/agent/tools.js.map +1 -1
  58. package/dist/ai-service/agent/tools2/registry.d.ts +1 -0
  59. package/dist/ai-service/agent/tools2/registry.d.ts.map +1 -1
  60. package/dist/ai-service/agent/tools2/registry.js +23 -5
  61. package/dist/ai-service/agent/tools2/registry.js.map +1 -1
  62. package/dist/ai-service/agent/tools2/tools/attachment-lease-resolver.d.ts +9 -0
  63. package/dist/ai-service/agent/tools2/tools/attachment-lease-resolver.d.ts.map +1 -0
  64. package/dist/ai-service/agent/tools2/tools/attachment-lease-resolver.js +52 -0
  65. package/dist/ai-service/agent/tools2/tools/attachment-lease-resolver.js.map +1 -0
  66. package/dist/ai-service/agent/tools2/tools/bash.d.ts +2 -2
  67. package/dist/ai-service/agent/tools2/tools/download-attachments.d.ts +1 -1
  68. package/dist/ai-service/agent/tools2/tools/download-attachments.d.ts.map +1 -1
  69. package/dist/ai-service/agent/tools2/tools/download-attachments.js +57 -49
  70. package/dist/ai-service/agent/tools2/tools/download-attachments.js.map +1 -1
  71. package/dist/ai-service/agent/tools2/tools/end-test-run.d.ts +3 -3
  72. package/dist/ai-service/agent/tools2/tools/git.d.ts +6 -6
  73. package/dist/ai-service/agent/tools2/tools/grep-metadata.d.ts +1 -1
  74. package/dist/ai-service/agent/tools2/tools/grep.d.ts +1 -1
  75. package/dist/ai-service/agent/tools2/tools/grep.js +1 -1
  76. package/dist/ai-service/agent/tools2/tools/list-attachments.d.ts +1 -1
  77. package/dist/ai-service/agent/tools2/tools/update-test-case-status.d.ts +1 -1
  78. package/dist/ai-service/agent/tools2/tools/web-fetch.d.ts +1 -1
  79. package/dist/ai-service/app-skills/helpers.d.ts +8 -2
  80. package/dist/ai-service/app-skills/helpers.d.ts.map +1 -1
  81. package/dist/ai-service/app-skills/helpers.js +184 -4
  82. package/dist/ai-service/app-skills/helpers.js.map +1 -1
  83. package/dist/ai-service/attachments/text-upload-artifacts.d.ts.map +1 -1
  84. package/dist/ai-service/attachments/text-upload-artifacts.js +1 -1
  85. package/dist/ai-service/attachments/text-upload-artifacts.js.map +1 -1
  86. package/dist/ai-service/attachments/upload-artifact-paths.d.ts +5 -0
  87. package/dist/ai-service/attachments/upload-artifact-paths.d.ts.map +1 -0
  88. package/dist/ai-service/attachments/upload-artifact-paths.js +18 -0
  89. package/dist/ai-service/attachments/upload-artifact-paths.js.map +1 -0
  90. package/dist/ai-service/chat/chat-session-store-metrics.d.ts +6 -0
  91. package/dist/ai-service/chat/chat-session-store-metrics.d.ts.map +1 -1
  92. package/dist/ai-service/chat/chat-session-store-metrics.js +23 -1
  93. package/dist/ai-service/chat/chat-session-store-metrics.js.map +1 -1
  94. package/dist/ai-service/chat/chat-session-store.d.ts +25 -1
  95. package/dist/ai-service/chat/chat-session-store.d.ts.map +1 -1
  96. package/dist/ai-service/chat/chat-session-store.js +70 -4
  97. package/dist/ai-service/chat/chat-session-store.js.map +1 -1
  98. package/dist/ai-service/edit-after-clark.d.ts +58 -0
  99. package/dist/ai-service/edit-after-clark.d.ts.map +1 -0
  100. package/dist/ai-service/edit-after-clark.js +143 -0
  101. package/dist/ai-service/edit-after-clark.js.map +1 -0
  102. package/dist/ai-service/features.d.ts +5 -0
  103. package/dist/ai-service/features.d.ts.map +1 -1
  104. package/dist/ai-service/features.js +5 -0
  105. package/dist/ai-service/features.js.map +1 -1
  106. package/dist/ai-service/index.d.ts +25 -3
  107. package/dist/ai-service/index.d.ts.map +1 -1
  108. package/dist/ai-service/index.js +123 -18
  109. package/dist/ai-service/index.js.map +1 -1
  110. package/dist/ai-service/judge/tools/playwright-action.d.ts +5 -5
  111. package/dist/ai-service/llm/context-v2/context.d.ts +23 -6
  112. package/dist/ai-service/llm/context-v2/context.d.ts.map +1 -1
  113. package/dist/ai-service/llm/context-v2/context.js +48 -13
  114. package/dist/ai-service/llm/context-v2/context.js.map +1 -1
  115. package/dist/ai-service/llm/context-v2/token-tracker.d.ts.map +1 -1
  116. package/dist/ai-service/llm/context-v2/token-tracker.js.map +1 -1
  117. package/dist/ai-service/llm/stream/abort-reason.d.ts +31 -0
  118. package/dist/ai-service/llm/stream/abort-reason.d.ts.map +1 -0
  119. package/dist/ai-service/llm/stream/abort-reason.js +36 -0
  120. package/dist/ai-service/llm/stream/abort-reason.js.map +1 -0
  121. package/dist/ai-service/llm/stream/orchestrator.d.ts +15 -0
  122. package/dist/ai-service/llm/stream/orchestrator.d.ts.map +1 -1
  123. package/dist/ai-service/llm/stream/orchestrator.js +88 -12
  124. package/dist/ai-service/llm/stream/orchestrator.js.map +1 -1
  125. package/dist/ai-service/migration-prompt-gating.d.ts +1 -0
  126. package/dist/ai-service/migration-prompt-gating.d.ts.map +1 -1
  127. package/dist/ai-service/migration-prompt-gating.js +6 -0
  128. package/dist/ai-service/migration-prompt-gating.js.map +1 -1
  129. package/dist/ai-service/skills/system/_registry.generated.d.ts.map +1 -1
  130. package/dist/ai-service/skills/system/_registry.generated.js +8 -0
  131. package/dist/ai-service/skills/system/_registry.generated.js.map +1 -1
  132. package/dist/ai-service/skills/system/resolve.d.ts.map +1 -1
  133. package/dist/ai-service/skills/system/resolve.js +29 -4
  134. package/dist/ai-service/skills/system/resolve.js.map +1 -1
  135. package/dist/ai-service/skills/system/spreadsheet-import/skill.generated.d.ts +2 -0
  136. package/dist/ai-service/skills/system/spreadsheet-import/skill.generated.d.ts.map +1 -0
  137. package/dist/ai-service/skills/system/spreadsheet-import/skill.generated.js +66 -0
  138. package/dist/ai-service/skills/system/spreadsheet-import/skill.generated.js.map +1 -0
  139. package/dist/ai-service/skills/system/superblocks-frontend/_variants/api-names-legacy.generated.d.ts +2 -0
  140. package/dist/ai-service/skills/system/superblocks-frontend/_variants/api-names-legacy.generated.d.ts.map +1 -0
  141. package/dist/ai-service/skills/system/superblocks-frontend/_variants/api-names-legacy.generated.js +5 -0
  142. package/dist/ai-service/skills/system/superblocks-frontend/_variants/api-names-legacy.generated.js.map +1 -0
  143. package/dist/ai-service/skills/system/superblocks-frontend/_variants/api-names-sdk.generated.d.ts +2 -0
  144. package/dist/ai-service/skills/system/superblocks-frontend/_variants/api-names-sdk.generated.d.ts.map +1 -0
  145. package/dist/ai-service/skills/system/superblocks-frontend/_variants/api-names-sdk.generated.js +5 -0
  146. package/dist/ai-service/skills/system/superblocks-frontend/_variants/api-names-sdk.generated.js.map +1 -0
  147. package/dist/ai-service/skills/system/superblocks-frontend/skill.generated.d.ts +1 -1
  148. package/dist/ai-service/skills/system/superblocks-frontend/skill.generated.d.ts.map +1 -1
  149. package/dist/ai-service/skills/system/superblocks-frontend/skill.generated.js +2 -1
  150. package/dist/ai-service/skills/system/superblocks-frontend/skill.generated.js.map +1 -1
  151. package/dist/ai-service/skills/system/superblocks-migration/skill.generated.d.ts +1 -1
  152. package/dist/ai-service/skills/system/superblocks-migration/skill.generated.d.ts.map +1 -1
  153. package/dist/ai-service/skills/system/superblocks-migration/skill.generated.js +6 -6
  154. package/dist/ai-service/skills/system/superblocks-sdk-api/skill.generated.d.ts +2 -0
  155. package/dist/ai-service/skills/system/superblocks-sdk-api/skill.generated.d.ts.map +1 -0
  156. package/dist/ai-service/skills/system/superblocks-sdk-api/skill.generated.js +29 -0
  157. package/dist/ai-service/skills/system/superblocks-sdk-api/skill.generated.js.map +1 -0
  158. package/dist/ai-service/skills/system/third-party-migration/skill.generated.d.ts +1 -1
  159. package/dist/ai-service/skills/system/third-party-migration/skill.generated.d.ts.map +1 -1
  160. package/dist/ai-service/skills/system/third-party-migration/skill.generated.js +2 -2
  161. package/dist/ai-service/state-machine/clark-fsm.d.ts +14 -1
  162. package/dist/ai-service/state-machine/clark-fsm.d.ts.map +1 -1
  163. package/dist/ai-service/state-machine/clark-fsm.js.map +1 -1
  164. package/dist/ai-service/state-machine/handlers/llm-generating.d.ts.map +1 -1
  165. package/dist/ai-service/state-machine/handlers/llm-generating.js +1 -0
  166. package/dist/ai-service/state-machine/handlers/llm-generating.js.map +1 -1
  167. package/dist/ai-service/state-machine/helpers/abandon-turn-metrics.d.ts +18 -0
  168. package/dist/ai-service/state-machine/helpers/abandon-turn-metrics.d.ts.map +1 -0
  169. package/dist/ai-service/state-machine/helpers/abandon-turn-metrics.js +66 -0
  170. package/dist/ai-service/state-machine/helpers/abandon-turn-metrics.js.map +1 -0
  171. package/dist/ai-service/state-machine/helpers/transition.d.ts.map +1 -1
  172. package/dist/ai-service/state-machine/helpers/transition.js.map +1 -1
  173. package/dist/ai-service/state-machine/traced-fsm.d.ts +12 -3
  174. package/dist/ai-service/state-machine/traced-fsm.d.ts.map +1 -1
  175. package/dist/ai-service/state-machine/traced-fsm.js +17 -5
  176. package/dist/ai-service/state-machine/traced-fsm.js.map +1 -1
  177. package/dist/ai-service/template-renderer.js +43 -7
  178. package/dist/ai-service/template-renderer.js.map +1 -1
  179. package/dist/ai-service/types.d.ts +2 -0
  180. package/dist/ai-service/types.d.ts.map +1 -1
  181. package/dist/ai-service/types.js.map +1 -1
  182. package/dist/file-sync-vite-plugin.d.ts +12 -3
  183. package/dist/file-sync-vite-plugin.d.ts.map +1 -1
  184. package/dist/file-sync-vite-plugin.js +57 -17
  185. package/dist/file-sync-vite-plugin.js.map +1 -1
  186. package/dist/migration/migration-routes.d.ts.map +1 -1
  187. package/dist/migration/migration-routes.js +42 -23
  188. package/dist/migration/migration-routes.js.map +1 -1
  189. package/dist/npm/normalize-workspace-protocol-for-npm.d.ts.map +1 -1
  190. package/dist/npm/normalize-workspace-protocol-for-npm.js +5 -5
  191. package/dist/npm/normalize-workspace-protocol-for-npm.js.map +1 -1
  192. package/dist/npm/rewrite-platform-workspace-deps.d.ts +2 -0
  193. package/dist/npm/rewrite-platform-workspace-deps.d.ts.map +1 -1
  194. package/dist/npm/rewrite-platform-workspace-deps.js +2 -2
  195. package/dist/npm/rewrite-platform-workspace-deps.js.map +1 -1
  196. package/dist/policy-gate-runner.d.ts.map +1 -1
  197. package/dist/policy-gate-runner.js +3 -0
  198. package/dist/policy-gate-runner.js.map +1 -1
  199. package/dist/socket-manager.d.ts +1 -1
  200. package/dist/socket-manager.d.ts.map +1 -1
  201. package/dist/socket-manager.js +64 -22
  202. package/dist/socket-manager.js.map +1 -1
  203. package/dist/sync-service/index.d.ts +14 -1
  204. package/dist/sync-service/index.d.ts.map +1 -1
  205. package/dist/sync-service/index.js +27 -1
  206. package/dist/sync-service/index.js.map +1 -1
  207. package/dist/sync-service/list-dir.d.ts.map +1 -1
  208. package/dist/sync-service/list-dir.js +15 -1
  209. package/dist/sync-service/list-dir.js.map +1 -1
  210. package/dist/sync-service/snapshot/take-snapshot.d.ts +6 -0
  211. package/dist/sync-service/snapshot/take-snapshot.d.ts.map +1 -1
  212. package/dist/sync-service/snapshot/take-snapshot.js +21 -0
  213. package/dist/sync-service/snapshot/take-snapshot.js.map +1 -1
  214. package/dist/vite-plugin-yaml-types.d.ts +6 -0
  215. package/dist/vite-plugin-yaml-types.d.ts.map +1 -1
  216. package/dist/vite-plugin-yaml-types.js +52 -61
  217. package/dist/vite-plugin-yaml-types.js.map +1 -1
  218. package/package.json +14 -11
@@ -0,0 +1,66 @@
1
+ // Auto-generated from src/ai-service/skills/system/spreadsheet-import/SKILL.md
2
+ // Do not edit directly - edit the .md file instead
3
+ export const content = `---
4
+ name: spreadsheet-import
5
+ description: |
6
+ Build an app from an attached Excel workbook, or import one into the App Database.
7
+ Load whenever an \`.xlsx\` attachment is part of the request — before designing a schema, before writing a migration, and before importing any rows.
8
+ Explains the structure digest the converter writes beside the CSVs, and how to decide which workbook columns become stored database columns and which become derived app behavior.
9
+ readOnly: true
10
+ metadata:
11
+ author: superblocks
12
+ version: "1.0"
13
+ ---
14
+
15
+ # Building an app from an Excel workbook
16
+
17
+ An \`.xlsx\` attachment never reaches you as a spreadsheet. Before \`downloadAttachments\` or \`testApi\` hands it over, the converter turns it into:
18
+
19
+ - one CSV per sheet that holds data, already trimmed to the table, and
20
+ - a \`*-structure.json\` digest describing every sheet in a few hundred tokens.
21
+
22
+ Generated SDK code must still parse CSV only. Never parse spreadsheet binary formats in sandbox code.
23
+
24
+ ## Design from the digest, not from the rows
25
+
26
+ Read the digest first. It is the thing to design from; the CSVs beside it are rows to load, not reading material. It is design evidence, not executable code.
27
+
28
+ Attaching a workbook is not by itself a request to load its rows. If the request was to inspect the file, design a schema, or build screens, do that and ask before importing. If the request was to build an app from the workbook, the data is part of it.
29
+
30
+ ## What the digest tells you
31
+
32
+ **\`role\`** — a \`data table\` has a \`csvFile\` to import. Most sheets are data tables, including ones with several calculated columns beside their typed-in values. A \`derived report\` is the narrower case where the sheet stores nothing of its own: every column beside its labels is worked out from other cells. It has no CSV, so build it as a page or view over the imported tables, never as another table.
33
+
34
+ **\`headerRowNumber\`, \`banner\`, \`notes\`, \`footerAggregates\`** — the CSVs are already trimmed to the table. A banner title, the rows above the header, a sentence written under the table, and a footer total are described here and excluded from the CSV, so load every CSV row as data and put the banner, the notes, and the totals in the UI instead.
35
+
36
+ **\`computed\` and \`coverage\`** — a column with \`computed\` states one rule for the whole column, and \`coverage\` says how many of its cells follow that rule (\`5000/5000\` is a per-row rule). Treat those as derived behavior, not stored columns. A column with only an \`example\` computes something different per row, so read it as report logic rather than a rule to repeat.
37
+
38
+ **\`readsFrom\`** — the sheets this sheet's formulas reference. Use it plus validated matching keys as relationship evidence. Do not invent foreign keys from column names alone.
39
+
40
+ **\`hidden\`** — a sheet or column the workbook hid from its own readers, so usually working data rather than part of the app. Ask before surfacing it instead of importing it as an ordinary table or field.
41
+
42
+ **\`type\` and \`format\`** — the workbook's own column types and number formats (\`yyyy-mm-dd\`, currency, \`0%\`). Use them for column types and UI display formats. A percentage is stored as a fraction, so \`0.15\` with format \`0%\` means 15 percent.
43
+
44
+ **\`errorCellCount\`** — cells whose formulas evaluate to an Excel error (\`#DIV/0!\`, \`#N/A\`) are blank in the CSV and counted here. Import them as empty and tell the user which columns were affected. Never substitute a guessed value.
45
+
46
+ A sheet whose only content is a column of sentences is a note to the reader, not a table. Say so and leave it out of the schema rather than importing the sentences as rows.
47
+
48
+ The workbook's colors, fonts, borders, and column widths are not reported and are not part of the app. Use the Superblocks design system.
49
+
50
+ ## Stored versus derived
51
+
52
+ Store constant and input columns in the App Database. Treat columns whose body cells are consistently formulas as derived behavior implemented in SQL queries, SDK APIs, or UI code instead of stored database columns.
53
+
54
+ A column that mixes formulas and constants stays stored unless the workbook clearly establishes one rule. Do not guess a formula for exceptional or manually overridden values.
55
+
56
+ Never evaluate workbook formulas, either yourself or in generated SDK code. Use cached formula results only to understand examples and to verify that the app's derived behavior matches the workbook.
57
+
58
+ If a formula cannot be represented safely in SQL, an SDK API, or UI code, import its cached result as a snapshot, keep it non-editable by default, and explain that it will not recalculate.
59
+
60
+ ## Order of work
61
+
62
+ When a workbook is attached to a sparse request, treat it as the app specification unless the user says otherwise, and infer useful tables and screens from sheet names, headers, values, and formulas.
63
+
64
+ After deciding stored versus derived fields: provision and migrate the schema, import only the stored fields, then build functional screens and APIs over the imported tables. Verify representative derived values against the cached workbook results before reporting completion.
65
+ `;
66
+ //# sourceMappingURL=skill.generated.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skill.generated.js","sourceRoot":"","sources":["../../../../../src/ai-service/skills/system/spreadsheet-import/skill.generated.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,mDAAmD;AAEnD,MAAM,CAAC,MAAM,OAAO,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA8DtB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare const content = "1. **MUST call API by exact name**: Format is `<ApiName>` matching folder `apis/<ApiName>/api.yaml`\n";
2
+ //# sourceMappingURL=api-names-legacy.generated.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api-names-legacy.generated.d.ts","sourceRoot":"","sources":["../../../../../../src/ai-service/skills/system/superblocks-frontend/_variants/api-names-legacy.generated.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,OAAO,0GACnB,CAAC"}
@@ -0,0 +1,5 @@
1
+ // Auto-generated from src/ai-service/skills/system/superblocks-frontend/_variants/api-names-legacy.md
2
+ // Do not edit directly - edit the .md file instead
3
+ export const content = `1. **MUST call API by exact name**: Format is \`<ApiName>\` matching folder \`apis/<ApiName>/api.yaml\`
4
+ `;
5
+ //# sourceMappingURL=api-names-legacy.generated.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api-names-legacy.generated.js","sourceRoot":"","sources":["../../../../../../src/ai-service/skills/system/superblocks-frontend/_variants/api-names-legacy.generated.ts"],"names":[],"mappings":"AAAA,sGAAsG;AACtG,mDAAmD;AAEnD,MAAM,CAAC,MAAM,OAAO,GAAG;CACtB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare const content = "1. **MUST call API by exact name**: Use a key on the default export in `server/apis/index.ts`, not the `name` field inside `api({ ... })` when they differ\n";
2
+ //# sourceMappingURL=api-names-sdk.generated.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api-names-sdk.generated.d.ts","sourceRoot":"","sources":["../../../../../../src/ai-service/skills/system/superblocks-frontend/_variants/api-names-sdk.generated.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,OAAO,iKACnB,CAAC"}
@@ -0,0 +1,5 @@
1
+ // Auto-generated from src/ai-service/skills/system/superblocks-frontend/_variants/api-names-sdk.md
2
+ // Do not edit directly - edit the .md file instead
3
+ export const content = `1. **MUST call API by exact name**: Use a key on the default export in \`server/apis/index.ts\`, not the \`name\` field inside \`api({ ... })\` when they differ
4
+ `;
5
+ //# sourceMappingURL=api-names-sdk.generated.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api-names-sdk.generated.js","sourceRoot":"","sources":["../../../../../../src/ai-service/skills/system/superblocks-frontend/_variants/api-names-sdk.generated.ts"],"names":[],"mappings":"AAAA,mGAAmG;AACnG,mDAAmD;AAEnD,MAAM,CAAC,MAAM,OAAO,GAAG;CACtB,CAAC"}
@@ -1,2 +1,2 @@
1
- export declare const content = "---\nname: superblocks-frontend\ndescription: |\n Build frontend UI using React, Tailwind CSS, and Superblocks components. Essential for connecting Superblocks UIs with APIs.\n Use when creating pages, components, handling user interactions, or working with the design system.\nreadOnly: true\nmetadata:\n author: superblocks\n version: \"1.0\"\n---\n\n# Superblocks Frontend Development\n\nThis skill covers building frontend UI for Superblocks applications using React, Tailwind CSS v4, and the Superblocks component library.\n\n## Platform Overview\n\nThis is a React-based web application platform. Use standard React patterns:\n\n- `useState`, `useEffect`, `useCallback`, `useMemo`\n- Event handlers and controlled components\n- JSX with Tailwind CSS classes\n\n## Using APIs from Frontend\n\n<!-- API_PRIMITIVE_IMPORT_GUIDANCE -->\n\n- **`useApiData`** \u2014 declarative reads with SWR caching (preferred for data loading)\n- **`useApi`** \u2014 imperative mutations (for event-driven actions)\n- **`executeApi`** \u2014 plain Promise outside React (utility functions, event handlers without hooks)\n\n**Data loading** \u2014 auto-fetches on mount and when inputs change (preferred for reads):\n\n```typescript\n// Auto-fetches when email or name changes. No useEffect or HMR guards needed.\n// IMPORTANT: Always use `fetching` to show a loading indicator during refetches.\nconst { data, loading, fetching, isError, error } = useApiData(\"GetUsers\", {\n email,\n name,\n});\n```\n\n**Mutations** \u2014 call `run()` manually (for event-driven actions like form submissions):\n\n```typescript\nimport { toast } from \"sonner\";\n\nconst { run: createOrder, loading } = useApi(\"CreateOrder\");\n\nconst handleSubmit = useCallback(async () => {\n try {\n await createOrder({ item, qty });\n } catch (error) {\n const message =\n error && typeof error === \"object\" && \"message\" in error\n ? String((error as { message: unknown }).message)\n : String(error);\n toast.error(\"Error creating order: \" + message);\n }\n}, [item, qty, createOrder]);\n```\n\n**Outside React** \u2014 call APIs as a plain Promise (utility functions, event handlers):\n\n```typescript\nconst result = await executeApi(\"GetUsers\", { email });\n```\n\n`useApiData` returns `{ data, loading, fetching, isError, isSuccess, isStale, error, status, fetchStatus, refetch, cancel }`. `data` persists across background refetches (stale-while-revalidate). `loading` is true on first fetch with no cached data. `fetching` is true during any fetch including background refetch. `status` is `\"pending\"` | `\"success\"` | `\"error\"`. `fetchStatus` is `\"idle\"` | `\"fetching\"`. `cancel()` is synchronous (returns `void`).\n\n`useApi` returns `{ run, cancel, reset, loading, data, error, status, variables }`. `data` persists across re-runs (shows previous result while loading). `error` holds the most recent failure, or `undefined` on success. `status` is `\"idle\"` | `\"pending\"` | `\"success\"` | `\"error\"`. `variables` holds the inputs from the most recent `run()`. `reset()` clears all state back to idle. HMR double-fetch prevention is built in \u2014 no `useRef` guard needed.\n\n### useApiData Options\n\n```typescript\nconst { data } = useApiData(\"GetUsers\", { email }, {\n enabled: true, // Skip fetching when false (conditional fetching)\n staleTime: 0, // Ms before cached data is considered stale (0 = always refetch)\n retry: 3, // Retry attempts on failure (false to disable)\n retryDelay: (n) => ..., // Custom delay strategy (default: exponential backoff, max 30s)\n refetchOnWindowFocus: false, // Refetch when tab regains focus\n refetchOnReconnect: true, // Refetch when network reconnects\n refetchInterval: false, // Polling interval in ms (false = disabled)\n structuralSharing: false, // Deep-compare to preserve object identity\n placeholderData: undefined, // Shown while the first fetch is in progress\n});\n```\n\n### Cache invalidation and optimistic updates\n\n**Same scope as the read**: After a mutation, prefer `refetch()` from the `useApiData` that loads the data (same component or wherever you can pass `refetch`).\n\n```typescript\nimport { toast } from \"sonner\";\n\nconst { data: orders, refetch } = useApiData(\"GetOrders\", { status });\nconst { run: createOrder } = useApi(\"CreateOrder\");\n\nconst handleSubmit = useCallback(async () => {\n try {\n await createOrder({ item, qty });\n await refetch();\n } catch (error) {\n const message =\n error && typeof error === \"object\" && \"message\" in error\n ? String((error as { message: unknown }).message)\n : String(error);\n toast.error(\"Error creating order: \" + message);\n }\n}, [item, qty, createOrder, refetch]);\n```\n\n**Cross-scope or broad invalidation**: Use `queryClient.invalidateQueries` when you cannot call that `refetch` (mutation in a different place than the `useApiData`, or you need every cached variant of an API to refetch). For **`queryClient.invalidateQueries`, `buildCacheKey`, `setQueryData`, and any other `queryClient` usage, always import `queryClient` from `@superblocksteam/library`**.\n\n```typescript\nimport { queryClient } from \"@superblocksteam/library\";\nimport { toast } from \"sonner\";\n\nconst { run: createOrder } = useApi(\"CreateOrder\");\n\nconst handleSubmit = useCallback(async () => {\n try {\n await createOrder({ item, qty });\n await queryClient.invalidateQueries(\"GetOrders\");\n } catch (error) {\n const message =\n error && typeof error === \"object\" && \"message\" in error\n ? String((error as { message: unknown }).message)\n : String(error);\n toast.error(\"Error creating order: \" + message);\n }\n}, [item, qty, createOrder]);\n```\n\n```typescript\n// Optimistic update: set cached data without refetching\nimport { queryClient } from \"@superblocksteam/library\";\n\nconst cacheKey = queryClient.buildCacheKey(\"GetOrders\", { status: \"active\" });\nqueryClient.setQueryData(cacheKey, (old) => ({\n ...old,\n orders: [...old.orders, newOrder],\n}));\n```\n\n### Critical API Rules\n\n1. **MUST call API by exact name**: Format is `<ApiName>` matching folder `apis/<ApiName>/api.yaml`\n2. **Use `useApiData` for data loading, `useApi` for mutations**: For data fetching, use `useApiData(\"GetUsers\", { email })`. Only use `useApi` for event-driven actions (button clicks, form submissions). Use `executeApi` for API calls outside React components.\n3. **Use the hook's state fields**: Do NOT create separate `useState` for loading or response data \u2014 the hooks manage this for you\n4. **ALWAYS check API interfaces**: NEVER guess the response structure\n5. **Include all parameters**: Even optional ones should be passed as `null`\n6. **ALWAYS use try/catch**: In imperative mode, APIs throw errors when they fail - wrap ALL `run()` calls in try/catch blocks. In declarative mode, check the `error` field for failure details.\n\n### File Handling\n\n**CRITICAL: Files must be wrapped in `{ files: [...] }` format:**\n\n```tsx\n// \u2705 CORRECT - Frontend: wrap files in { files: [...] }\nconst response = await runUploadApi({ userFile: { files: selectedFiles } });\n\n// \u274C WRONG - backend cannot process unwrapped files\nconst response = await runUploadApi({ userFile: selectedFiles });\n```\n\n## Platform Hooks and Functions\n\nAvailable hooks and functions from `@superblocksteam/library`:\n\n### User and Group Context\n\n```typescript\nimport {\n useSuperblocksUser,\n useSuperblocksGroups,\n} from \"@superblocksteam/library\";\n\n// Get current user info\nconst user = useSuperblocksUser();\n// user.name, user.email, user.id, user.groups, user.username, user.metadata\n\n// Get organization groups\nconst groups = useSuperblocksGroups();\n```\n\n### Environments and Data Tags\n\nConcepts & terminology:\n\n- `Data tags` are labels for different data segments, such as `Staging`, `Production`, or `us-east`.\n- `Profiles` is the legacy term for the same concept. If a user asks about profiles, treat that as data tags.\n- `Environments` are `Edit`, `Preview`, and `Production`, and each data tag is allowed in one or more of those environments.\n- Clark operates in `Edit` mode, so the data tags visible in app context represent the tags available in `Edit`; do not assume this is the same set that will be available in `Preview` or `Production`.\n- When implementing app code, prefer `useSuperblocksDataTags()`, `dataTags`, and `setDataTag()`.\n\nThe `DataTag` type has these fields (it is an alias for `Profile` from `@superblocksteam/shared`):\n\n```typescript\ntype DataTag = {\n id: string;\n key: string;\n displayName: string; // human-readable label \u2014 use this for display, NOT \"name\"\n description: string;\n type: \"RESERVED\" | \"CUSTOM\";\n};\n```\n\nRelevant hooks and functions:\n\n```typescript\nimport { useSuperblocksDataTags, getAppMode } from \"@superblocksteam/library\";\nimport type { DataTag, DataTags } from \"@superblocksteam/library\";\n\n// Manage data tags\nconst { dataTags, setDataTag } = useSuperblocksDataTags();\n// dataTags.available: DataTag[] \u2014 all tags in the current environment\n// dataTags.selected: DataTag | undefined \u2014 currently active tag (may be undefined)\n// dataTags.default: DataTag \u2014 the default tag\n// setDataTag(dataTag.key) \u2014 switch the active tag by key\n\n// Read the current app mode when reasoning about environment-specific behavior\nconst appMode = getAppMode();\n```\n\nExample \u2014 data tag switcher using a Select:\n\n```tsx\nconst { dataTags, setDataTag } = useSuperblocksDataTags();\n\n<Select value={dataTags?.selected?.key} onValueChange={setDataTag}>\n {dataTags?.available.map((tag) => (\n <SelectItem key={tag.key} value={tag.key}>\n {tag.displayName}\n </SelectItem>\n ))}\n</Select>;\n```\n\n### Embedded Applications\n\n**For embedded applications**, see the `references/embedding.md` file for `useEmbedProperties`, `useEmbedEvent`, and `useEmitEmbedEvent` hooks.\n\n## Logging Out of Integrations\n\nWhen building a \"Log out\" or \"Sign out\" button for apps that use OAuth-like integrations, use `logoutIntegrations` from the library to clear OAuth tokens:\n\n```typescript\nimport { logoutIntegrations } from \"@superblocksteam/library\";\n\nconst handleLogout = async () => {\n await logoutIntegrations();\n // Optionally redirect or show confirmation\n};\n```\n\n## Application Architecture\n\n### {{ui}}App.tsx Layout Structure\n\n**For single-page applications:**\n\n- Put all content in `{{ui}}pages/<pageName>/index.tsx`\n- Use components to compose and build the page\n- Keep `{{ui}}App.tsx` minimal with just `<AppProvider>` and `<Outlet />`\n\n**For multi-page applications:**\n\n- Put shared navigation/layout in `{{ui}}App.tsx` (sidebars, headers, footers)\n- Always include `<Outlet />` for page content\n- Always include `<AppProvider />` wrapper\n- Individual pages focus on content, not layout\n\n```tsx\n// \u2705 CORRECT - Multi-page {{ui}}App.tsx layout\n<AppProvider>\n <div className=\"flex flex-row size-screen\">\n {/* Sidebar - persistent across all pages */}\n <div className=\"flex bg-sidebar border-r w-[250px]\">\n <Navigation />\n </div>\n\n {/* Main content area */}\n <div className=\"flex flex-1 h-full flex-col\">\n {/* Header - persistent across all pages */}\n <div className=\"flex bg-header border-b h-[60px]\">\n <Header />\n </div>\n\n {/* Page content area */}\n <div className=\"flex p-4 flex-1 overflow-auto\">\n <Outlet />\n </div>\n </div>\n </div>\n</AppProvider>\n```\n\n**PROTECTED: NEVER remove `<AppProvider>` or `<Outlet />` from {{ui}}App.tsx!**\n\n### Page Structure\n\n```tsx\n// \u2705 CORRECT - Page focuses on content only\n<div className=\"flex flex-col gap-4 size-screen overflow-auto\">\n <h1 className=\"text-3xl font-bold\">Dashboard Content</h1>\n <Card>{/* Page-specific content */}</Card>\n</div>\n```\n\n**IMPORTANT**: The first div on the page must have `overflow-auto` so the user's page can scroll.\n\n## Routing\n\nUse `react-router@7` in data mode. Standard patterns apply:\n\n- `useNavigate()` for programmatic navigation\n- `useParams()` for route parameters\n- `useSearchParams()` for query strings\n\n**If you add new pages or rename files, you MUST update the router.**\n\nFile edits change the live app immediately, so keep every intermediate state valid:\n\n- Create a page before adding its router reference.\n- Remove router references before deleting a page.\n- To rename a page, create the new page, repoint the router, then delete the old page.\n- Wait for each dependent tool call to return without an error before starting the next step. Batch only independent tool calls.\n\nKeep route elements statically discoverable by the Superblocks editor:\n\n- Prefer direct JSX such as `element: <Page />`.\n- Keep code splitting with top-level `lazy(() => import(\"./pages/Page\"))`\n bindings.\n- Keep page routes as `children` of the `App` layout route, and put one\n `<Suspense>` boundary around `<Outlet />` inside `<AppProvider>` in `{{ui}}App.tsx`.\n- A single-argument wrapper such as `element: withSuspense(Page)` is also\n discoverable when `Page` is directly imported or bound by top-level\n `React.lazy`.\n- Do not unwrap working routes that use this shape.\n- Do not use nested or multi-argument calls such as\n `element: withSuspense(makePage(Home))` or\n `element: withSuspense(Home, opts)`.\n- Do not wrap page JSX in another element, such as\n `element: <PageErrorBoundary><Page /></PageErrorBoundary>`.\n\n```tsx\nimport { App as AppProvider } from \"@superblocksteam/library\";\nimport { Suspense, lazy } from \"react\";\nimport { Outlet, createBrowserRouter } from \"react-router\";\n\nconst Page = lazy(() => import(\"./pages/Page\"));\n\nconst router = createBrowserRouter([\n {\n Component: App,\n children: [{ path: \"/page\", element: <Page /> }],\n },\n]);\n\nfunction App() {\n return (\n <AppProvider className=\"h-full w-full\">\n <Suspense fallback={null}>\n <Outlet />\n </Suspense>\n </AppProvider>\n );\n}\n```\n\n### Page names\n\nA new app ships with one empty page at `{{ui}}pages/Home`, bound as the `/` index route:\n\n```tsx\n{ path: \"/\", index: true, lazy: () => import(\"./pages/Home/index.js\").then((mod) => ({ Component: mod.default })) }\n```\n\n- **Single-page app**: build the landing content in `{{ui}}pages/Home` and keep it named `Home` unless the user asks for something else\n- **Multi-page app**: every page you add beyond the starter gets a descriptive PascalCase name for what it shows (`Dashboard`, `Inventory`, `TopBuilders`), and the starter page keeps `Home` as the app's landing page \u2014 rename it too if the app's landing route is really something else (e.g. `Overview`), keeping it bound to the `/` index route\n- Page names are user-visible in the editor's page switcher, so name them the way you'd label a nav item\n- Never name a page `Page1`, `Page2`, ...\n\n## Design System (Tailwind CSS v4)\n\n**All design tokens are defined in `{{ui}}index.css`**. Apps come with a professional black-and-white theme by default.\n\n### Semantic Tokens Rule\n\n**ALWAYS use semantic tokens** \u2014 NEVER raw Tailwind utilities:\n\n```tsx\n// \u2705 CORRECT\n<Card className=\"bg-background text-foreground border border-border\" />\n\n// \u274C WRONG\n<Card className=\"bg-white text-black border-gray-200\" />\n```\n\n### When to Modify {{ui}}index.css\n\nOnly modify when:\n\n- User explicitly requests branding/theme changes\n- Replicating a specific brand look (e.g., Yelp, Instacart)\n- Migrating an attached third-party app \u2014 see `skills/system/third-party-migration/SKILL.md` (its theme-port rules override the Modification Rules below \u2014 copy source tokens as-is, do not convert to OKLCH or trim the palette)\n- Feature requests (lists, filters, CRUD) do NOT require changes\n\n**Modification Rules:**\n\n- All colors must be in OKLCH format\n- Use semantic names (`--color-warning`, `--shadow-elevated`)\n- Do not remove or rename existing tokens\n- Limit color palette to 5 colors max\n- Avoid gradients unless explicitly requested\n- Minimal font sizes (3 max: body, section heading, main heading)\n\n### Component Variants\n\nUse or create variants instead of one-off styles:\n\n```tsx\n// \u274C WRONG - Hacky inline overrides\n<Button className=\"text-white border-white hover:bg-white\" />\n\n// \u2705 CORRECT - Use a variant\n<Button variant=\"secondary\" />\n```\n\n## Icons\n\nUse icons from Lucide React library:\n\n```tsx\nimport { Icon } from \"@/components/ui/icon\";\n<Icon icon=\"heart\" />;\n\nimport { Button } from \"@/components/ui/button\";\n<Button>\n <Icon icon=\"plus\" /> Add Item\n</Button>;\n```\n\n**Always use icons rather than emojis unless explicitly requested.**\n\nUse current Lucide icon names, not stale aliases: use `house` instead of `home`, `life-buoy` instead of `help-circle` or `circle-help`, and `chart-column-big` instead of `bar-chart-3`.\n\nWhen storing icon names as data (arrays/objects), type them with `IconName`. **`IconName` is a TYPE-only export \u2014 you MUST import it with the `type` keyword.** A value import compiles but CRASHES the app at runtime (`does not provide an export named 'IconName'`) and leaves the preview blank, which blocks UI verification. If you hit that error, read `skills/system/common-import-issues/SKILL.md`.\n\n```tsx\n// \u2705 CORRECT \u2014 type-only import\nimport type { IconName } from \"lucide-react/dynamic\";\n\nconst navItems: { icon: IconName; label: string }[] = [\n { icon: \"house\", label: \"Home\" },\n { icon: \"settings\", label: \"Settings\" },\n];\n\n// \u274C WRONG \u2014 value import crashes the app at runtime and blanks the preview\nimport { IconName } from \"lucide-react/dynamic\";\n```\n\n## Custom Components\n\n**CRITICAL: Component composition is MANDATORY. DO NOT create monolithic pages.**\n\n### The Composition Rule\n\nWhen building ANY feature:\n\n1. **First**, identify reusable parts (list items, cards, forms, filters, headers)\n2. **Then**, create separate component files\n3. **Finally**, compose them together in the page\n\n### When to Create Components\n\n- **Rendering any list** - Extract to a component\n- **Building cards/complex UI** - Each card type is a component\n- **Creating forms** - Form sections are components\n- **Adding filters/headers** - These are components\n- **Page has >50 lines JSX** - Break it down\n\n### Component Boundary Rules\n\n**Inside a custom component:**\n\n- \u2705 Use React hooks (useState, useReducer, useEffect, etc.)\n- \u2705 Use local React state\n- \u2705 Use internal helper components\n- \u2705 Use other registered components\n- \u274C CANNOT call APIs - must pass data into the component\n\n### Example: Proper Component Structure\n\n```tsx\n// {{ui}}components/ProductCard/index.tsx - Extract to component\nimport { Card } from \"@/components/ui/card\";\nimport { Button } from \"@/components/ui/button\";\n\ntype ProductCardProps = {\n product: {\n id: string;\n name: string;\n image: string;\n };\n};\n\nexport default function ProductCard(props: ProductCardProps) {\n return (\n <Card>\n <img src={props.product.image} />\n <h3 className=\"text-lg font-semibold\">{props.product.name}</h3>\n <Button>Add</Button>\n </Card>\n );\n}\n\n// {{ui}}pages/Products/index.tsx - Clean composition\nimport ProductCard from \"@/components/ProductCard\";\n\nconst ProductsPage = () => {\n const [products, setProducts] = useState([]);\n\n return (\n <div className=\"grid grid-cols-3 gap-4\">\n {products.map((p) => (\n <ProductCard key={p.id} product={p} />\n ))}\n </div>\n );\n};\n```\n\n## Visual Excellence\n\n**Prioritize visual excellence from the start:**\n\n1. **Design System Enhancement**: Start by enhancing `{{ui}}index.css` with app-specific colors that match the target aesthetic\n2. **Professional Layout Architecture**: Use sophisticated layouts with proper spacing, responsive design\n3. **Rich Interactive Components**: Leverage advanced components with proper variants and states\n4. **Visual Polish**: Add shadows, smooth transitions, skeleton loaders, hover effects, typography hierarchy\n5. **Real-World UI Patterns**: Create layouts that feel like professional applications\n\n**Make applications that look and feel like real, polished products - not basic wireframes.**\n\n### Loading States\n\nLoading behavior must differ based on whether data has already been fetched:\n\n**Initial load (no data yet):** Show skeleton shimmers that mirror the shape of the final content. Never show a blank screen or a generic spinner.\n\n**Refetch / background refresh (data already exists):** Keep showing the existing data. Use `fetching` from `useApiData` to apply a subtle visual indicator: light opacity (e.g. `opacity-70`) to signal a refresh is in progress, plus a non-blocking label such as a small inline spinner, a thin progress bar, or an \"Updating\u2026\" label. **Do not** use `pointer-events-none` or otherwise disable the content \u2014 it must remain fully interactive during refetch.\n\nUse `loading` and `fetching` from `useApiData` to distinguish these states:\n\n```tsx\nconst { data, loading, fetching, isError, error } = useApiData(\"GetOrders\", {\n status: statusFilter,\n search,\n});\n\n// Initial load \u2014 no data yet \u2192 show skeleton placeholder\nif (loading) {\n return <OrderTableSkeleton />;\n}\n\nif (isError) return <ErrorBanner error={error} />;\n\n// Refetch \u2014 data exists \u2192 subtle opacity + indicator, table stays interactive\nreturn (\n <div>\n {fetching && <div className=\"text-xs text-muted-foreground\">Updating\u2026</div>}\n <div className={fetching ? \"opacity-70\" : \"\"}>\n <OrderTable orders={data.orders} />\n </div>\n </div>\n);\n```\n\n```tsx\n// \u274C WRONG \u2014 pointer-events-none disables the table, making it feel broken\nreturn (\n <div className={fetching ? \"pointer-events-none\" : \"\"}>\n <OrderTable orders={data.orders} />\n </div>\n);\n\n// \u274C WRONG \u2014 no loading feedback when filters change\nif (loading) return <Skeleton />;\nreturn <OrderTable orders={data.orders} />;\n```\n\n#### Table Loading Rules\n\n- **Do not** use `pointer-events-none` on a table during loading \u2014 this disables interaction and makes the UI feel broken. During refetch, apply a subtle opacity (e.g. `opacity-70`) plus a non-blocking indicator (e.g., an \"Updating\u2026\" label or a thin progress bar). The table must remain clickable, sortable, and scrollable.\n- **Do not** replace a populated table with a full skeleton on refetch \u2014 this causes disorienting content flashes.\n- **Skeleton tables are only for initial load** (`loading` is true) when there is no data to display yet. Build them to match the real table's column structure (header + a few placeholder rows).\n\n### Efficient Loading Patterns\n\n1. **Always show loading indicators on refetch**: When inputs change (e.g. filters, search), show a non-blocking visual indicator while new data loads. Use `fetching` from `useApiData`.\n2. **Loading State Hierarchy**:\n - No data yet (`loading`) \u2192 Full skeleton placeholder\n - Has data, refetching (`fetching` && !`loading`) \u2192 Keep showing current data with a subtle visual indicator: light opacity (e.g. `opacity-70`) plus a non-blocking label (e.g. \"Updating\u2026\" text, thin progress bar, inline spinner). Do not use `pointer-events-none` \u2014 the content must remain fully interactive.\n - Error state (`isError`) \u2192 Show error with retry option, optionally keep stale data visible\n3. **Debounce Rapid Requests**: Prevent multiple API calls in short succession\n4. **Use useApiData for automatic refetching**: `useApiData` auto-refetches when inputs change and supports `staleTime`, `refetchOnWindowFocus`, `refetchOnReconnect`, `refetchInterval`, and `retry` options.\n\n## Performance Rules\n\n### 1. ALWAYS Paginate Tables and Lists\n\n**NEVER render more than 50 rows without pagination.** Always add client-side pagination:\n\n```tsx\nfunction PaginatedTable({ data }: { data: any[] }) {\n const PAGE_SIZE = 20;\n const [page, setPage] = useState(0);\n const totalPages = Math.ceil(data.length / PAGE_SIZE);\n const pageData = useMemo(\n () => data.slice(page * PAGE_SIZE, (page + 1) * PAGE_SIZE),\n [data, page],\n );\n\n useEffect(() => {\n setPage(0);\n }, [data.length]);\n\n return (\n <>\n <Table>{/* render pageData rows */}</Table>\n <span>\n Page {page + 1} of {totalPages} ({data.length} total rows)\n </span>\n <Button\n onClick={() => setPage((p) => Math.max(0, p - 1))}\n disabled={page === 0}\n >\n Previous\n </Button>\n <Button\n onClick={() => setPage((p) => Math.min(totalPages - 1, p + 1))}\n disabled={page >= totalPages - 1}\n >\n Next\n </Button>\n </>\n );\n}\n```\n\nFor 200+ rows, prefer **server-side pagination**; use cursor/keyset pagination for high offsets instead of `LIMIT`/`OFFSET`.\n\nFor 500+ item lists where pagination doesn't fit the UX (chat messages, infinite scroll feeds, large dropdowns), use **virtualization** (`react-virtuoso` or `@tanstack/react-virtual`) to render only the items visible in the viewport.\n\n### 2. ALWAYS Debounce Input-Driven API Calls\n\n**NEVER call an API directly from onChange.** Debounce search/filter inputs with a 300ms timer:\n\n```tsx\nfunction DebouncedSearch({ onSearch }: { onSearch: (query: string) => void }) {\n const [localValue, setLocalValue] = useState(\"\");\n const timerRef = useRef<ReturnType<typeof setTimeout>>();\n const handleChange = useCallback(\n (e: React.ChangeEvent<HTMLInputElement>) => {\n setLocalValue(e.target.value);\n clearTimeout(timerRef.current);\n timerRef.current = setTimeout(() => onSearch(e.target.value), 300);\n },\n [onSearch],\n );\n useEffect(() => () => clearTimeout(timerRef.current), []);\n\n return (\n <Input value={localValue} onChange={handleChange} placeholder=\"Search...\" />\n );\n}\n```\n\n### 3. Memoize Expensive Renders\n\nUse memoization (`memo()`, `useMemo`, `useCallback`) when it prevents measurable re-renders or expensive recomputation:\n\n```tsx\nconst OrderRow = memo(function OrderRow({\n order,\n onSelect,\n}: {\n order: Order;\n onSelect: (id: string) => void;\n}) {\n return (\n <TableRow onClick={() => onSelect(order.id)}>\n <TableCell>{order.id}</TableCell>...\n </TableRow>\n );\n});\n\nconst handleSelect = useCallback((id: string) => {\n setSelectedId(id);\n}, []);\n\nconst filtered = useMemo(\n () =>\n orders.filter((o) => o.status === status).sort((a, b) => b.total - a.total),\n [orders, status],\n);\n```\n\n### 4. ALWAYS Clean Up Side Effects\n\nClean up timers, event listeners, and subscriptions in `useEffect` return functions:\n\n```tsx\nuseEffect(() => {\n const handler = (e: KeyboardEvent) => {\n /* ... */\n };\n window.addEventListener(\"keydown\", handler);\n return () => window.removeEventListener(\"keydown\", handler);\n}, []);\n```\n\n### 5. Cancel In-Flight API Requests on Unmount\n\nFor search/filter patterns, prefer `useApiData` \u2014 it handles cancellation and cleanup automatically:\n\n```tsx\n// useApiData: auto-fetches and cancels on unmount/input change\nconst { data, fetching } = useApiData(\"SearchProducts\", { query });\n```\n\nFor imperative usage with manual cleanup, use the `cancel()` function:\n\n```tsx\nconst { run, cancel } = useApi(\"SearchProducts\");\n\nuseEffect(() => {\n if (!query) return;\n run({ query }).catch(console.error);\n return () => {\n cancel().catch(() => {});\n };\n}, [query, run, cancel]);\n```\n";
1
+ export declare const content = "---\nname: superblocks-frontend\ndescription: |\n Build frontend UI using React, Tailwind CSS, and Superblocks components. Essential for connecting Superblocks UIs with APIs.\n Use when creating pages, components, handling user interactions, or working with the design system.\nreadOnly: true\nmetadata:\n author: superblocks\n version: \"1.0\"\n---\n\n# Superblocks Frontend Development\n\nThis skill covers building frontend UI for Superblocks applications using React, Tailwind CSS v4, and the Superblocks component library.\n\n## Platform Overview\n\nThis is a React-based web application platform. Use standard React patterns:\n\n- `useState`, `useEffect`, `useCallback`, `useMemo`\n- Event handlers and controlled components\n- JSX with Tailwind CSS classes\n\n## Using APIs from Frontend\n\n<!-- API_PRIMITIVE_IMPORT_GUIDANCE -->\n\n- **`useApiData`** \u2014 declarative reads with SWR caching (preferred for data loading)\n- **`useApi`** \u2014 imperative mutations (for event-driven actions)\n- **`executeApi`** \u2014 plain Promise outside React (utility functions, event handlers without hooks)\n\n**Data loading** \u2014 auto-fetches on mount and when inputs change (preferred for reads):\n\n```typescript\n// Auto-fetches when email or name changes. No useEffect or HMR guards needed.\n// IMPORTANT: Always use `fetching` to show a loading indicator during refetches.\nconst { data, loading, fetching, isError, error } = useApiData(\"GetUsers\", {\n email,\n name,\n});\n```\n\n**Mutations** \u2014 call `run()` manually (for event-driven actions like form submissions):\n\n```typescript\nimport { toast } from \"sonner\";\n\nconst { run: createOrder, loading } = useApi(\"CreateOrder\");\n\nconst handleSubmit = useCallback(async () => {\n try {\n await createOrder({ item, qty });\n } catch (error) {\n const message =\n error && typeof error === \"object\" && \"message\" in error\n ? String((error as { message: unknown }).message)\n : String(error);\n toast.error(\"Error creating order: \" + message);\n }\n}, [item, qty, createOrder]);\n```\n\n**Outside React** \u2014 call APIs as a plain Promise (utility functions, event handlers):\n\n```typescript\nconst result = await executeApi(\"GetUsers\", { email });\n```\n\n`useApiData` returns `{ data, loading, fetching, isError, isSuccess, isStale, error, status, fetchStatus, refetch, cancel }`. `data` persists across background refetches (stale-while-revalidate). `loading` is true on first fetch with no cached data. `fetching` is true during any fetch including background refetch. `status` is `\"pending\"` | `\"success\"` | `\"error\"`. `fetchStatus` is `\"idle\"` | `\"fetching\"`. `cancel()` is synchronous (returns `void`).\n\n`useApi` returns `{ run, cancel, reset, loading, data, error, status, variables }`. `data` persists across re-runs (shows previous result while loading). `error` holds the most recent failure, or `undefined` on success. `status` is `\"idle\"` | `\"pending\"` | `\"success\"` | `\"error\"`. `variables` holds the inputs from the most recent `run()`. `reset()` clears all state back to idle. HMR double-fetch prevention is built in \u2014 no `useRef` guard needed.\n\n### useApiData Options\n\n```typescript\nconst { data } = useApiData(\"GetUsers\", { email }, {\n enabled: true, // Skip fetching when false (conditional fetching)\n staleTime: 0, // Ms before cached data is considered stale (0 = always refetch)\n retry: 3, // Retry attempts on failure (false to disable)\n retryDelay: (n) => ..., // Custom delay strategy (default: exponential backoff, max 30s)\n refetchOnWindowFocus: false, // Refetch when tab regains focus\n refetchOnReconnect: true, // Refetch when network reconnects\n refetchInterval: false, // Polling interval in ms (false = disabled)\n structuralSharing: false, // Deep-compare to preserve object identity\n placeholderData: undefined, // Shown while the first fetch is in progress\n});\n```\n\n### Cache invalidation and optimistic updates\n\n**Same scope as the read**: After a mutation, prefer `refetch()` from the `useApiData` that loads the data (same component or wherever you can pass `refetch`).\n\n```typescript\nimport { toast } from \"sonner\";\n\nconst { data: orders, refetch } = useApiData(\"GetOrders\", { status });\nconst { run: createOrder } = useApi(\"CreateOrder\");\n\nconst handleSubmit = useCallback(async () => {\n try {\n await createOrder({ item, qty });\n await refetch();\n } catch (error) {\n const message =\n error && typeof error === \"object\" && \"message\" in error\n ? String((error as { message: unknown }).message)\n : String(error);\n toast.error(\"Error creating order: \" + message);\n }\n}, [item, qty, createOrder, refetch]);\n```\n\n**Cross-scope or broad invalidation**: Use `queryClient.invalidateQueries` when you cannot call that `refetch` (mutation in a different place than the `useApiData`, or you need every cached variant of an API to refetch). For **`queryClient.invalidateQueries`, `buildCacheKey`, `setQueryData`, and any other `queryClient` usage, always import `queryClient` from `@superblocksteam/library`**.\n\n```typescript\nimport { queryClient } from \"@superblocksteam/library\";\nimport { toast } from \"sonner\";\n\nconst { run: createOrder } = useApi(\"CreateOrder\");\n\nconst handleSubmit = useCallback(async () => {\n try {\n await createOrder({ item, qty });\n await queryClient.invalidateQueries(\"GetOrders\");\n } catch (error) {\n const message =\n error && typeof error === \"object\" && \"message\" in error\n ? String((error as { message: unknown }).message)\n : String(error);\n toast.error(\"Error creating order: \" + message);\n }\n}, [item, qty, createOrder]);\n```\n\n```typescript\n// Optimistic update: set cached data without refetching\nimport { queryClient } from \"@superblocksteam/library\";\n\nconst cacheKey = queryClient.buildCacheKey(\"GetOrders\", { status: \"active\" });\nqueryClient.setQueryData(cacheKey, (old) => ({\n ...old,\n orders: [...old.orders, newOrder],\n}));\n```\n\n### Critical API Rules\n\n<!-- API_NAME_GUIDANCE -->\n\n2. **Use `useApiData` for data loading, `useApi` for mutations**: For data fetching, use `useApiData(\"GetUsers\", { email })`. Only use `useApi` for event-driven actions (button clicks, form submissions). Use `executeApi` for API calls outside React components.\n3. **Use the hook's state fields**: Do NOT create separate `useState` for loading or response data \u2014 the hooks manage this for you\n4. **ALWAYS check API interfaces**: NEVER guess the response structure\n5. **Include all parameters**: Even optional ones should be passed as `null`\n6. **ALWAYS use try/catch**: In imperative mode, APIs throw errors when they fail - wrap ALL `run()` calls in try/catch blocks. In declarative mode, check the `error` field for failure details.\n\n### File Handling\n\n**CRITICAL: Files must be wrapped in `{ files: [...] }` format:**\n\n```tsx\n// \u2705 CORRECT - Frontend: wrap files in { files: [...] }\nconst response = await runUploadApi({ userFile: { files: selectedFiles } });\n\n// \u274C WRONG - backend cannot process unwrapped files\nconst response = await runUploadApi({ userFile: selectedFiles });\n```\n\n## Platform Hooks and Functions\n\nAvailable hooks and functions from `@superblocksteam/library`:\n\n### User and Group Context\n\n```typescript\nimport {\n useSuperblocksUser,\n useSuperblocksGroups,\n} from \"@superblocksteam/library\";\n\n// Get current user info\nconst user = useSuperblocksUser();\n// user.name, user.email, user.id, user.groups, user.username, user.metadata\n\n// Get organization groups\nconst groups = useSuperblocksGroups();\n```\n\n### Environments and Data Tags\n\nConcepts & terminology:\n\n- `Data tags` are labels for different data segments, such as `Staging`, `Production`, or `us-east`.\n- `Profiles` is the legacy term for the same concept. If a user asks about profiles, treat that as data tags.\n- `Environments` are `Edit`, `Preview`, and `Production`, and each data tag is allowed in one or more of those environments.\n- Clark operates in `Edit` mode, so the data tags visible in app context represent the tags available in `Edit`; do not assume this is the same set that will be available in `Preview` or `Production`.\n- When implementing app code, prefer `useSuperblocksDataTags()`, `dataTags`, and `setDataTag()`.\n\nThe `DataTag` type has these fields (it is an alias for `Profile` from `@superblocksteam/shared`):\n\n```typescript\ntype DataTag = {\n id: string;\n key: string;\n displayName: string; // human-readable label \u2014 use this for display, NOT \"name\"\n description: string;\n type: \"RESERVED\" | \"CUSTOM\";\n};\n```\n\nRelevant hooks and functions:\n\n```typescript\nimport { useSuperblocksDataTags, getAppMode } from \"@superblocksteam/library\";\nimport type { DataTag, DataTags } from \"@superblocksteam/library\";\n\n// Manage data tags\nconst { dataTags, setDataTag } = useSuperblocksDataTags();\n// dataTags.available: DataTag[] \u2014 all tags in the current environment\n// dataTags.selected: DataTag | undefined \u2014 currently active tag (may be undefined)\n// dataTags.default: DataTag \u2014 the default tag\n// setDataTag(dataTag.key) \u2014 switch the active tag by key\n\n// Read the current app mode when reasoning about environment-specific behavior\nconst appMode = getAppMode();\n```\n\nExample \u2014 data tag switcher using a Select:\n\n```tsx\nconst { dataTags, setDataTag } = useSuperblocksDataTags();\n\n<Select value={dataTags?.selected?.key} onValueChange={setDataTag}>\n {dataTags?.available.map((tag) => (\n <SelectItem key={tag.key} value={tag.key}>\n {tag.displayName}\n </SelectItem>\n ))}\n</Select>;\n```\n\n### Embedded Applications\n\n**For embedded applications**, see the `references/embedding.md` file for `useEmbedProperties`, `useEmbedEvent`, and `useEmitEmbedEvent` hooks.\n\n## Logging Out of Integrations\n\nWhen building a \"Log out\" or \"Sign out\" button for apps that use OAuth-like integrations, use `logoutIntegrations` from the library to clear OAuth tokens:\n\n```typescript\nimport { logoutIntegrations } from \"@superblocksteam/library\";\n\nconst handleLogout = async () => {\n await logoutIntegrations();\n // Optionally redirect or show confirmation\n};\n```\n\n## Application Architecture\n\n### {{ui}}App.tsx Layout Structure\n\n**For single-page applications:**\n\n- Put all content in `{{ui}}pages/<pageName>/index.tsx`\n- Use components to compose and build the page\n- Keep `{{ui}}App.tsx` minimal with just `<AppProvider>` and `<Outlet />`\n\n**For multi-page applications:**\n\n- Put shared navigation/layout in `{{ui}}App.tsx` (sidebars, headers, footers)\n- Always include `<Outlet />` for page content\n- Always include `<AppProvider />` wrapper\n- Individual pages focus on content, not layout\n\n```tsx\n// \u2705 CORRECT - Multi-page {{ui}}App.tsx layout\n<AppProvider>\n <div className=\"flex flex-row size-screen\">\n {/* Sidebar - persistent across all pages */}\n <div className=\"flex bg-sidebar border-r w-[250px]\">\n <Navigation />\n </div>\n\n {/* Main content area */}\n <div className=\"flex flex-1 h-full flex-col\">\n {/* Header - persistent across all pages */}\n <div className=\"flex bg-header border-b h-[60px]\">\n <Header />\n </div>\n\n {/* Page content area */}\n <div className=\"flex p-4 flex-1 overflow-auto\">\n <Outlet />\n </div>\n </div>\n </div>\n</AppProvider>\n```\n\n**PROTECTED: NEVER remove `<AppProvider>` or `<Outlet />` from {{ui}}App.tsx!**\n\n### Page Structure\n\n```tsx\n// \u2705 CORRECT - Page focuses on content only\n<div className=\"flex flex-col gap-4 size-screen overflow-auto\">\n <h1 className=\"text-3xl font-bold\">Dashboard Content</h1>\n <Card>{/* Page-specific content */}</Card>\n</div>\n```\n\n**IMPORTANT**: The first div on the page must have `overflow-auto` so the user's page can scroll.\n\n## Routing\n\nUse `react-router@7` in data mode. Standard patterns apply:\n\n- `useNavigate()` for programmatic navigation\n- `useParams()` for route parameters\n- `useSearchParams()` for query strings\n\n**If you add new pages or rename files, you MUST update the router.**\n\nFile edits change the live app immediately, so keep every intermediate state valid:\n\n- Create a page before adding its router reference.\n- Remove router references before deleting a page.\n- To rename a page, create the new page, repoint the router, then delete the old page.\n- Wait for each dependent tool call to return without an error before starting the next step. Batch only independent tool calls.\n\nKeep route elements statically discoverable by the Superblocks editor:\n\n- Prefer direct JSX such as `element: <Page />`.\n- Keep code splitting with top-level `lazy(() => import(\"./pages/Page\"))`\n bindings.\n- Keep page routes as `children` of the `App` layout route, and put one\n `<Suspense>` boundary around `<Outlet />` inside `<AppProvider>` in `{{ui}}App.tsx`.\n- A single-argument wrapper such as `element: withSuspense(Page)` is also\n discoverable when `Page` is directly imported or bound by top-level\n `React.lazy`.\n- Do not unwrap working routes that use this shape.\n- Do not use nested or multi-argument calls such as\n `element: withSuspense(makePage(Home))` or\n `element: withSuspense(Home, opts)`.\n- Do not wrap page JSX in another element, such as\n `element: <PageErrorBoundary><Page /></PageErrorBoundary>`.\n\n```tsx\nimport { App as AppProvider } from \"@superblocksteam/library\";\nimport { Suspense, lazy } from \"react\";\nimport { Outlet, createBrowserRouter } from \"react-router\";\n\nconst Page = lazy(() => import(\"./pages/Page\"));\n\nconst router = createBrowserRouter([\n {\n Component: App,\n children: [{ path: \"/page\", element: <Page /> }],\n },\n]);\n\nfunction App() {\n return (\n <AppProvider className=\"h-full w-full\">\n <Suspense fallback={null}>\n <Outlet />\n </Suspense>\n </AppProvider>\n );\n}\n```\n\n### Page names\n\nA new app ships with one empty page at `{{ui}}pages/Home`, bound as the `/` index route:\n\n```tsx\n{ path: \"/\", index: true, lazy: () => import(\"./pages/Home/index.js\").then((mod) => ({ Component: mod.default })) }\n```\n\n- **Single-page app**: build the landing content in `{{ui}}pages/Home` and keep it named `Home` unless the user asks for something else\n- **Multi-page app**: every page you add beyond the starter gets a descriptive PascalCase name for what it shows (`Dashboard`, `Inventory`, `TopBuilders`), and the starter page keeps `Home` as the app's landing page \u2014 rename it too if the app's landing route is really something else (e.g. `Overview`), keeping it bound to the `/` index route\n- Page names are user-visible in the editor's page switcher, so name them the way you'd label a nav item\n- Never name a page `Page1`, `Page2`, ...\n\n## Design System (Tailwind CSS v4)\n\n**All design tokens are defined in `{{ui}}index.css`**. Apps come with a professional black-and-white theme by default.\n\n### Semantic Tokens Rule\n\n**ALWAYS use semantic tokens** \u2014 NEVER raw Tailwind utilities:\n\n```tsx\n// \u2705 CORRECT\n<Card className=\"bg-background text-foreground border border-border\" />\n\n// \u274C WRONG\n<Card className=\"bg-white text-black border-gray-200\" />\n```\n\n### When to Modify {{ui}}index.css\n\nOnly modify when:\n\n- User explicitly requests branding/theme changes\n- Replicating a specific brand look (e.g., Yelp, Instacart)\n- Migrating an attached third-party app \u2014 see `skills/system/third-party-migration/SKILL.md` (its theme-port rules override the Modification Rules below \u2014 copy source tokens as-is, do not convert to OKLCH or trim the palette)\n- Feature requests (lists, filters, CRUD) do NOT require changes\n\n**Modification Rules:**\n\n- All colors must be in OKLCH format\n- Use semantic names (`--color-warning`, `--shadow-elevated`)\n- Do not remove or rename existing tokens\n- Limit color palette to 5 colors max\n- Avoid gradients unless explicitly requested\n- Minimal font sizes (3 max: body, section heading, main heading)\n\n### Component Variants\n\nUse or create variants instead of one-off styles:\n\n```tsx\n// \u274C WRONG - Hacky inline overrides\n<Button className=\"text-white border-white hover:bg-white\" />\n\n// \u2705 CORRECT - Use a variant\n<Button variant=\"secondary\" />\n```\n\n## Icons\n\nUse icons from Lucide React library:\n\n```tsx\nimport { Icon } from \"@/components/ui/icon\";\n<Icon icon=\"heart\" />;\n\nimport { Button } from \"@/components/ui/button\";\n<Button>\n <Icon icon=\"plus\" /> Add Item\n</Button>;\n```\n\n**Always use icons rather than emojis unless explicitly requested.**\n\nUse current Lucide icon names, not stale aliases: use `house` instead of `home`, `life-buoy` instead of `help-circle` or `circle-help`, and `chart-column-big` instead of `bar-chart-3`.\n\nWhen storing icon names as data (arrays/objects), type them with `IconName`. **`IconName` is a TYPE-only export \u2014 you MUST import it with the `type` keyword.** A value import compiles but CRASHES the app at runtime (`does not provide an export named 'IconName'`) and leaves the preview blank, which blocks UI verification. If you hit that error, read `skills/system/common-import-issues/SKILL.md`.\n\n```tsx\n// \u2705 CORRECT \u2014 type-only import\nimport type { IconName } from \"lucide-react/dynamic\";\n\nconst navItems: { icon: IconName; label: string }[] = [\n { icon: \"house\", label: \"Home\" },\n { icon: \"settings\", label: \"Settings\" },\n];\n\n// \u274C WRONG \u2014 value import crashes the app at runtime and blanks the preview\nimport { IconName } from \"lucide-react/dynamic\";\n```\n\n## Custom Components\n\n**CRITICAL: Component composition is MANDATORY. DO NOT create monolithic pages.**\n\n### The Composition Rule\n\nWhen building ANY feature:\n\n1. **First**, identify reusable parts (list items, cards, forms, filters, headers)\n2. **Then**, create separate component files\n3. **Finally**, compose them together in the page\n\n### When to Create Components\n\n- **Rendering any list** - Extract to a component\n- **Building cards/complex UI** - Each card type is a component\n- **Creating forms** - Form sections are components\n- **Adding filters/headers** - These are components\n- **Page has >50 lines JSX** - Break it down\n\n### Component Boundary Rules\n\n**Inside a custom component:**\n\n- \u2705 Use React hooks (useState, useReducer, useEffect, etc.)\n- \u2705 Use local React state\n- \u2705 Use internal helper components\n- \u2705 Use other registered components\n- \u274C CANNOT call APIs - must pass data into the component\n\n### Example: Proper Component Structure\n\n```tsx\n// {{ui}}components/ProductCard/index.tsx - Extract to component\nimport { Card } from \"@/components/ui/card\";\nimport { Button } from \"@/components/ui/button\";\n\ntype ProductCardProps = {\n product: {\n id: string;\n name: string;\n image: string;\n };\n};\n\nexport default function ProductCard(props: ProductCardProps) {\n return (\n <Card>\n <img src={props.product.image} />\n <h3 className=\"text-lg font-semibold\">{props.product.name}</h3>\n <Button>Add</Button>\n </Card>\n );\n}\n\n// {{ui}}pages/Products/index.tsx - Clean composition\nimport ProductCard from \"@/components/ProductCard\";\n\nconst ProductsPage = () => {\n const [products, setProducts] = useState([]);\n\n return (\n <div className=\"grid grid-cols-3 gap-4\">\n {products.map((p) => (\n <ProductCard key={p.id} product={p} />\n ))}\n </div>\n );\n};\n```\n\n## Visual Excellence\n\n**Prioritize visual excellence from the start:**\n\n1. **Design System Enhancement**: Start by enhancing `{{ui}}index.css` with app-specific colors that match the target aesthetic\n2. **Professional Layout Architecture**: Use sophisticated layouts with proper spacing, responsive design\n3. **Rich Interactive Components**: Leverage advanced components with proper variants and states\n4. **Visual Polish**: Add shadows, smooth transitions, skeleton loaders, hover effects, typography hierarchy\n5. **Real-World UI Patterns**: Create layouts that feel like professional applications\n\n**Make applications that look and feel like real, polished products - not basic wireframes.**\n\n### Loading States\n\nLoading behavior must differ based on whether data has already been fetched:\n\n**Initial load (no data yet):** Show skeleton shimmers that mirror the shape of the final content. Never show a blank screen or a generic spinner.\n\n**Refetch / background refresh (data already exists):** Keep showing the existing data. Use `fetching` from `useApiData` to apply a subtle visual indicator: light opacity (e.g. `opacity-70`) to signal a refresh is in progress, plus a non-blocking label such as a small inline spinner, a thin progress bar, or an \"Updating\u2026\" label. **Do not** use `pointer-events-none` or otherwise disable the content \u2014 it must remain fully interactive during refetch.\n\nUse `loading` and `fetching` from `useApiData` to distinguish these states:\n\n```tsx\nconst { data, loading, fetching, isError, error } = useApiData(\"GetOrders\", {\n status: statusFilter,\n search,\n});\n\n// Initial load \u2014 no data yet \u2192 show skeleton placeholder\nif (loading) {\n return <OrderTableSkeleton />;\n}\n\nif (isError) return <ErrorBanner error={error} />;\n\n// Refetch \u2014 data exists \u2192 subtle opacity + indicator, table stays interactive\nreturn (\n <div>\n {fetching && <div className=\"text-xs text-muted-foreground\">Updating\u2026</div>}\n <div className={fetching ? \"opacity-70\" : \"\"}>\n <OrderTable orders={data.orders} />\n </div>\n </div>\n);\n```\n\n```tsx\n// \u274C WRONG \u2014 pointer-events-none disables the table, making it feel broken\nreturn (\n <div className={fetching ? \"pointer-events-none\" : \"\"}>\n <OrderTable orders={data.orders} />\n </div>\n);\n\n// \u274C WRONG \u2014 no loading feedback when filters change\nif (loading) return <Skeleton />;\nreturn <OrderTable orders={data.orders} />;\n```\n\n#### Table Loading Rules\n\n- **Do not** use `pointer-events-none` on a table during loading \u2014 this disables interaction and makes the UI feel broken. During refetch, apply a subtle opacity (e.g. `opacity-70`) plus a non-blocking indicator (e.g., an \"Updating\u2026\" label or a thin progress bar). The table must remain clickable, sortable, and scrollable.\n- **Do not** replace a populated table with a full skeleton on refetch \u2014 this causes disorienting content flashes.\n- **Skeleton tables are only for initial load** (`loading` is true) when there is no data to display yet. Build them to match the real table's column structure (header + a few placeholder rows).\n\n### Efficient Loading Patterns\n\n1. **Always show loading indicators on refetch**: When inputs change (e.g. filters, search), show a non-blocking visual indicator while new data loads. Use `fetching` from `useApiData`.\n2. **Loading State Hierarchy**:\n - No data yet (`loading`) \u2192 Full skeleton placeholder\n - Has data, refetching (`fetching` && !`loading`) \u2192 Keep showing current data with a subtle visual indicator: light opacity (e.g. `opacity-70`) plus a non-blocking label (e.g. \"Updating\u2026\" text, thin progress bar, inline spinner). Do not use `pointer-events-none` \u2014 the content must remain fully interactive.\n - Error state (`isError`) \u2192 Show error with retry option, optionally keep stale data visible\n3. **Debounce Rapid Requests**: Prevent multiple API calls in short succession\n4. **Use useApiData for automatic refetching**: `useApiData` auto-refetches when inputs change and supports `staleTime`, `refetchOnWindowFocus`, `refetchOnReconnect`, `refetchInterval`, and `retry` options.\n\n## Performance Rules\n\n### 1. ALWAYS Paginate Tables and Lists\n\n**NEVER render more than 50 rows without pagination.** Always add client-side pagination:\n\n```tsx\nfunction PaginatedTable({ data }: { data: any[] }) {\n const PAGE_SIZE = 20;\n const [page, setPage] = useState(0);\n const totalPages = Math.ceil(data.length / PAGE_SIZE);\n const pageData = useMemo(\n () => data.slice(page * PAGE_SIZE, (page + 1) * PAGE_SIZE),\n [data, page],\n );\n\n useEffect(() => {\n setPage(0);\n }, [data.length]);\n\n return (\n <>\n <Table>{/* render pageData rows */}</Table>\n <span>\n Page {page + 1} of {totalPages} ({data.length} total rows)\n </span>\n <Button\n onClick={() => setPage((p) => Math.max(0, p - 1))}\n disabled={page === 0}\n >\n Previous\n </Button>\n <Button\n onClick={() => setPage((p) => Math.min(totalPages - 1, p + 1))}\n disabled={page >= totalPages - 1}\n >\n Next\n </Button>\n </>\n );\n}\n```\n\nFor 200+ rows, prefer **server-side pagination**; use cursor/keyset pagination for high offsets instead of `LIMIT`/`OFFSET`.\n\nFor 500+ item lists where pagination doesn't fit the UX (chat messages, infinite scroll feeds, large dropdowns), use **virtualization** (`react-virtuoso` or `@tanstack/react-virtual`) to render only the items visible in the viewport.\n\n### 2. ALWAYS Debounce Input-Driven API Calls\n\n**NEVER call an API directly from onChange.** Debounce search/filter inputs with a 300ms timer:\n\n```tsx\nfunction DebouncedSearch({ onSearch }: { onSearch: (query: string) => void }) {\n const [localValue, setLocalValue] = useState(\"\");\n const timerRef = useRef<ReturnType<typeof setTimeout>>();\n const handleChange = useCallback(\n (e: React.ChangeEvent<HTMLInputElement>) => {\n setLocalValue(e.target.value);\n clearTimeout(timerRef.current);\n timerRef.current = setTimeout(() => onSearch(e.target.value), 300);\n },\n [onSearch],\n );\n useEffect(() => () => clearTimeout(timerRef.current), []);\n\n return (\n <Input value={localValue} onChange={handleChange} placeholder=\"Search...\" />\n );\n}\n```\n\n### 3. Memoize Expensive Renders\n\nUse memoization (`memo()`, `useMemo`, `useCallback`) when it prevents measurable re-renders or expensive recomputation:\n\n```tsx\nconst OrderRow = memo(function OrderRow({\n order,\n onSelect,\n}: {\n order: Order;\n onSelect: (id: string) => void;\n}) {\n return (\n <TableRow onClick={() => onSelect(order.id)}>\n <TableCell>{order.id}</TableCell>...\n </TableRow>\n );\n});\n\nconst handleSelect = useCallback((id: string) => {\n setSelectedId(id);\n}, []);\n\nconst filtered = useMemo(\n () =>\n orders.filter((o) => o.status === status).sort((a, b) => b.total - a.total),\n [orders, status],\n);\n```\n\n### 4. ALWAYS Clean Up Side Effects\n\nClean up timers, event listeners, and subscriptions in `useEffect` return functions:\n\n```tsx\nuseEffect(() => {\n const handler = (e: KeyboardEvent) => {\n /* ... */\n };\n window.addEventListener(\"keydown\", handler);\n return () => window.removeEventListener(\"keydown\", handler);\n}, []);\n```\n\n### 5. Cancel In-Flight API Requests on Unmount\n\nFor search/filter patterns, prefer `useApiData` \u2014 it handles cancellation and cleanup automatically:\n\n```tsx\n// useApiData: auto-fetches and cancels on unmount/input change\nconst { data, fetching } = useApiData(\"SearchProducts\", { query });\n```\n\nFor imperative usage with manual cleanup, use the `cancel()` function:\n\n```tsx\nconst { run, cancel } = useApi(\"SearchProducts\");\n\nuseEffect(() => {\n if (!query) return;\n run({ query }).catch(console.error);\n return () => {\n cancel().catch(() => {});\n };\n}, [query, run, cancel]);\n```\n";
2
2
  //# sourceMappingURL=skill.generated.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"skill.generated.d.ts","sourceRoot":"","sources":["../../../../../src/ai-service/skills/system/superblocks-frontend/skill.generated.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,OAAO,q52BA0uBnB,CAAC"}
1
+ {"version":3,"file":"skill.generated.d.ts","sourceRoot":"","sources":["../../../../../src/ai-service/skills/system/superblocks-frontend/skill.generated.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,OAAO,802BA2uBnB,CAAC"}
@@ -147,7 +147,8 @@ queryClient.setQueryData(cacheKey, (old) => ({
147
147
 
148
148
  ### Critical API Rules
149
149
 
150
- 1. **MUST call API by exact name**: Format is \`<ApiName>\` matching folder \`apis/<ApiName>/api.yaml\`
150
+ <!-- API_NAME_GUIDANCE -->
151
+
151
152
  2. **Use \`useApiData\` for data loading, \`useApi\` for mutations**: For data fetching, use \`useApiData("GetUsers", { email })\`. Only use \`useApi\` for event-driven actions (button clicks, form submissions). Use \`executeApi\` for API calls outside React components.
152
153
  3. **Use the hook's state fields**: Do NOT create separate \`useState\` for loading or response data — the hooks manage this for you
153
154
  4. **ALWAYS check API interfaces**: NEVER guess the response structure
@@ -1 +1 @@
1
- {"version":3,"file":"skill.generated.js","sourceRoot":"","sources":["../../../../../src/ai-service/skills/system/superblocks-frontend/skill.generated.ts"],"names":[],"mappings":"AAAA,iFAAiF;AACjF,mDAAmD;AAEnD,MAAM,CAAC,MAAM,OAAO,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA0uBtB,CAAC"}
1
+ {"version":3,"file":"skill.generated.js","sourceRoot":"","sources":["../../../../../src/ai-service/skills/system/superblocks-frontend/skill.generated.ts"],"names":[],"mappings":"AAAA,iFAAiF;AACjF,mDAAmD;AAEnD,MAAM,CAAC,MAAM,OAAO,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2uBtB,CAAC"}
@@ -1,2 +1,2 @@
1
- export declare const content = "---\nname: superblocks-migration\ndescription: |\n Convert legacy Superblocks 2.0 YAML APIs (\"block-chain\" format) to the new 3.0 sdk-api TypeScript format.\n Load when executing a v2\u2192v3 migration turn \u2014 the runtime prompt will tell you to.\nreadOnly: true\nmetadata:\n author: superblocks\n version: \"1.0\"\n---\n\n# v2 \u2192 v3 API Translation\n\nYou are converting **legacy Superblocks \"block-chain\" YAML APIs** into the **new code-mode `@superblocksteam/sdk-api` TypeScript format**. Each YAML file under `scratch/v2-backup/apis/<ApiName>/api.yaml` must be emitted as a single TypeScript file at `server/apis/<ApiName>/api.ts` that compiles, typechecks, and preserves behavior exactly.\n\n## Meta-rule: ZERO judgment calls\n\nIf any situation below is not resolved deterministically by this document, **STOP** for that API. Do **not** pick a default. Do **not** guess a factory, a shape, or an import. Skip it, continue with other APIs, and mark the checklist item as `failed` via `build_manageChecklist` with a short `failureReason` describing the exact ambiguity (failing rule, block name if any, what would be needed to proceed). This rule overrides everything else.\n\nCorollary: do not add `// TODO` comments that silently ship. Unresolved items live only as `failed` checklist entries.\n\nCorollary for `migration_page_route_verification_*`: see the _Page route verification_ section below \u2014 screenshot timeout/error \u2192 `failed`, never `completed`, never \"verified via router/source inspection.\" The full rule (including the hard-rule restatements) lives there to avoid drift.\n\n## Inputs and outputs\n\n- **Source tree:** `scratch/v2-backup/apis/<ApiName>/api.yaml`. Discover the actual set by listing the directory \u2014 do not hardcode a count or list.\n- **Target tree:** `server/apis/<ApiName>/api.ts`. Do not create scaffolding trees that do not already exist (see \"Registry registration\" below).\n- **Checklist:** the migration checklist has already been seeded with one item per API (id `api_<ApiName>`, origin `2.0-upgrade`, status `pending`, `clearOnFinalize: false`). Legacy in-flight runs may still have `origin: \"seed_api\"` for these API items. Pull the live set by calling `build_manageChecklist` with `action: \"get\"` and filtering to `status: \"pending\"` plus API-migration origins (`origin: \"2.0-upgrade\"` or legacy `origin: \"seed_api\"`) \u2014 that is your authoritative API list and count for this turn.\n\n## Required reading (read these BEFORE writing any output)\n\nAuthoritative. If a rule here conflicts with your priors, these win.\n\n1. `node_modules/@superblocksteam/sdk-api/README.md` \u2014 whole thing (api() contract, execution model, `ctx.*`, exports, error classes, performance best practices, `useApi` frontend hook, integration method table).\n2. `node_modules/@superblocksteam/sdk-api/src/index.ts` (the **barrel**) \u2014 the authoritative list of exported factory names. Integration factory names are whatever this file exports, not whatever the directory name is. Never invent or case-fold a factory name without confirming it here.\n3. The README for **every integration** referenced by the YAMLs you convert:\n - `node_modules/@superblocksteam/sdk-api/src/integrations/<kind>/README.md`\n - If a vendor-specific factory exists for the target service (e.g., a dedicated one for a given LLM provider), prefer it over a generic HTTP one.\n4. `skills/system/superblocks-migration/references/yaml-block-mapping.md` \u2014 full YAML \u2192 TS mapping for each legacy block type.\n\nThere is **no `javascript` integration** in sdk-api \u2014 inline JS/TS lives directly inside `run(ctx, input)`. Any block whose YAML key is `javascript:` becomes plain TypeScript in `run()`. The same applies to **`python:` blocks**: there is no `python` integration in sdk-api, so port the body to inline TypeScript inside `run(ctx, input)` (see \"Substituting unsupported integrations\" below and the Python section in `yaml-block-mapping.md`).\n\n## Substituting unsupported integrations\n\nThe pre-migration UI warns the user when an app references integrations whose v2 plugin is not present in sdk-api. The migration is **not** aborted on those APIs \u2014 they are still in your `seed_api` checklist and you are expected to make a best-effort port rather than immediately marking them `failed`.\n\nSubstitution rules (apply in order; first match wins):\n\n1. **`python:` step \u2192 inline TypeScript.** Re-write the Python body as equivalent TypeScript inside `run(ctx, input)`, exactly the way `javascript:` blocks are inlined. Translate Python idioms to JS/TS (e.g. `requests.get(...)` \u2192 `fetch(...)`, list comprehensions \u2192 `array.map`/`filter`, `len(x)` \u2192 `x.length`, dict access \u2192 object property access, `os.environ[...]` \u2192 `process.env[...]`, raise/except \u2192 `throw`/`try\u2026catch`). If the Python relied on a third-party PyPI package with no obvious JS equivalent, mark that one API `failed` with a `failureReason` naming the package \u2014 do not ship a guess.\n2. **HTTP-shaped vendor plugin \u2192 REST.** If an unsupported integration was effectively making REST calls (e.g. a thin wrapper around an HTTP API) and you can read the request shape from the YAML, port it to the `restApi` factory from sdk-api (or the dedicated vendor factory if one exists in the barrel \u2014 check `node_modules/@superblocksteam/sdk-api/src/index.ts`).\n3. **Otherwise, mark `failed`.** Per the meta-rule, do not invent a factory or fabricate behavior. Use `build_manageChecklist` with `status: \"failed\"` and a concrete `failureReason` (e.g. `\"unsupported integration <pluginId> with no JS-equivalent path\"`).\n\nDo **not** silently skip a step or replace it with a `// TODO` \u2014 every API still needs a deterministic outcome (`completed` or `failed`) on the checklist.\n\n## Preflight gate (MUST pass before any file edits)\n\nComplete this orientation sequence before writing or modifying any API file:\n\n0. **Install recommended user dependencies (before API work).** The platform restructure already computed exactly which v2 user-added packages the migrated client/server code imports, and persisted them at `scratch/migration-state.json` \u2192 `recommendedUserDeps` (each entry has `name`, `version`, and `dev`). Your job is to install that list \u2014 not to recompute it.\n - Mark `migration_dependency_verification` as `in_progress` via `build_manageChecklist`.\n - Read `scratch/migration-state.json` and extract `recommendedUserDeps`.\n - If `recommendedUserDeps` is missing or empty, mark `migration_dependency_verification` as `completed` and continue to step 1.\n - Otherwise, call `build_installPackages` **once** with every entry from `recommendedUserDeps` (passing `name`, `version`, and `dev` through unchanged). Do **not** skip because `node_modules` exists on disk; the platform never wrote these packages into `package.json`.\n - **Failure semantics (covers every non-success outcome \u2014 no judgment calls):**\n - On full success \u2192 mark `migration_dependency_verification` as `completed`.\n - On **any** failure \u2014 full, partial (some packages installed, others did not), structured registry error (`not_in_registry`, `registry_auth_failed`, `registry_unreachable`), or unstructured error \u2014 mark `migration_dependency_verification` as `failed` with a `failureReason` that lists the affected package names and the tool's verbatim error code or message. Treat partial success the same as full failure for checklist purposes; do not split into multiple checklist items. Then continue to step 1 so API translation can still proceed. The user/operator will repair the registry/packages and re-trigger the migration turn.\n - If `scratch/migration-state.json` also contains `recommendedUserDepsPinnedToLatest` (a string array of package names), include those names in the `failureReason` of `migration_dependency_verification` even on full success \u2014 phrased as \"pinned to latest, may need user confirmation: <names>\" \u2014 so the user can downgrade them before runtime if the latest major is incompatible. Use status `completed` in this case (the install succeeded), but the surfaced reason gives the user a checkpoint to act on.\n - Do **not** scan imports yourself, edit `package.json`, or add packages outside `recommendedUserDeps`. If you believe a package is missing from the list, that is a platform bug: mark `migration_dependency_verification` as `failed` with `failureReason: \"platform bug: <pkg> imported by <file> but absent from recommendedUserDeps\"` and continue with API work using the packages that did install. (`build_manageChecklist` has no `note` action \u2014 `failed` with a structured `failureReason` is the only way to record this.)\n1. Call `build_manageChecklist` with `action: \"get\"` and filter to `status: \"pending\"` and API-migration origins (`origin: \"2.0-upgrade\"` or legacy `origin: \"seed_api\"`) \u2014 this is your authoritative API list and count for this turn.\n2. Read `node_modules/@superblocksteam/sdk-api/src/index.ts` and treat it as the **only** source of truth for sdk-api export/factory names.\n3. Read `node_modules/@superblocksteam/sdk-api/README.md`.\n4. For each API, read only the integration README(s) needed for that API right before migrating it (just-in-time). Do **not** preload every integration README for the entire app.\n\nHard constraints:\n\n- Do **not** rely on prior conversation memory, \"knowledge\" summaries, or inferred export names.\n- Do **not** start editing API files until preflight steps 0\u20133 are complete.\n- If a required file cannot be read, mark that API's checklist item `failed` with a concrete `failureReason` that names the missing path.\n\n## File layout & exports\n\n- One API per file, default export: `export default api({ ... });`.\n- Use ESM-style relative `.js` specifiers in imports.\n- File path: `server/apis/<ApiName>/api.ts` where `<ApiName>` is the YAML `metadata.name` (for example, `GetFloors` \u2192 `server/apis/GetFloors/api.ts`).\n- The `api({ name })` string and the registry key must both equal the YAML `metadata.name` verbatim \u2014 frontends call `useApi(\"<metadata.name>\")`.\n\n### Registry registration (conditional)\n\n- If `server/apis/index.ts` exists in the target tree: add an import + entry for each new module. Do not reorder existing entries.\n- If it does **not** exist: do **not** create it, do not create scaffolding. Mark the checklist item `failed` with a `failureReason` noting the missing registry.\n\n## Critical rules\n\n1. **Integration IDs are opaque per-YAML.** Extract each distinct `step.integration` UUID to a named `const` at the top of the file. Never carry UUIDs across files.\n2. **SQL: parameterize always.** Zero `${\u2026}` interpolations may remain inside any SQL string. Dynamic lists use `= ANY($N::<type>[])` \u2014 never string-building `IN (\u2026)`.\n3. **`query` vs `execute`.** Rows returned \u2192 `query` with Zod schema. Nothing actionable \u2192 `execute`.\n4. **Output shape preservation.** Mirror the legacy API's externally-visible response exactly \u2014 shape, nullability, cardinality. Do not wrap in envelopes.\n5. **Default is sequential.** `TYPE_FOREACH` \u2192 `for ... of` with sequential `await`. `Promise.all` only when YAML explicitly used a `parallel:` block.\n6. **Determinism, no slop.** No retries, no caching, no extra logging beyond a single `ctx.log.info(\"<ApiName> start\", {...})` at the top of `run`. No narrative code comments.\n7. **Never invent client methods.** If the integration README only documents `apiRequest`, use `apiRequest`.\n\n## Security / user identity\n\n- `authorization.type: AUTHORIZATION_TYPE_APP_USERS` is enforced by the server; rely on `ctx.user` in TS.\n- If the YAML accepts `userId` / `email` / etc. as API input, remove that input and read from `ctx.user` instead.\n\n## Observability\n\n- One `ctx.log.info(\"<ApiName> start\", { <safe-ids> })` at the top of `run` is allowed. Never log secrets, tokens, or PII.\n\n## Verification before you hand off\n\nRuntime verification (required):\n\n1. `build_debug` passing is required but not sufficient. Do not treat compile/typecheck success as proof that runtime behavior is correct.\n2. Use risk-based runtime checks: run `testApi` for APIs with integrations (REST/vendor/SQL), multi-step control flow, transformed outputs, or any uncertainty. For obviously simple APIs, you may skip `testApi`.\n3. If `testApi` output appears stale or mismatched, run `build_reloadFile` once and re-test. If runtime still fails after documented-method verification + one reload/retest cycle, record a concrete `failureReason` and continue.\n\nPage route verification (required once per migration run):\n\n**Hard rule:** A route is verified only with **runtime visual evidence on that exact path**. Reading `client/router.tsx`, page source, or backup artifacts is orientation only \u2014 it **never** satisfies verification.\n\n**Hard rule:** `build_debug` passing and `get_runtime_errors` returning `count: 0` are **not sufficient** for route verification. Do not mark routes `completed` because \"only APIs changed\" or \"the frontend files are unchanged.\"\n\n**Hard rule:** If `build_captureScreenshot` errors or times out for a route, mark that `migration_page_route_verification_*` item `failed` with `failureReason` naming the path and outcome (prefix `page_route_screenshot_timeout:` or `page_route_screenshot_error:`). Do **not** mark `completed`. Do **not** substitute router/source inspection.\n\n### Evidence required per route\n\nMark `completed` only when **all** are true:\n\n1. The app preview is on **that route's path** (not merely the default route).\n2. `build_captureScreenshot` **succeeds** and returns an image you inspect.\n3. You **describe** what you see and confirm it is not a loading-only view (follow the screenshot tool's skeleton/spinner retry procedure first).\n4. Any route-specific runtime errors are resolved (re-check after fixes).\n\n### Procedure (one route at a time)\n\n1. Enumerate checklist items whose IDs start with `migration_page_route_verification_`.\n2. Enumerate paths from `scratch/v2-backup/router.tsx` (fallback: `scratch/v2-backup/pages/**/index.tsx` confirmed against `client/router.tsx`).\n3. For each route: set the matching item `in_progress` \u2192 `build_navigatePreview` with that path \u2192 `build_captureScreenshot` \u2192 on success, `completed`.\n4. If the preview looks stale, `build_reloadFile` **once**, then retry screenshot. If capture still fails, `failed` with `failureReason` as above.\n5. Fix import/lazy-load/runtime failures, then repeat from step 3 for that route.\n\n### Forbidden shortcuts\n\n- Marking `completed` because `client/router.tsx` lists the path\n- Marking `completed` because the page module exists under `client/pages/`\n- Marking `completed` after screenshot timeout/error\n- Batch-marking all route items without per-route screenshot evidence\n\nFor each converted API, self-check:\n\n1. File default-exports exactly one `api({...})`; `name` matches `metadata.name`.\n2. Zero `${\u2026}` interpolations remain in any SQL string.\n3. No SQL `IN (\u2026)` built by concatenation; dynamic lists use `= ANY($N::<type>[])`.\n4. Every prior block's `.output` read is replaced by an actual result `const`.\n5. Input is the minimal union of identifiers the YAML references that are not produced by earlier blocks or `Variables`.\n6. Output shape exactly mirrors the legacy API's external response.\n7. If `server/apis/index.ts` exists, it registers the new module under `metadata.name`.\n8. **sdk-api integration wiring:** declare each integration in an `integrations: { \u2026 }` block and call it via `ctx.integrations.<key>` \u2014 never call `postgres(ID)` / `github(ID)` / etc. inside `run()` as if they were clients.\n9. **`input` / `output` schemas:** both are Zod schemas (`input: z.object({\u2026})`, `output: z.\u2026`). Do not use plain `{ field: { type: \"string\" } }` objects.\n10. **`run` signature:** `async run(ctx, input)` (or destructured input fields as the second argument). Never import `ctx` from the module scope.\n\n## Parallelization policy\n\nCount pending APIs from the checklist (`build_manageChecklist` `action: \"get\"`, filter `status: \"pending\"` and API-migration origins: `origin: \"2.0-upgrade\"` or legacy `origin: \"seed_api\"`).\n\n- **If the pending count is `< 8`:** migrate the APIs yourself, sequentially. Do NOT call `spawnCodingSubagents`. Work through each API using the per-API procedure below.\n- **If the pending count is `>= 8`:** you **MUST spawn sub-agents** to migrate these APIs in parallel. There are too many APIs to migrate sequentially yourself. Do NOT attempt to migrate APIs yourself in this run \u2014 use `spawnCodingSubagents`.\n - Split pending APIs into batches of ~5 (aim for 4\u20135 per batch; 4 workers \u00D7 5 APIs = 20, the hard cap), with a hard cap of **4 sub-agents** and **20 APIs** in this spawn. If more APIs remain pending, leave them for a **later spawn round** after this one returns \u2014 never enlarge a worker's share.\n - The spawn tool will reject oversized batches and will refuse to fan out under heap pressure. If it errors because the batch is too large, shrink the batch and retry. If it refuses because of heap pressure, do not retry spawn in this turn \u2014 wait for a later turn after memory has dropped. Do not fall back to migrating remaining APIs yourself while the pending count is still \u2265 8.\n - For each batch, craft an `instructions` string that:\n - Names the exact `<ApiName>` values that sub-agent owns, verbatim, as a bulleted list.\n - **Directs the sub-agent to `build_readFile` `skills/system/superblocks-migration/SKILL.md` (and the integration README(s) for the APIs it owns) before writing any files.** Do not inline a shortened sdk-api contract \u2014 keep a single source of truth.\n - Restates the per-API procedure below so the sub-agent is self-contained.\n - Reminds the sub-agent to update the shared checklist (id `api_<ApiName>`) for every item it owns.\n - **Restates the vacuous-pass rule and the sentinel explicitly**: the sub-agent must apply the `passed` outcome's vacuous-pass check itself (source real inputs before accepting a sad-path-only pass), and when it marks an API `completed` on an exhausted sourcing attempt it MUST set `failureReason` to `vacuous_pass_accepted: <reason>`. This is the only signal that reaches you \u2014 you run the final sweep without the sub-agents' chat context, so an unflagged vacuous pass is invisible to you.\n - **Also pass `apiNames: [\"<ApiName1>\", \"<ApiName2>\", \u2026]` for every sub-agent**, listing the same names that appear in its `instructions`. The spawn tool stamps each `api_<ApiName>` checklist item with that sub-agent's `id` as `workerId` BEFORE fan-out, which lets the migration UI render one batch row per sub-agent in the chat sidebar. Forgetting `apiNames` is non-fatal (the sub-agents still run) but the sidebar checklist will fall back to one row per API instead of one per batch.\n - **Call `spawnCodingSubagents` as the ONLY tool call in that turn.** Do not emit any other tool calls alongside it \u2014 no reads, no writes, no checklist updates. The tool blocks until every sub-agent finishes; you cannot do migration work \"while waiting\" because there is no waiting from your perspective \u2014 your next turn only begins after the tool returns. If you emit other tool calls in the same turn, you and the sub-agents will race on the shared checklist and corrupt state.\n - **After `spawnCodingSubagents` returns** \u2014 only in a subsequent turn \u2014 read the checklist via `build_manageChecklist` with `action: \"get\"`. For any `failed` items, review the `failureReason`. Retry small numbers of recoverable failures yourself (sequentially, using the per-API procedure below). Leave deterministic failures as `failed`. Then re-count pending APIs; if any remain, repeat this batching step with another `spawnCodingSubagents` call. Do not proceed to the final sweep until every API item is `completed` or `failed`.\n\n## Per-API procedure\n\nFor APIs you migrate yourself, or that you embed in a sub-agent's `instructions`:\n\n1. Call `build_manageChecklist` with `action: \"update\"`, `itemId: \"api_<ApiName>\"`, `status: \"in_progress\"`.\n2. Read `scratch/v2-backup/apis/<ApiName>/api.yaml` and any sibling files the blocks reference.\n3. Produce the TypeScript module at `server/apis/<ApiName>/api.ts`, overwriting the stub. Use the YAML block \u2192 TS mapping in `skills/system/superblocks-migration/references/yaml-block-mapping.md`.\n4. If `server/apis/index.ts` exists, update it to register the new module.\n5. Call `build_debug`. If it fails, fix and repeat until it passes. Do not proceed to step 6 until `build_debug` succeeds.\n6. **Call `runMigrationVerification({ apiName: \"<ApiName>\", inputs: <inputs> })` and resolve every divergence in this same chat.** The tool's response includes `attempt` and `maxAttempts` so you don't need to track an attempt counter yourself \u2014 the server reads it from on-disk history per API.\n\n Your job is to make v3 produce the same response as v2 \u2014 not to \"try a few times and punt to the user\". The user already lived with this API working on v2 with their real data; if v3 doesn't match, that's a regression you introduced, and you fix it here. NEVER leave a `diverged` API for the user to \"Accept divergence\" or \"Try one more attempt\" via the API calls panel \u2014 those buttons exist as escape hatches for situations the policy gate requires (mutations, denied integrations), not as a way for you to skip work.\n\n Synthesize `inputs` the same way you would for `testApi`. Inspect the v2 YAML from step 2 (`apiInputs` / externally-declared parameters) and the v3 TS from step 3 (`input` schema on `run(ctx, input)`). Every name the caller must supply must appear as a top-level key in `inputs` with a realistic value \u2014 a mock, or a real value harvested per the \"Sourcing input values\" rules below \u2014 matching its expected type. This includes two categories \u2014 do not conflate them:\n - **API parameters** \u2014 names declared in `apiInputs` (v2) or the v3 `input` shape (e.g. `sourceContext`, `currentDocument`, `messages`). Pass each parameter name directly as a key in `inputs`. These are what the frontend passes when it invokes the API; they are not unavailable \"runtime bindings.\"\n - **UI bindings and caller context** \u2014 values the API reads from the app that are not API parameters (component values like `Input1.value`/`Select1.selectedItem.id`, table selections like `Table1.selectedRow`, state variables, workflow `body`/`params` not declared as `apiInputs`). Mock each binding the API references that isn't produced by an earlier block.\n\n Both v2 and v3 receive the same `inputs`, so this is what makes the diff meaningful \u2014 `{}` for an API that expects parameters or bindings produces a vacuous `both_failed` that tells you nothing. Errors like `'sourceContext' is not defined` (v2) or v3 input-validation failures for missing required fields mean you omitted keys from `inputs`; synthesize mocks and retry. That is not an environmental failure.\n\n **Sourcing input values \u2014 real app data over invented data.** A value is only \"realistic\" if the system the API talks to actually accepts it. Invented values are fine for self-contained inputs (a search string, a page size, a boolean flag). But when an input must reference something that already exists \u2014 any key the target system looks up, such as a row/record ID, a resource name, or an uploaded-file reference \u2014 do NOT invent it: an invented identifier either fails on both sides or exercises only the not-found/guard-clause path, and neither proves the API works. Source a real value instead, in this order:\n 1. **Harvest from the app's own APIs.** Find a read-only API in this app (prefer zero-input \"list\"/\"get all\" APIs) whose output contains the identifier you need, run it via `testApi`, and take a value from its real response. Chain as needed: a list API yields a real ID \u2192 pass it to the detail API \u2192 its response yields the next identifier. Prefer migrating and verifying zero-input read-only APIs first so their outputs become your pool of real test data.\n 2. **Follow the client code.** The `useApi`/`useApiData`/`executeApi` call sites under `client/` (and the v2 pages under `scratch/v2-backup/`) show where the app gets each input \u2014 a table's selected row, a route param, a select populated by another API. Trace that source to a concrete real value.\n 3. Only if neither path yields a value, fall back to an invented mock \u2014 and treat any resulting not-found/empty output as unverified (see the vacuous-pass rule under `passed` below).\n\n Outcomes and how to resolve them:\n - **`passed`**: `passed` only proves v2 and v3 agree on the inputs you supplied. Before accepting it, check what actually ran: if the shared output is a sad-path/guard-clause result \u2014 a not-found or error object from a caught exception, a validity flag that came back false, an empty result from a lookup that should have matched \u2014 the API's core logic never executed and the pass is vacuous. In that case source real inputs (per the sourcing rules above) and re-run `runMigrationVerification`; only accept a sad-path-only pass after the sourcing steps genuinely produced no usable value. Once a non-vacuous `passed` is in hand, call `build_manageChecklist` with `status: \"completed\"`. Done. If instead you are marking `completed` on an exhausted sourcing attempt (the pass stayed vacuous), you MUST also set `failureReason` to a string beginning `vacuous_pass_accepted:` followed by a one-line reason \u2014 this sentinel is the only durable signal that survives into the final sweep (and across sub-agents, whose chat context the orchestrator cannot see).\n\n **Platform gate:** the checklist store rejects `status: \"completed\"` on `api_<ApiName>` items until verification has run at least once (`lastVerificationAt` is set \u2014 normally via this tool). You cannot bulk-mark APIs complete without calling `runMigrationVerification` per API.\n - **`skipped_mutation`**: the runtime marks the checklist item `completed` with `verificationSkippedReason: \"mutation\"` automatically. Do **NOT** keep it `in_progress` or ask the user a grouped decision. Continue translating each API as well as you can from the code; the user will verify mutation-skipped APIs manually at finalize time.\n\n - **`v2_unrunnable`**: the v2 backup itself couldn't execute, so there is no baseline to match. Call `build_manageChecklist` with `status: \"completed\"` and continue.\n\n - **`timeout`**: a pipeline stalled past the reporting timeout (`side` says which one). This is transient environment noise \u2014 a dropped editor connection or a backgrounded browser tab \u2014 NOT evidence about the API. Do **NOT** mark the item `completed`. Retry `runMigrationVerification` after finishing your current API. If it times out repeatedly, tell the user verification is stalling (likely tab backgrounded / connection dropped), ask them to keep the app tab focused, and retry \u2014 never convert repeated timeouts into `completed`.\n\n - **`disconnected`**: the editor connection was down when the pipeline tried to run (`side` says which one) \u2014 the app tab is closed, backgrounded, or reconnecting. Unlike `timeout` this diagnosis is certain, and it says nothing about the API. Do **NOT** mark the item `completed`. Tell the user the app tab must stay open and focused during verification, then retry `runMigrationVerification` \u2014 never convert `disconnected` into `completed`.\n\n - **`both_failed`**:\n - If you passed `inputs: {}` or omitted declared API parameters / bindings: synthesize real `inputs` (per the bullet above) and retry. Do NOT mark completed here \u2014 missing inputs caused this, not a real failure.\n - If the error names a symbol that appears in `apiInputs` or the v3 `input` schema: add that key to `inputs` with a realistic mock and retry. Do NOT classify this as environmental \u2014 the parameter is available; you did not supply it.\n - If you passed real inputs and v2 still failed: read v2's error. If it is environmental (auth/credentials/integration-unavailable: \"no integration configured\", \"401\", \"ECONNREFUSED\", \"missing API key\"), v2 cannot run in this verification context regardless of what you do \u2014 call `build_manageChecklist` with `status: \"completed\"` and continue; the user will verify manually. If it's a v2 runtime error that v3 also reproduces, treat as `diverged` (your inputs are wrong, or the API expects state that isn't reproducible here \u2014 revise inputs and retry).\n\n - **`error`**: a system error in the verification flow itself (cannot read v3 source, etc.). Fix what you can; if it persists across one retry, mark the item `failed` with `failureReason: \"<error detail>\"` and continue.\n\n- **`diverged`**: the API ran on both sides and the outputs differ. **Iterate until v3 matches v2 byte-for-byte (modulo the normalizations api-comparator already applies: identical ISO timestamps, near-now ISO timestamps within \u00B160s of each other and \u00B15min of \"now\", UUIDs, monotonic integer IDs differing by \u22641 on fields whose leaf ends with \"id\", floats within 1e-9 relative tolerance, and per-request opaque identifiers at leaves named `requestId`/`traceId`/`spanId`/`correlationId`/`sessionId`/`nonce`/`csrfToken`/`_id` \u2014 note: a bare leaf named `token` is intentionally NOT normalized, since auth/access-token divergence is a security-relevant signal we want to surface).**\n\n There is one bounded judgment exception: if both v2 and v3 executions succeeded, the output shape/types are equivalent, and the remaining diffs are value-level fields you reasonably infer are inherently non-deterministic outputs for this API, treat the migration as semantically complete for this run. In that case, mark the checklist item `completed` and continue.\n\n Use this exception only when all of the following are true:\n - The divergence is not structural (no missing/extra fields, no type mismatch, no changed nesting/cardinality, no changed error/status behavior).\n - The differing fields are expected to vary per invocation and do not change control flow decisions.\n - The API's functional contract remains intact for downstream consumers.\n\n If any of those conditions are not met, the divergence is semantic and must be fixed \u2014 do NOT add code purely to mask values the comparator already tolerates.\n\n For each `summaryForAgent` entry \u2014 every entry names the JSON path that diverged and the v2/v3 values \u2014 find the block in `server/apis/<ApiName>/api.ts` that produces that path and fix the discrepancy. Common causes, in rough order of frequency:\n - wrong column projection or missing field in a SELECT\n - missing transform / output shape mismatch (array vs object wrapper, single vs list)\n - off-by-one or wrong placeholder in a SQL parameter\n - mistyped binding key (case, plural, dotted path)\n - type coercion (string vs number, null vs undefined, boolean vs \"true\")\n - missing default value when the binding is undefined\n - wrong join order or implicit ordering of rows\n - filter/where condition different from v2\n - missing `LIMIT`, `OFFSET`, or pagination handling\n\n If a divergence stems from the `inputs` you passed (e.g. v2 echoes the input one way and v3 a different way because they normalize it differently), revise the inputs alongside the source.\n\n Then call `build_debug` (fix any failures), then `runMigrationVerification` again with the revised source/inputs.\n\n **There is no fixed retry budget \u2014 iterate until `passed` or until you have firm structural evidence that no v3 source change can reconcile v2 and v3.** Acceptable evidence for stopping: v2 references a YAML construct with no v3 SDK equivalent and the divergence is in that construct; v2 derives a value from runtime state v3 cannot observe (e.g. an env var that differs by environment). When you stop on structural grounds, mark the item `failed` with `failureReason: \"verification_diverged_irreconcilable: <one-paragraph reason naming the specific summaryForAgent entry and the structural reason> | summary: <JSON.stringify(summaryForAgent)>\"`.\n\n Guardrail against infinite loops (NOT a retry budget): if you have iterated 8 times on the same API and the set of divergences has not materially changed across the last 3 attempts (i.e. you are flailing, not converging), stop and mark `failed` with the same `failureReason` shape. If you ARE converging \u2014 fewer divergences each attempt, or the remaining ones look smaller \u2014 keep going past 8.\n\n## Run-level rules\n\n- Do NOT emit a free-text UNRESOLVED report \u2014 every unresolved item must be a `failed` checklist entry with a `failureReason`.\n- Do NOT stop the run because one API failed. Only stop once every API is either `completed` or `failed`.\n\n## Updating integration write policy (Clark-only, with user consent)\n\nDuring migration, integration write policy (`allow` / `deny` per integration UUID) lives in `scratch/migration-state.json` \u2192 `integrationWritePolicy`. The pre-migration modal sets it once; **there is no mid-migration UI for the user to change it.**\n\n- **Only Clark can update write policy mid-migration**, and only when the user **explicitly asks** (e.g. \"test this API even though I set Don't test\", \"go ahead and run writes against Postgres\").\n- **Never** tell the user to update write policy in the UI \u2014 they cannot.\n- **Never** hand-edit `scratch/migration-state.json` with `build_writeFile` / `build_editFile` to change write policy. Use `updateMigrationWritePolicy` instead; it validates input and shows a permission card when granting `allow`.\n- To find integration ids: read the `const` UUID at the top of `server/apis/<ApiName>/api.ts`, or read existing keys from `scratch/migration-state.json` \u2192 `integrationWritePolicy`.\n- After updating policy to `allow` (with user approval), call `testApi` or `runMigrationVerification` again for the affected APIs.\n\nIf `testApi` is blocked by access control because an integration is still `deny`, explain that the user previously chose \"Don't test\" for that integration and ask whether they want you to update the policy and retry \u2014 do not suggest they change settings in the UI.\n\n## User-reported manual verification\n\nSome APIs are marked `completed` without automatic testing \u2014 write-policy \"Don't test\" (`verificationSkippedReason: \"mutation\"`) or a `testApi` permission denial (`verificationSkippedReason: \"testing_denied\"`). The user is expected to verify these by using the app themselves.\n\n### When the user asks how to test a skipped API\n\nIf the user requests manual test instructions for a specific API (including when the UI sends a prompt referencing `skills/system/migration-api-verification/SKILL.md`):\n\n1. Read `skills/system/migration-api-verification/SKILL.md` and follow it.\n2. Produce UI click-through steps for that API \u2014 do **NOT** call `testApi` or `runMigrationVerification`.\n\n### When the user confirms they verified an API\n\nIf the user tells you they have checked such an API and it works (e.g. \"I clicked through the app and GetOrders works, mark it complete\"), call `markApiManuallyVerified({ apiName })`. Do **NOT** call `testApi` or `runMigrationVerification` for it \u2014 the user opted out of automatic testing. This records the API as user-verified so it no longer appears in the list of APIs needing manual verification.\n\nIf the user instead asks you to **test** a denied or skipped API (not merely confirm they tested it manually), call `updateMigrationWritePolicy` first to grant `allow` for the relevant integration ids, then run `testApi` or `runMigrationVerification`.\n\n## Post-batch re-verification\n\nAfter `spawnCodingSubagents` returns (or after all sequential APIs are settled), before updating the checklist overall status:\n\n1. Call `build_manageChecklist` with `action: \"get\"` and read all items.\n2. For any item where `verificationOutcome === \"auto_passed\"` and `lastVerificationAt` is older than any sibling item's `updatedAt` minus 60 seconds: this item may have drifted due to registry edits made by later sub-agents. Re-run `runMigrationVerification({ apiName, inputs: <inputs> })` for each such item \u2014 synthesize `inputs` the same way as in step 6 of the per-API procedure, including the \"Sourcing input values\" rule (harvest real IDs from the app's read-only APIs; do not invent identifiers).\n3. If any re-verification produces `diverged`, apply the normal per-API diverge\u2192retry loop (no fixed retry budget; iterate until `passed` or firm structural-irreconcilability evidence, with the anti-flail guardrail at 8 iterations without convergence).\n4. Proceed to mark overall checklist completed/failed only after all re-verifications settle and the final sweep below is done.\n\n## Final sweep: retry unverified happy paths with real app data (required once per run)\n\nIf your closing summary would say \"I could not verify the happy path for X\" \u2014 that sentence is a trigger to do more work, not a conclusion to hand the user. Before marking the overall checklist completed/failed, do one last pass:\n\n1. From the checklist, collect every API that (a) is `failed` or unresolved because its test never succeeded, or (b) was marked `completed` but carries the `vacuous_pass_accepted:` sentinel in `failureReason` (set by you or by a sub-agent when a sad-path-only pass was accepted after exhausting sourcing).\n\n Note on the parallel path: for APIs a sub-agent migrated, you cannot see its chat context or reconstruct its verification outputs \u2014 `verificationOutcome` records only the outcome type (`auto_passed`), not whether the pass was vacuous. The `vacuous_pass_accepted:` sentinel is therefore the ONLY way a sub-agent's vacuous pass surfaces here; APIs a sub-agent completed without it are treated as genuinely verified. This is why the sub-agent `instructions` MUST restate the vacuous-pass rule and the sentinel (see Parallelization policy). If you have specific reason to distrust a sub-agent's `completed` items (e.g. its batch reported many `both_failed` retries), re-verify them here with harvested real inputs rather than trusting the bare `completed`.\n\n2. For each, harvest real inputs from the app itself per the \"Sourcing input values\" rule in the per-API procedure: run the app's read-only APIs via `testApi` (zero-input list APIs first) and chain their real outputs into the inputs of the API under test.\n3. Re-run `runMigrationVerification` with those real inputs and resolve the outcome per the normal per-API rules.\n4. Only APIs whose sourcing steps genuinely produced no usable value (or whose failures are environmental per the `both_failed` rules) may finish the run without a happy-path verification \u2014 and each needs a `failureReason` or verification record saying so.\n";
1
+ export declare const content = "---\nname: superblocks-migration\ndescription: |\n Convert legacy Superblocks 2.0 YAML APIs (\"block-chain\" format) to the new 3.0 sdk-api TypeScript format.\n Load when executing a v2\u2192v3 migration turn \u2014 the runtime prompt will tell you to.\nreadOnly: true\nmetadata:\n author: superblocks\n version: \"1.0\"\n---\n\n# v2 \u2192 v3 API Translation\n\nYou are converting **legacy Superblocks \"block-chain\" YAML APIs** into the **new code-mode `@superblocksteam/sdk-api` TypeScript format**. Each YAML file under `scratch/v2-backup/apis/<ApiName>/api.yaml` must be emitted as a single TypeScript file at `server/apis/<ApiName>/api.ts` that compiles, typechecks, and preserves behavior exactly.\n\n## Meta-rule: ZERO judgment calls\n\nIf any situation below is not resolved deterministically by this document, **STOP** for that API. Do **not** pick a default. Do **not** guess a factory, a shape, or an import. Skip it, continue with other APIs, and mark the checklist item as `failed` via `build_manageChecklist` with a short `failureReason` describing the exact ambiguity (failing rule, block name if any, what would be needed to proceed). This rule overrides everything else.\n\nCorollary: do not add `// TODO` comments that silently ship. Unresolved items live only as `failed` checklist entries.\n\nCorollary for `migration_page_route_verification_*`: see the _Page route verification_ section below \u2014 screenshot timeout/error \u2192 `failed`, never `completed`, never \"verified via router/source inspection.\" The full rule (including the hard-rule restatements) lives there to avoid drift.\n\n## Inputs and outputs\n\n- **Source tree:** `scratch/v2-backup/apis/<ApiName>/api.yaml`. Discover the actual set by listing the directory \u2014 do not hardcode a count or list.\n- **Target tree:** `server/apis/<ApiName>/api.ts`. Do not create scaffolding trees that do not already exist (see \"Registry registration\" below).\n- **Checklist:** the migration checklist has already been seeded with one item per API (id `api_<ApiName>`, origin `2.0-upgrade`, status `pending`, `clearOnFinalize: false`). Legacy in-flight runs may still have `origin: \"seed_api\"` for these API items. Pull the live set by calling `build_manageChecklist` with `action: \"get\"` and filtering to `status: \"pending\"` plus API-migration origins (`origin: \"2.0-upgrade\"` or legacy `origin: \"seed_api\"`) \u2014 that is your authoritative API list and count for this turn.\n\n## Required reading (read these BEFORE writing any output)\n\nAuthoritative. If a rule here conflicts with your priors, these win.\n\n1. The installed SDK root documentation through `getSdkApiDocs` \u2014 call it with no arguments for the current section index, then retrieve every top-level section (following every `root.continuation`) so the full api() contract, execution model, `ctx.*`, exports, error classes, performance best practices, `useApi` frontend hook, and integration method table are covered without loading the whole README at once.\n2. `node_modules/@superblocksteam/sdk-api/src/index.ts` (the **barrel**) \u2014 the authoritative list of exported factory names. Integration factory names are whatever this file exports, not whatever the directory name is. Never invent or case-fold a factory name without confirming it here.\n3. Version-aware documentation for **every integration** referenced by the YAMLs you convert:\n - Call `getSdkApiDocs` with that integration's ID (or plugin ID when no integration ID is available).\n - If a vendor-specific factory exists for the target service (e.g., a dedicated one for a given LLM provider), prefer it over a generic HTTP one.\n4. `skills/system/superblocks-migration/references/yaml-block-mapping.md` \u2014 full YAML \u2192 TS mapping for each legacy block type.\n\nThere is **no `javascript` integration** in sdk-api \u2014 inline JS/TS lives directly inside `run(ctx, input)`. Any block whose YAML key is `javascript:` becomes plain TypeScript in `run()`. The same applies to **`python:` blocks**: there is no `python` integration in sdk-api, so port the body to inline TypeScript inside `run(ctx, input)` (see \"Substituting unsupported integrations\" below and the Python section in `yaml-block-mapping.md`).\n\n## Substituting unsupported integrations\n\nThe pre-migration UI warns the user when an app references integrations whose v2 plugin is not present in sdk-api. The migration is **not** aborted on those APIs \u2014 they are still in your `seed_api` checklist and you are expected to make a best-effort port rather than immediately marking them `failed`.\n\nSubstitution rules (apply in order; first match wins):\n\n1. **`python:` step \u2192 inline TypeScript.** Re-write the Python body as equivalent TypeScript inside `run(ctx, input)`, exactly the way `javascript:` blocks are inlined. Translate Python idioms to JS/TS (e.g. `requests.get(...)` \u2192 `fetch(...)`, list comprehensions \u2192 `array.map`/`filter`, `len(x)` \u2192 `x.length`, dict access \u2192 object property access, `os.environ[...]` \u2192 `process.env[...]`, raise/except \u2192 `throw`/`try\u2026catch`). If the Python relied on a third-party PyPI package with no obvious JS equivalent, mark that one API `failed` with a `failureReason` naming the package \u2014 do not ship a guess.\n2. **HTTP-shaped vendor plugin \u2192 REST.** If an unsupported integration was effectively making REST calls (e.g. a thin wrapper around an HTTP API) and you can read the request shape from the YAML, port it to the `restApi` factory from sdk-api (or the dedicated vendor factory if one exists in the barrel \u2014 check `node_modules/@superblocksteam/sdk-api/src/index.ts`).\n3. **Otherwise, mark `failed`.** Per the meta-rule, do not invent a factory or fabricate behavior. Use `build_manageChecklist` with `status: \"failed\"` and a concrete `failureReason` (e.g. `\"unsupported integration <pluginId> with no JS-equivalent path\"`).\n\nDo **not** silently skip a step or replace it with a `// TODO` \u2014 every API still needs a deterministic outcome (`completed` or `failed`) on the checklist.\n\n## Preflight gate (MUST pass before any file edits)\n\nComplete this orientation sequence before writing or modifying any API file:\n\n0. **Install recommended user dependencies (before API work).** The platform restructure already computed exactly which v2 user-added packages the migrated client/server code imports, and persisted them at `scratch/migration-state.json` \u2192 `recommendedUserDeps` (each entry has `name`, `version`, and `dev`). Your job is to install that list \u2014 not to recompute it.\n - Mark `migration_dependency_verification` as `in_progress` via `build_manageChecklist`.\n - Read `scratch/migration-state.json` and extract `recommendedUserDeps`.\n - If `recommendedUserDeps` is missing or empty, mark `migration_dependency_verification` as `completed` and continue to step 1.\n - Otherwise, call `build_installPackages` **once** with every entry from `recommendedUserDeps` (passing `name`, `version`, and `dev` through unchanged). Do **not** skip because `node_modules` exists on disk; the platform never wrote these packages into `package.json`.\n - **Failure semantics (covers every non-success outcome \u2014 no judgment calls):**\n - On full success \u2192 mark `migration_dependency_verification` as `completed`.\n - On **any** failure \u2014 full, partial (some packages installed, others did not), structured registry error (`not_in_registry`, `registry_auth_failed`, `registry_unreachable`), or unstructured error \u2014 mark `migration_dependency_verification` as `failed` with a `failureReason` that lists the affected package names and the tool's verbatim error code or message. Treat partial success the same as full failure for checklist purposes; do not split into multiple checklist items. Then continue to step 1 so API translation can still proceed. The user/operator will repair the registry/packages and re-trigger the migration turn.\n - If `scratch/migration-state.json` also contains `recommendedUserDepsPinnedToLatest` (a string array of package names), include those names in the `failureReason` of `migration_dependency_verification` even on full success \u2014 phrased as \"pinned to latest, may need user confirmation: <names>\" \u2014 so the user can downgrade them before runtime if the latest major is incompatible. Use status `completed` in this case (the install succeeded), but the surfaced reason gives the user a checkpoint to act on.\n - Do **not** scan imports yourself, edit `package.json`, or add packages outside `recommendedUserDeps`. If you believe a package is missing from the list, that is a platform bug: mark `migration_dependency_verification` as `failed` with `failureReason: \"platform bug: <pkg> imported by <file> but absent from recommendedUserDeps\"` and continue with API work using the packages that did install. (`build_manageChecklist` has no `note` action \u2014 `failed` with a structured `failureReason` is the only way to record this.)\n1. Call `build_manageChecklist` with `action: \"get\"` and filter to `status: \"pending\"` and API-migration origins (`origin: \"2.0-upgrade\"` or legacy `origin: \"seed_api\"`) \u2014 this is your authoritative API list and count for this turn.\n2. Read `node_modules/@superblocksteam/sdk-api/src/index.ts` and treat it as the **only** source of truth for sdk-api export/factory names.\n3. Call `getSdkApiDocs()` for the installed root README index, then retrieve every top-level section and follow every continuation before editing. This is the progressive equivalent of reading the whole root README.\n4. For each API, call `getSdkApiDocs` only for the integration(s) needed by that API right before migrating it (just-in-time). Do **not** preload every integration's documentation for the entire app.\n\nHard constraints:\n\n- Do **not** rely on prior conversation memory, \"knowledge\" summaries, or inferred export names.\n- Do **not** start editing API files until preflight steps 0\u20133 are complete.\n- If required documentation cannot be retrieved, mark that API's checklist item `failed` with a concrete `failureReason` that names the failed `getSdkApiDocs` selector or integration ID.\n\n## File layout & exports\n\n- One API per file, default export: `export default api({ ... });`.\n- Use ESM-style relative `.js` specifiers in imports.\n- File path: `server/apis/<ApiName>/api.ts` where `<ApiName>` is the YAML `metadata.name` (for example, `GetFloors` \u2192 `server/apis/GetFloors/api.ts`).\n- The `api({ name })` string and the registry key must both equal the YAML `metadata.name` verbatim \u2014 frontends call `useApi(\"<metadata.name>\")`.\n\n### Registry registration (conditional)\n\n- If `server/apis/index.ts` exists in the target tree: add an import + entry for each new module. Do not reorder existing entries.\n- If it does **not** exist: do **not** create it, do not create scaffolding. Mark the checklist item `failed` with a `failureReason` noting the missing registry.\n\n## Critical rules\n\n1. **Integration IDs are opaque per-YAML.** Extract each distinct `step.integration` UUID to a named `const` at the top of the file. Never carry UUIDs across files.\n2. **SQL: parameterize always.** Zero `${\u2026}` interpolations may remain inside any SQL string. Dynamic lists use `= ANY($N::<type>[])` \u2014 never string-building `IN (\u2026)`.\n3. **`query` vs `execute`.** Rows returned \u2192 `query` with Zod schema. Nothing actionable \u2192 `execute`.\n4. **Output shape preservation.** Mirror the legacy API's externally-visible response exactly \u2014 shape, nullability, cardinality. Do not wrap in envelopes.\n5. **Default is sequential.** `TYPE_FOREACH` \u2192 `for ... of` with sequential `await`. `Promise.all` only when YAML explicitly used a `parallel:` block.\n6. **Determinism, no slop.** No retries, no caching, no extra logging beyond a single `ctx.log.info(\"<ApiName> start\", {...})` at the top of `run`. No narrative code comments.\n7. **Never invent client methods.** If the integration README only documents `apiRequest`, use `apiRequest`.\n\n## Security / user identity\n\n- `authorization.type: AUTHORIZATION_TYPE_APP_USERS` is enforced by the server; rely on `ctx.user` in TS.\n- If the YAML accepts `userId` / `email` / etc. as API input, remove that input and read from `ctx.user` instead.\n\n## Observability\n\n- One `ctx.log.info(\"<ApiName> start\", { <safe-ids> })` at the top of `run` is allowed. Never log secrets, tokens, or PII.\n\n## Verification before you hand off\n\nRuntime verification (required):\n\n1. `build_debug` passing is required but not sufficient. Do not treat compile/typecheck success as proof that runtime behavior is correct.\n2. Use risk-based runtime checks: run `testApi` for APIs with integrations (REST/vendor/SQL), multi-step control flow, transformed outputs, or any uncertainty. For obviously simple APIs, you may skip `testApi`.\n3. If `testApi` output appears stale or mismatched, run `build_reloadFile` once and re-test. If runtime still fails after documented-method verification + one reload/retest cycle, record a concrete `failureReason` and continue.\n\nPage route verification (required once per migration run):\n\n**Hard rule:** A route is verified only with **runtime visual evidence on that exact path**. Reading `client/router.tsx`, page source, or backup artifacts is orientation only \u2014 it **never** satisfies verification.\n\n**Hard rule:** `build_debug` passing and `get_runtime_errors` returning `count: 0` are **not sufficient** for route verification. Do not mark routes `completed` because \"only APIs changed\" or \"the frontend files are unchanged.\"\n\n**Hard rule:** If `build_captureScreenshot` errors or times out for a route, mark that `migration_page_route_verification_*` item `failed` with `failureReason` naming the path and outcome (prefix `page_route_screenshot_timeout:` or `page_route_screenshot_error:`). Do **not** mark `completed`. Do **not** substitute router/source inspection.\n\n### Evidence required per route\n\nMark `completed` only when **all** are true:\n\n1. The app preview is on **that route's path** (not merely the default route).\n2. `build_captureScreenshot` **succeeds** and returns an image you inspect.\n3. You **describe** what you see and confirm it is not a loading-only view (follow the screenshot tool's skeleton/spinner retry procedure first).\n4. Any route-specific runtime errors are resolved (re-check after fixes).\n\n### Procedure (one route at a time)\n\n1. Enumerate checklist items whose IDs start with `migration_page_route_verification_`.\n2. Enumerate paths from `scratch/v2-backup/router.tsx` (fallback: `scratch/v2-backup/pages/**/index.tsx` confirmed against `client/router.tsx`).\n3. For each route: set the matching item `in_progress` \u2192 `build_navigatePreview` with that path \u2192 `build_captureScreenshot` \u2192 on success, `completed`.\n4. If the preview looks stale, `build_reloadFile` **once**, then retry screenshot. If capture still fails, `failed` with `failureReason` as above.\n5. Fix import/lazy-load/runtime failures, then repeat from step 3 for that route.\n\n### Forbidden shortcuts\n\n- Marking `completed` because `client/router.tsx` lists the path\n- Marking `completed` because the page module exists under `client/pages/`\n- Marking `completed` after screenshot timeout/error\n- Batch-marking all route items without per-route screenshot evidence\n\nFor each converted API, self-check:\n\n1. File default-exports exactly one `api({...})`; `name` matches `metadata.name`.\n2. Zero `${\u2026}` interpolations remain in any SQL string.\n3. No SQL `IN (\u2026)` built by concatenation; dynamic lists use `= ANY($N::<type>[])`.\n4. Every prior block's `.output` read is replaced by an actual result `const`.\n5. Input is the minimal union of identifiers the YAML references that are not produced by earlier blocks or `Variables`.\n6. Output shape exactly mirrors the legacy API's external response.\n7. If `server/apis/index.ts` exists, it registers the new module under `metadata.name`.\n8. **sdk-api integration wiring:** declare each integration in an `integrations: { \u2026 }` block and call it via `ctx.integrations.<key>` \u2014 never call `postgres(ID)` / `github(ID)` / etc. inside `run()` as if they were clients.\n9. **`input` / `output` schemas:** both are Zod schemas (`input: z.object({\u2026})`, `output: z.\u2026`). Do not use plain `{ field: { type: \"string\" } }` objects.\n10. **`run` signature:** `async run(ctx, input)` (or destructured input fields as the second argument). Never import `ctx` from the module scope.\n\n## Parallelization policy\n\nCount pending APIs from the checklist (`build_manageChecklist` `action: \"get\"`, filter `status: \"pending\"` and API-migration origins: `origin: \"2.0-upgrade\"` or legacy `origin: \"seed_api\"`).\n\n- **If the pending count is `< 8`:** migrate the APIs yourself, sequentially. Do NOT call `spawnCodingSubagents`. Work through each API using the per-API procedure below.\n- **If the pending count is `>= 8`:** you **MUST spawn sub-agents** to migrate these APIs in parallel. There are too many APIs to migrate sequentially yourself. Do NOT attempt to migrate APIs yourself in this run \u2014 use `spawnCodingSubagents`.\n - Split pending APIs into batches of ~5 (aim for 4\u20135 per batch; 4 workers \u00D7 5 APIs = 20, the hard cap), with a hard cap of **4 sub-agents** and **20 APIs** in this spawn. If more APIs remain pending, leave them for a **later spawn round** after this one returns \u2014 never enlarge a worker's share.\n - The spawn tool will reject oversized batches and will refuse to fan out under heap pressure. If it errors because the batch is too large, shrink the batch and retry. If it refuses because of heap pressure, do not retry spawn in this turn \u2014 wait for a later turn after memory has dropped. Do not fall back to migrating remaining APIs yourself while the pending count is still \u2265 8.\n - For each batch, craft an `instructions` string that:\n - Names the exact `<ApiName>` values that sub-agent owns, verbatim, as a bulleted list.\n - **Directs the sub-agent to `build_readFile` `skills/system/superblocks-migration/SKILL.md` (and the integration README(s) for the APIs it owns) before writing any files.** Do not inline a shortened sdk-api contract \u2014 keep a single source of truth.\n - Restates the per-API procedure below so the sub-agent is self-contained.\n - Reminds the sub-agent to update the shared checklist (id `api_<ApiName>`) for every item it owns.\n - **Restates the vacuous-pass rule and the sentinel explicitly**: the sub-agent must apply the `passed` outcome's vacuous-pass check itself (source real inputs before accepting a sad-path-only pass), and when it marks an API `completed` on an exhausted sourcing attempt it MUST set `failureReason` to `vacuous_pass_accepted: <reason>`. This is the only signal that reaches you \u2014 you run the final sweep without the sub-agents' chat context, so an unflagged vacuous pass is invisible to you.\n - **Also pass `apiNames: [\"<ApiName1>\", \"<ApiName2>\", \u2026]` for every sub-agent**, listing the same names that appear in its `instructions`. The spawn tool stamps each `api_<ApiName>` checklist item with that sub-agent's `id` as `workerId` BEFORE fan-out, which lets the migration UI render one batch row per sub-agent in the chat sidebar. Forgetting `apiNames` is non-fatal (the sub-agents still run) but the sidebar checklist will fall back to one row per API instead of one per batch.\n - **Call `spawnCodingSubagents` as the ONLY tool call in that turn.** Do not emit any other tool calls alongside it \u2014 no reads, no writes, no checklist updates. The tool blocks until every sub-agent finishes; you cannot do migration work \"while waiting\" because there is no waiting from your perspective \u2014 your next turn only begins after the tool returns. If you emit other tool calls in the same turn, you and the sub-agents will race on the shared checklist and corrupt state.\n - **After `spawnCodingSubagents` returns** \u2014 only in a subsequent turn \u2014 read the checklist via `build_manageChecklist` with `action: \"get\"`. For any `failed` items, review the `failureReason`. Retry small numbers of recoverable failures yourself (sequentially, using the per-API procedure below). Leave deterministic failures as `failed`. Then re-count pending APIs; if any remain, repeat this batching step with another `spawnCodingSubagents` call. Do not proceed to the final sweep until every API item is `completed` or `failed`.\n\n## Per-API procedure\n\nFor APIs you migrate yourself, or that you embed in a sub-agent's `instructions`:\n\n1. Call `build_manageChecklist` with `action: \"update\"`, `itemId: \"api_<ApiName>\"`, `status: \"in_progress\"`.\n2. Read `scratch/v2-backup/apis/<ApiName>/api.yaml` and any sibling files the blocks reference.\n3. Produce the TypeScript module at `server/apis/<ApiName>/api.ts`, overwriting the stub. Use the YAML block \u2192 TS mapping in `skills/system/superblocks-migration/references/yaml-block-mapping.md`.\n4. If `server/apis/index.ts` exists, update it to register the new module.\n5. Call `build_debug`. If it fails, fix and repeat until it passes. Do not proceed to step 6 until `build_debug` succeeds.\n6. **Call `runMigrationVerification({ apiName: \"<ApiName>\", inputs: <inputs> })` and resolve every divergence in this same chat.** The tool's response includes `attempt` and `maxAttempts` so you don't need to track an attempt counter yourself \u2014 the server reads it from on-disk history per API.\n\n Your job is to make v3 produce the same response as v2 \u2014 not to \"try a few times and punt to the user\". The user already lived with this API working on v2 with their real data; if v3 doesn't match, that's a regression you introduced, and you fix it here. NEVER leave a `diverged` API for the user to \"Accept divergence\" or \"Try one more attempt\" via the API calls panel \u2014 those buttons exist as escape hatches for situations the policy gate requires (mutations, denied integrations), not as a way for you to skip work.\n\n Synthesize `inputs` the same way you would for `testApi`. Inspect the v2 YAML from step 2 (`apiInputs` / externally-declared parameters) and the v3 TS from step 3 (`input` schema on `run(ctx, input)`). Every name the caller must supply must appear as a top-level key in `inputs` with a realistic value \u2014 a mock, or a real value harvested per the \"Sourcing input values\" rules below \u2014 matching its expected type. This includes two categories \u2014 do not conflate them:\n - **API parameters** \u2014 names declared in `apiInputs` (v2) or the v3 `input` shape (e.g. `sourceContext`, `currentDocument`, `messages`). Pass each parameter name directly as a key in `inputs`. These are what the frontend passes when it invokes the API; they are not unavailable \"runtime bindings.\"\n - **UI bindings and caller context** \u2014 values the API reads from the app that are not API parameters (component values like `Input1.value`/`Select1.selectedItem.id`, table selections like `Table1.selectedRow`, state variables, workflow `body`/`params` not declared as `apiInputs`). Mock each binding the API references that isn't produced by an earlier block.\n\n Both v2 and v3 receive the same `inputs`, so this is what makes the diff meaningful \u2014 `{}` for an API that expects parameters or bindings produces a vacuous `both_failed` that tells you nothing. Errors like `'sourceContext' is not defined` (v2) or v3 input-validation failures for missing required fields mean you omitted keys from `inputs`; synthesize mocks and retry. That is not an environmental failure.\n\n **Sourcing input values \u2014 real app data over invented data.** A value is only \"realistic\" if the system the API talks to actually accepts it. Invented values are fine for self-contained inputs (a search string, a page size, a boolean flag). But when an input must reference something that already exists \u2014 any key the target system looks up, such as a row/record ID, a resource name, or an uploaded-file reference \u2014 do NOT invent it: an invented identifier either fails on both sides or exercises only the not-found/guard-clause path, and neither proves the API works. Source a real value instead, in this order:\n 1. **Harvest from the app's own APIs.** Find a read-only API in this app (prefer zero-input \"list\"/\"get all\" APIs) whose output contains the identifier you need, run it via `testApi`, and take a value from its real response. Chain as needed: a list API yields a real ID \u2192 pass it to the detail API \u2192 its response yields the next identifier. Prefer migrating and verifying zero-input read-only APIs first so their outputs become your pool of real test data.\n 2. **Follow the client code.** The `useApi`/`useApiData`/`executeApi` call sites under `client/` (and the v2 pages under `scratch/v2-backup/`) show where the app gets each input \u2014 a table's selected row, a route param, a select populated by another API. Trace that source to a concrete real value.\n 3. Only if neither path yields a value, fall back to an invented mock \u2014 and treat any resulting not-found/empty output as unverified (see the vacuous-pass rule under `passed` below).\n\n Outcomes and how to resolve them:\n - **`passed`**: `passed` only proves v2 and v3 agree on the inputs you supplied. Before accepting it, check what actually ran: if the shared output is a sad-path/guard-clause result \u2014 a not-found or error object from a caught exception, a validity flag that came back false, an empty result from a lookup that should have matched \u2014 the API's core logic never executed and the pass is vacuous. In that case source real inputs (per the sourcing rules above) and re-run `runMigrationVerification`; only accept a sad-path-only pass after the sourcing steps genuinely produced no usable value. Once a non-vacuous `passed` is in hand, call `build_manageChecklist` with `status: \"completed\"`. Done. If instead you are marking `completed` on an exhausted sourcing attempt (the pass stayed vacuous), you MUST also set `failureReason` to a string beginning `vacuous_pass_accepted:` followed by a one-line reason \u2014 this sentinel is the only durable signal that survives into the final sweep (and across sub-agents, whose chat context the orchestrator cannot see).\n\n **Platform gate:** the checklist store rejects `status: \"completed\"` on `api_<ApiName>` items until verification has run at least once (`lastVerificationAt` is set \u2014 normally via this tool). You cannot bulk-mark APIs complete without calling `runMigrationVerification` per API.\n - **`skipped_mutation`**: the runtime marks the checklist item `completed` with `verificationSkippedReason: \"mutation\"` automatically. Do **NOT** keep it `in_progress` or ask the user a grouped decision. Continue translating each API as well as you can from the code; the user will verify mutation-skipped APIs manually at finalize time.\n\n - **`v2_unrunnable`**: the v2 backup itself couldn't execute, so there is no baseline to match. Call `build_manageChecklist` with `status: \"completed\"` and continue.\n\n - **`timeout`**: a pipeline stalled past the reporting timeout (`side` says which one). This is transient environment noise \u2014 a dropped editor connection or a backgrounded browser tab \u2014 NOT evidence about the API. Do **NOT** mark the item `completed`. Retry `runMigrationVerification` after finishing your current API. If it times out repeatedly, tell the user verification is stalling (likely tab backgrounded / connection dropped), ask them to keep the app tab focused, and retry \u2014 never convert repeated timeouts into `completed`.\n\n - **`disconnected`**: the editor connection was down when the pipeline tried to run (`side` says which one) \u2014 the app tab is closed, backgrounded, or reconnecting. Unlike `timeout` this diagnosis is certain, and it says nothing about the API. Do **NOT** mark the item `completed`. Tell the user the app tab must stay open and focused during verification, then retry `runMigrationVerification` \u2014 never convert `disconnected` into `completed`.\n\n - **`both_failed`**:\n - If you passed `inputs: {}` or omitted declared API parameters / bindings: synthesize real `inputs` (per the bullet above) and retry. Do NOT mark completed here \u2014 missing inputs caused this, not a real failure.\n - If the error names a symbol that appears in `apiInputs` or the v3 `input` schema: add that key to `inputs` with a realistic mock and retry. Do NOT classify this as environmental \u2014 the parameter is available; you did not supply it.\n - If you passed real inputs and v2 still failed: read v2's error. If it is environmental (auth/credentials/integration-unavailable: \"no integration configured\", \"401\", \"ECONNREFUSED\", \"missing API key\"), v2 cannot run in this verification context regardless of what you do \u2014 call `build_manageChecklist` with `status: \"completed\"` and continue; the user will verify manually. If it's a v2 runtime error that v3 also reproduces, treat as `diverged` (your inputs are wrong, or the API expects state that isn't reproducible here \u2014 revise inputs and retry).\n\n - **`error`**: a system error in the verification flow itself (cannot read v3 source, etc.). Fix what you can; if it persists across one retry, mark the item `failed` with `failureReason: \"<error detail>\"` and continue.\n\n- **`diverged`**: the API ran on both sides and the outputs differ. **Iterate until v3 matches v2 byte-for-byte (modulo the normalizations api-comparator already applies: identical ISO timestamps, near-now ISO timestamps within \u00B160s of each other and \u00B15min of \"now\", UUIDs, monotonic integer IDs differing by \u22641 on fields whose leaf ends with \"id\", floats within 1e-9 relative tolerance, and per-request opaque identifiers at leaves named `requestId`/`traceId`/`spanId`/`correlationId`/`sessionId`/`nonce`/`csrfToken`/`_id` \u2014 note: a bare leaf named `token` is intentionally NOT normalized, since auth/access-token divergence is a security-relevant signal we want to surface).**\n\n There is one bounded judgment exception: if both v2 and v3 executions succeeded, the output shape/types are equivalent, and the remaining diffs are value-level fields you reasonably infer are inherently non-deterministic outputs for this API, treat the migration as semantically complete for this run. In that case, mark the checklist item `completed` and continue.\n\n Use this exception only when all of the following are true:\n - The divergence is not structural (no missing/extra fields, no type mismatch, no changed nesting/cardinality, no changed error/status behavior).\n - The differing fields are expected to vary per invocation and do not change control flow decisions.\n - The API's functional contract remains intact for downstream consumers.\n\n If any of those conditions are not met, the divergence is semantic and must be fixed \u2014 do NOT add code purely to mask values the comparator already tolerates.\n\n For each `summaryForAgent` entry \u2014 every entry names the JSON path that diverged and the v2/v3 values \u2014 find the block in `server/apis/<ApiName>/api.ts` that produces that path and fix the discrepancy. Common causes, in rough order of frequency:\n - wrong column projection or missing field in a SELECT\n - missing transform / output shape mismatch (array vs object wrapper, single vs list)\n - off-by-one or wrong placeholder in a SQL parameter\n - mistyped binding key (case, plural, dotted path)\n - type coercion (string vs number, null vs undefined, boolean vs \"true\")\n - missing default value when the binding is undefined\n - wrong join order or implicit ordering of rows\n - filter/where condition different from v2\n - missing `LIMIT`, `OFFSET`, or pagination handling\n\n If a divergence stems from the `inputs` you passed (e.g. v2 echoes the input one way and v3 a different way because they normalize it differently), revise the inputs alongside the source.\n\n Then call `build_debug` (fix any failures), then `runMigrationVerification` again with the revised source/inputs.\n\n **There is no fixed retry budget \u2014 iterate until `passed` or until you have firm structural evidence that no v3 source change can reconcile v2 and v3.** Acceptable evidence for stopping: v2 references a YAML construct with no v3 SDK equivalent and the divergence is in that construct; v2 derives a value from runtime state v3 cannot observe (e.g. an env var that differs by environment). When you stop on structural grounds, mark the item `failed` with `failureReason: \"verification_diverged_irreconcilable: <one-paragraph reason naming the specific summaryForAgent entry and the structural reason> | summary: <JSON.stringify(summaryForAgent)>\"`.\n\n Guardrail against infinite loops (NOT a retry budget): if you have iterated 8 times on the same API and the set of divergences has not materially changed across the last 3 attempts (i.e. you are flailing, not converging), stop and mark `failed` with the same `failureReason` shape. If you ARE converging \u2014 fewer divergences each attempt, or the remaining ones look smaller \u2014 keep going past 8.\n\n## Run-level rules\n\n- Do NOT emit a free-text UNRESOLVED report \u2014 every unresolved item must be a `failed` checklist entry with a `failureReason`.\n- Do NOT stop the run because one API failed. Only stop once every API is either `completed` or `failed`.\n\n## Updating integration write policy (Clark-only, with user consent)\n\nDuring migration, integration write policy (`allow` / `deny` per integration UUID) lives in `scratch/migration-state.json` \u2192 `integrationWritePolicy`. The pre-migration modal sets it once; **there is no mid-migration UI for the user to change it.**\n\n- **Only Clark can update write policy mid-migration**, and only when the user **explicitly asks** (e.g. \"test this API even though I set Don't test\", \"go ahead and run writes against Postgres\").\n- **Never** tell the user to update write policy in the UI \u2014 they cannot.\n- **Never** hand-edit `scratch/migration-state.json` with `build_writeFile` / `build_editFile` to change write policy. Use `updateMigrationWritePolicy` instead; it validates input and shows a permission card when granting `allow`.\n- To find integration ids: read the `const` UUID at the top of `server/apis/<ApiName>/api.ts`, or read existing keys from `scratch/migration-state.json` \u2192 `integrationWritePolicy`.\n- After updating policy to `allow` (with user approval), call `testApi` or `runMigrationVerification` again for the affected APIs.\n\nIf `testApi` is blocked by access control because an integration is still `deny`, explain that the user previously chose \"Don't test\" for that integration and ask whether they want you to update the policy and retry \u2014 do not suggest they change settings in the UI.\n\n## User-reported manual verification\n\nSome APIs are marked `completed` without automatic testing \u2014 write-policy \"Don't test\" (`verificationSkippedReason: \"mutation\"`) or a `testApi` permission denial (`verificationSkippedReason: \"testing_denied\"`). The user is expected to verify these by using the app themselves.\n\n### When the user asks how to test a skipped API\n\nIf the user requests manual test instructions for a specific API (including when the UI sends a prompt referencing `skills/system/migration-api-verification/SKILL.md`):\n\n1. Read `skills/system/migration-api-verification/SKILL.md` and follow it.\n2. Produce UI click-through steps for that API \u2014 do **NOT** call `testApi` or `runMigrationVerification`.\n\n### When the user confirms they verified an API\n\nIf the user tells you they have checked such an API and it works (e.g. \"I clicked through the app and GetOrders works, mark it complete\"), call `markApiManuallyVerified({ apiName })`. Do **NOT** call `testApi` or `runMigrationVerification` for it \u2014 the user opted out of automatic testing. This records the API as user-verified so it no longer appears in the list of APIs needing manual verification.\n\nIf the user instead asks you to **test** a denied or skipped API (not merely confirm they tested it manually), call `updateMigrationWritePolicy` first to grant `allow` for the relevant integration ids, then run `testApi` or `runMigrationVerification`.\n\n## Post-batch re-verification\n\nAfter `spawnCodingSubagents` returns (or after all sequential APIs are settled), before updating the checklist overall status:\n\n1. Call `build_manageChecklist` with `action: \"get\"` and read all items.\n2. For any item where `verificationOutcome === \"auto_passed\"` and `lastVerificationAt` is older than any sibling item's `updatedAt` minus 60 seconds: this item may have drifted due to registry edits made by later sub-agents. Re-run `runMigrationVerification({ apiName, inputs: <inputs> })` for each such item \u2014 synthesize `inputs` the same way as in step 6 of the per-API procedure, including the \"Sourcing input values\" rule (harvest real IDs from the app's read-only APIs; do not invent identifiers).\n3. If any re-verification produces `diverged`, apply the normal per-API diverge\u2192retry loop (no fixed retry budget; iterate until `passed` or firm structural-irreconcilability evidence, with the anti-flail guardrail at 8 iterations without convergence).\n4. Proceed to mark overall checklist completed/failed only after all re-verifications settle and the final sweep below is done.\n\n## Final sweep: retry unverified happy paths with real app data (required once per run)\n\nIf your closing summary would say \"I could not verify the happy path for X\" \u2014 that sentence is a trigger to do more work, not a conclusion to hand the user. Before marking the overall checklist completed/failed, do one last pass:\n\n1. From the checklist, collect every API that (a) is `failed` or unresolved because its test never succeeded, or (b) was marked `completed` but carries the `vacuous_pass_accepted:` sentinel in `failureReason` (set by you or by a sub-agent when a sad-path-only pass was accepted after exhausting sourcing).\n\n Note on the parallel path: for APIs a sub-agent migrated, you cannot see its chat context or reconstruct its verification outputs \u2014 `verificationOutcome` records only the outcome type (`auto_passed`), not whether the pass was vacuous. The `vacuous_pass_accepted:` sentinel is therefore the ONLY way a sub-agent's vacuous pass surfaces here; APIs a sub-agent completed without it are treated as genuinely verified. This is why the sub-agent `instructions` MUST restate the vacuous-pass rule and the sentinel (see Parallelization policy). If you have specific reason to distrust a sub-agent's `completed` items (e.g. its batch reported many `both_failed` retries), re-verify them here with harvested real inputs rather than trusting the bare `completed`.\n\n2. For each, harvest real inputs from the app itself per the \"Sourcing input values\" rule in the per-API procedure: run the app's read-only APIs via `testApi` (zero-input list APIs first) and chain their real outputs into the inputs of the API under test.\n3. Re-run `runMigrationVerification` with those real inputs and resolve the outcome per the normal per-API rules.\n4. Only APIs whose sourcing steps genuinely produced no usable value (or whose failures are environmental per the `both_failed` rules) may finish the run without a happy-path verification \u2014 and each needs a `failureReason` or verification record saying so.\n";
2
2
  //# sourceMappingURL=skill.generated.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"skill.generated.d.ts","sourceRoot":"","sources":["../../../../../src/ai-service/skills/system/superblocks-migration/skill.generated.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,OAAO,8gtCAoTnB,CAAC"}
1
+ {"version":3,"file":"skill.generated.d.ts","sourceRoot":"","sources":["../../../../../src/ai-service/skills/system/superblocks-migration/skill.generated.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,OAAO,m+tCAoTnB,CAAC"}
@@ -33,10 +33,10 @@ Corollary for \`migration_page_route_verification_*\`: see the _Page route verif
33
33
 
34
34
  Authoritative. If a rule here conflicts with your priors, these win.
35
35
 
36
- 1. \`node_modules/@superblocksteam/sdk-api/README.md\` — whole thing (api() contract, execution model, \`ctx.*\`, exports, error classes, performance best practices, \`useApi\` frontend hook, integration method table).
36
+ 1. The installed SDK root documentation through \`getSdkApiDocs\` — call it with no arguments for the current section index, then retrieve every top-level section (following every \`root.continuation\`) so the full api() contract, execution model, \`ctx.*\`, exports, error classes, performance best practices, \`useApi\` frontend hook, and integration method table are covered without loading the whole README at once.
37
37
  2. \`node_modules/@superblocksteam/sdk-api/src/index.ts\` (the **barrel**) — the authoritative list of exported factory names. Integration factory names are whatever this file exports, not whatever the directory name is. Never invent or case-fold a factory name without confirming it here.
38
- 3. The README for **every integration** referenced by the YAMLs you convert:
39
- - \`node_modules/@superblocksteam/sdk-api/src/integrations/<kind>/README.md\`
38
+ 3. Version-aware documentation for **every integration** referenced by the YAMLs you convert:
39
+ - Call \`getSdkApiDocs\` with that integration's ID (or plugin ID when no integration ID is available).
40
40
  - If a vendor-specific factory exists for the target service (e.g., a dedicated one for a given LLM provider), prefer it over a generic HTTP one.
41
41
  4. \`skills/system/superblocks-migration/references/yaml-block-mapping.md\` — full YAML → TS mapping for each legacy block type.
42
42
 
@@ -70,14 +70,14 @@ Complete this orientation sequence before writing or modifying any API file:
70
70
  - Do **not** scan imports yourself, edit \`package.json\`, or add packages outside \`recommendedUserDeps\`. If you believe a package is missing from the list, that is a platform bug: mark \`migration_dependency_verification\` as \`failed\` with \`failureReason: "platform bug: <pkg> imported by <file> but absent from recommendedUserDeps"\` and continue with API work using the packages that did install. (\`build_manageChecklist\` has no \`note\` action — \`failed\` with a structured \`failureReason\` is the only way to record this.)
71
71
  1. Call \`build_manageChecklist\` with \`action: "get"\` and filter to \`status: "pending"\` and API-migration origins (\`origin: "2.0-upgrade"\` or legacy \`origin: "seed_api"\`) — this is your authoritative API list and count for this turn.
72
72
  2. Read \`node_modules/@superblocksteam/sdk-api/src/index.ts\` and treat it as the **only** source of truth for sdk-api export/factory names.
73
- 3. Read \`node_modules/@superblocksteam/sdk-api/README.md\`.
74
- 4. For each API, read only the integration README(s) needed for that API right before migrating it (just-in-time). Do **not** preload every integration README for the entire app.
73
+ 3. Call \`getSdkApiDocs()\` for the installed root README index, then retrieve every top-level section and follow every continuation before editing. This is the progressive equivalent of reading the whole root README.
74
+ 4. For each API, call \`getSdkApiDocs\` only for the integration(s) needed by that API right before migrating it (just-in-time). Do **not** preload every integration's documentation for the entire app.
75
75
 
76
76
  Hard constraints:
77
77
 
78
78
  - Do **not** rely on prior conversation memory, "knowledge" summaries, or inferred export names.
79
79
  - Do **not** start editing API files until preflight steps 0–3 are complete.
80
- - If a required file cannot be read, mark that API's checklist item \`failed\` with a concrete \`failureReason\` that names the missing path.
80
+ - If required documentation cannot be retrieved, mark that API's checklist item \`failed\` with a concrete \`failureReason\` that names the failed \`getSdkApiDocs\` selector or integration ID.
81
81
 
82
82
  ## File layout & exports
83
83
 
@@ -0,0 +1,2 @@
1
+ export declare const content = "---\nname: superblocks-sdk-api\ndescription: |\n Retrieve authoritative SDK API and integration guidance progressively before creating or editing SDK APIs.\nreadOnly: true\nmetadata:\n author: superblocks\n version: \"1.0\"\n---\n\n# SDK API documentation workflow\n\nUse `getSdkApiDocs` as the authoritative SDK documentation interface. Do not read SDK README files directly and do not fetch newer documentation from the internet: the tool reads the installed `@superblocksteam/sdk-api` package and applies plugin/orchestrator version overlays.\n\nThe root README reflects that installed package and is not proof that every statement matches a customer's execution runtime. Plugin documentation remains compatibility-aware: the tool resolves overlays using both the integration execution version and the orchestrator's `javascriptsdkapi` version.\n\n## Before writing or modifying an SDK API\n\n1. Call `getSdkApiDocs` with the app's integration IDs whenever available. Use the returned version-aware plugin guidance for integration methods and signatures.\n2. For shared SDK rules, call `getSdkApiDocs()` with no arguments. This returns a compact index derived from the installed root README, not the full document.\n3. Request only the relevant root entry with `getSdkApiDocs({ section: \"<id>\" })`.\n4. If the result contains `root.continuation`, retrieve the rest with `getSdkApiDocs({ continuation: \"<exact token>\" })`. Continue until no token is returned. Never repeat the original section call to get the next chunk.\n5. If a section or continuation is stale or unknown, use the refreshed index in the response and restart from the current section ID.\n\nKeep exact `section`, `continuation`, integration ID, and plugin ID arguments when refetching after compaction. SDK signatures, examples, and detailed implementation rules remain in `getSdkApiDocs`; do not infer or duplicate them from this skill.\n";
2
+ //# sourceMappingURL=skill.generated.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skill.generated.d.ts","sourceRoot":"","sources":["../../../../../src/ai-service/skills/system/superblocks-sdk-api/skill.generated.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,OAAO,k4DAyBnB,CAAC"}
@@ -0,0 +1,29 @@
1
+ // Auto-generated from src/ai-service/skills/system/superblocks-sdk-api/SKILL.md
2
+ // Do not edit directly - edit the .md file instead
3
+ export const content = `---
4
+ name: superblocks-sdk-api
5
+ description: |
6
+ Retrieve authoritative SDK API and integration guidance progressively before creating or editing SDK APIs.
7
+ readOnly: true
8
+ metadata:
9
+ author: superblocks
10
+ version: "1.0"
11
+ ---
12
+
13
+ # SDK API documentation workflow
14
+
15
+ Use \`getSdkApiDocs\` as the authoritative SDK documentation interface. Do not read SDK README files directly and do not fetch newer documentation from the internet: the tool reads the installed \`@superblocksteam/sdk-api\` package and applies plugin/orchestrator version overlays.
16
+
17
+ The root README reflects that installed package and is not proof that every statement matches a customer's execution runtime. Plugin documentation remains compatibility-aware: the tool resolves overlays using both the integration execution version and the orchestrator's \`javascriptsdkapi\` version.
18
+
19
+ ## Before writing or modifying an SDK API
20
+
21
+ 1. Call \`getSdkApiDocs\` with the app's integration IDs whenever available. Use the returned version-aware plugin guidance for integration methods and signatures.
22
+ 2. For shared SDK rules, call \`getSdkApiDocs()\` with no arguments. This returns a compact index derived from the installed root README, not the full document.
23
+ 3. Request only the relevant root entry with \`getSdkApiDocs({ section: "<id>" })\`.
24
+ 4. If the result contains \`root.continuation\`, retrieve the rest with \`getSdkApiDocs({ continuation: "<exact token>" })\`. Continue until no token is returned. Never repeat the original section call to get the next chunk.
25
+ 5. If a section or continuation is stale or unknown, use the refreshed index in the response and restart from the current section ID.
26
+
27
+ Keep exact \`section\`, \`continuation\`, integration ID, and plugin ID arguments when refetching after compaction. SDK signatures, examples, and detailed implementation rules remain in \`getSdkApiDocs\`; do not infer or duplicate them from this skill.
28
+ `;
29
+ //# sourceMappingURL=skill.generated.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skill.generated.js","sourceRoot":"","sources":["../../../../../src/ai-service/skills/system/superblocks-sdk-api/skill.generated.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,mDAAmD;AAEnD,MAAM,CAAC,MAAM,OAAO,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;CAyBtB,CAAC"}