@synap-core/cli 1.12.2 → 1.13.1-next.20261102.ec3d166

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 (354) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +38 -11
  3. package/dist/build-info.json +2 -3
  4. package/dist/commands/agents.d.ts +0 -1
  5. package/dist/commands/agents.js +13 -3
  6. package/dist/commands/agents.js.map +1 -1
  7. package/dist/commands/app.js +2 -1
  8. package/dist/commands/app.js.map +1 -1
  9. package/dist/commands/automation.js +46 -41
  10. package/dist/commands/automation.js.map +1 -1
  11. package/dist/commands/box.d.ts +42 -0
  12. package/dist/commands/box.js +89 -0
  13. package/dist/commands/box.js.map +1 -0
  14. package/dist/commands/bridge-setup.js +3 -1
  15. package/dist/commands/bridge-setup.js.map +1 -1
  16. package/dist/commands/capability.d.ts +87 -1
  17. package/dist/commands/capability.js +174 -64
  18. package/dist/commands/capability.js.map +1 -1
  19. package/dist/commands/close-questions.d.ts +27 -0
  20. package/dist/commands/close-questions.js +49 -0
  21. package/dist/commands/close-questions.js.map +1 -0
  22. package/dist/commands/close-questions.test.d.ts +1 -0
  23. package/dist/commands/close-questions.test.js +52 -0
  24. package/dist/commands/close-questions.test.js.map +1 -0
  25. package/dist/commands/connect.d.ts +1 -3
  26. package/dist/commands/connect.js +2 -2
  27. package/dist/commands/connect.js.map +1 -1
  28. package/dist/commands/context.js +72 -43
  29. package/dist/commands/context.js.map +1 -1
  30. package/dist/commands/data-extra.d.ts +13 -0
  31. package/dist/commands/data-extra.js +35 -6
  32. package/dist/commands/data-extra.js.map +1 -1
  33. package/dist/commands/data.js +2 -1
  34. package/dist/commands/data.js.map +1 -1
  35. package/dist/commands/doctor.d.ts +39 -0
  36. package/dist/commands/doctor.js +136 -14
  37. package/dist/commands/doctor.js.map +1 -1
  38. package/dist/commands/hooks.d.ts +19 -0
  39. package/dist/commands/hooks.js +70 -0
  40. package/dist/commands/hooks.js.map +1 -0
  41. package/dist/commands/import.d.ts +25 -0
  42. package/dist/commands/import.js +98 -12
  43. package/dist/commands/import.js.map +1 -1
  44. package/dist/commands/init.d.ts +14 -0
  45. package/dist/commands/init.js +63 -21
  46. package/dist/commands/init.js.map +1 -1
  47. package/dist/commands/keys-request.d.ts +48 -0
  48. package/dist/commands/keys-request.js +236 -0
  49. package/dist/commands/keys-request.js.map +1 -0
  50. package/dist/commands/keys-request.test.d.ts +1 -0
  51. package/dist/commands/keys-request.test.js +176 -0
  52. package/dist/commands/keys-request.test.js.map +1 -0
  53. package/dist/commands/knowledge.d.ts +6 -0
  54. package/dist/commands/knowledge.js +13 -3
  55. package/dist/commands/knowledge.js.map +1 -1
  56. package/dist/commands/lens.js +2 -1
  57. package/dist/commands/lens.js.map +1 -1
  58. package/dist/commands/market-authoring.js +1 -1
  59. package/dist/commands/market-authoring.js.map +1 -1
  60. package/dist/commands/market.d.ts +61 -4
  61. package/dist/commands/market.js +106 -48
  62. package/dist/commands/market.js.map +1 -1
  63. package/dist/commands/mcp.d.ts +1 -1
  64. package/dist/commands/mcp.js +14 -9
  65. package/dist/commands/mcp.js.map +1 -1
  66. package/dist/commands/observe.category.test.d.ts +1 -0
  67. package/dist/commands/observe.category.test.js +59 -0
  68. package/dist/commands/observe.category.test.js.map +1 -0
  69. package/dist/commands/observe.d.ts +3 -0
  70. package/dist/commands/observe.js +7 -5
  71. package/dist/commands/observe.js.map +1 -1
  72. package/dist/commands/observe.title.test.d.ts +1 -0
  73. package/dist/commands/observe.title.test.js +37 -0
  74. package/dist/commands/observe.title.test.js.map +1 -0
  75. package/dist/commands/playbook.js +9 -0
  76. package/dist/commands/playbook.js.map +1 -1
  77. package/dist/commands/pods.d.ts +6 -1
  78. package/dist/commands/pods.js +38 -17
  79. package/dist/commands/pods.js.map +1 -1
  80. package/dist/commands/proposals-actions.js +7 -1
  81. package/dist/commands/proposals-actions.js.map +1 -1
  82. package/dist/commands/proton-setup.js +3 -1
  83. package/dist/commands/proton-setup.js.map +1 -1
  84. package/dist/commands/providers.d.ts +22 -6
  85. package/dist/commands/providers.js +39 -32
  86. package/dist/commands/providers.js.map +1 -1
  87. package/dist/commands/raycast.d.ts +15 -0
  88. package/dist/commands/raycast.js +36 -10
  89. package/dist/commands/raycast.js.map +1 -1
  90. package/dist/commands/session-feed-wait.d.ts +48 -0
  91. package/dist/commands/session-feed-wait.js +261 -0
  92. package/dist/commands/session-feed-wait.js.map +1 -0
  93. package/dist/commands/session-post.d.ts +40 -0
  94. package/dist/commands/session-post.js +96 -0
  95. package/dist/commands/session-post.js.map +1 -0
  96. package/dist/commands/session-wait.d.ts +9 -4
  97. package/dist/commands/session-wait.js +20 -5
  98. package/dist/commands/session-wait.js.map +1 -1
  99. package/dist/commands/sessions.d.ts +2 -0
  100. package/dist/commands/sessions.js +23 -0
  101. package/dist/commands/sessions.js.map +1 -1
  102. package/dist/commands/skills-commands.d.ts +2 -0
  103. package/dist/commands/skills-commands.js +9 -1
  104. package/dist/commands/skills-commands.js.map +1 -1
  105. package/dist/commands/skills-commands.test.d.ts +1 -0
  106. package/dist/commands/skills-commands.test.js +57 -0
  107. package/dist/commands/skills-commands.test.js.map +1 -0
  108. package/dist/commands/status.js +13 -7
  109. package/dist/commands/status.js.map +1 -1
  110. package/dist/commands/statusline.backoff.test.js +13 -3
  111. package/dist/commands/statusline.backoff.test.js.map +1 -1
  112. package/dist/commands/statusline.d.ts +104 -9
  113. package/dist/commands/statusline.js +392 -148
  114. package/dist/commands/statusline.js.map +1 -1
  115. package/dist/commands/statusline.line2.test.d.ts +15 -0
  116. package/dist/commands/statusline.line2.test.js +226 -0
  117. package/dist/commands/statusline.line2.test.js.map +1 -0
  118. package/dist/commands/statusline.needs-you.test.d.ts +1 -0
  119. package/dist/commands/statusline.needs-you.test.js +17 -0
  120. package/dist/commands/statusline.needs-you.test.js.map +1 -0
  121. package/dist/commands/statusline.now.test.d.ts +10 -0
  122. package/dist/commands/statusline.now.test.js +250 -0
  123. package/dist/commands/statusline.now.test.js.map +1 -0
  124. package/dist/commands/tracks.d.ts +68 -6
  125. package/dist/commands/tracks.js +272 -16
  126. package/dist/commands/tracks.js.map +1 -1
  127. package/dist/commands/upload.js +12 -0
  128. package/dist/commands/upload.js.map +1 -1
  129. package/dist/commands/wait.d.ts +124 -0
  130. package/dist/commands/wait.js +384 -0
  131. package/dist/commands/wait.js.map +1 -0
  132. package/dist/commands/wait.test.d.ts +8 -0
  133. package/dist/commands/wait.test.js +308 -0
  134. package/dist/commands/wait.test.js.map +1 -0
  135. package/dist/commands/whoami.d.ts +7 -0
  136. package/dist/commands/whoami.js +17 -0
  137. package/dist/commands/whoami.js.map +1 -1
  138. package/dist/commands/worker-accounts.d.ts +149 -0
  139. package/dist/commands/worker-accounts.js +339 -0
  140. package/dist/commands/worker-accounts.js.map +1 -0
  141. package/dist/commands/worker-accounts.test.d.ts +1 -0
  142. package/dist/commands/worker-accounts.test.js +224 -0
  143. package/dist/commands/worker-accounts.test.js.map +1 -0
  144. package/dist/commands/worker-github-app.d.ts +63 -0
  145. package/dist/commands/worker-github-app.js +161 -0
  146. package/dist/commands/worker-github-app.js.map +1 -0
  147. package/dist/commands/worker-github-app.test.d.ts +1 -0
  148. package/dist/commands/worker-github-app.test.js +102 -0
  149. package/dist/commands/worker-github-app.test.js.map +1 -0
  150. package/dist/commands/worker-github-broker.d.ts +39 -0
  151. package/dist/commands/worker-github-broker.js +106 -0
  152. package/dist/commands/worker-github-broker.js.map +1 -0
  153. package/dist/commands/worker-github-broker.test.d.ts +1 -0
  154. package/dist/commands/worker-github-broker.test.js +90 -0
  155. package/dist/commands/worker-github-broker.test.js.map +1 -0
  156. package/dist/commands/worker-runs.test.d.ts +1 -0
  157. package/dist/commands/worker-runs.test.js +150 -0
  158. package/dist/commands/worker-runs.test.js.map +1 -0
  159. package/dist/commands/worker-worktrees.d.ts +34 -0
  160. package/dist/commands/worker-worktrees.js +87 -0
  161. package/dist/commands/worker-worktrees.js.map +1 -0
  162. package/dist/commands/worker.d.ts +406 -10
  163. package/dist/commands/worker.js +1180 -126
  164. package/dist/commands/worker.js.map +1 -1
  165. package/dist/commands/worker.test.js +588 -11
  166. package/dist/commands/worker.test.js.map +1 -1
  167. package/dist/index.js +220 -28
  168. package/dist/index.js.map +1 -1
  169. package/dist/lib/agent-door.d.ts +1 -3
  170. package/dist/lib/agent-door.js +10 -7
  171. package/dist/lib/agent-door.js.map +1 -1
  172. package/dist/lib/agents-config.d.ts +9 -0
  173. package/dist/lib/agents-config.js +20 -0
  174. package/dist/lib/agents-config.js.map +1 -1
  175. package/dist/lib/atomic-file.d.ts +20 -1
  176. package/dist/lib/atomic-file.js +100 -2
  177. package/dist/lib/atomic-file.js.map +1 -1
  178. package/dist/lib/auth.d.ts +28 -2
  179. package/dist/lib/auth.js +52 -12
  180. package/dist/lib/auth.js.map +1 -1
  181. package/dist/lib/box/egress.d.ts +82 -0
  182. package/dist/lib/box/egress.js +264 -0
  183. package/dist/lib/box/egress.js.map +1 -0
  184. package/dist/lib/box/evidence.d.ts +86 -0
  185. package/dist/lib/box/evidence.js +121 -0
  186. package/dist/lib/box/evidence.js.map +1 -0
  187. package/dist/lib/box/job-server.d.ts +72 -0
  188. package/dist/lib/box/job-server.js +250 -0
  189. package/dist/lib/box/job-server.js.map +1 -0
  190. package/dist/lib/box/provisioner.d.ts +83 -0
  191. package/dist/lib/box/provisioner.js +312 -0
  192. package/dist/lib/box/provisioner.js.map +1 -0
  193. package/dist/lib/box/proxmox.d.ts +112 -0
  194. package/dist/lib/box/proxmox.js +211 -0
  195. package/dist/lib/box/proxmox.js.map +1 -0
  196. package/dist/lib/box/wire.d.ts +98 -0
  197. package/dist/lib/box/wire.js +70 -0
  198. package/dist/lib/box/wire.js.map +1 -0
  199. package/dist/lib/bridge-invocation.test.d.ts +1 -0
  200. package/dist/lib/bridge-invocation.test.js +75 -0
  201. package/dist/lib/bridge-invocation.test.js.map +1 -0
  202. package/dist/lib/bridge-status.d.ts +144 -0
  203. package/dist/lib/bridge-status.js +227 -0
  204. package/dist/lib/bridge-status.js.map +1 -0
  205. package/dist/lib/bridge-status.test.d.ts +6 -0
  206. package/dist/lib/bridge-status.test.js +197 -0
  207. package/dist/lib/bridge-status.test.js.map +1 -0
  208. package/dist/lib/client-header.test.d.ts +1 -0
  209. package/dist/lib/client-header.test.js +103 -0
  210. package/dist/lib/client-header.test.js.map +1 -0
  211. package/dist/lib/conversation-headers.d.ts +89 -9
  212. package/dist/lib/conversation-headers.js +156 -3
  213. package/dist/lib/conversation-headers.js.map +1 -1
  214. package/dist/lib/conversation-headers.test.d.ts +1 -0
  215. package/dist/lib/conversation-headers.test.js +78 -0
  216. package/dist/lib/conversation-headers.test.js.map +1 -0
  217. package/dist/lib/credential-class.d.ts +40 -2
  218. package/dist/lib/credential-class.js +48 -6
  219. package/dist/lib/credential-class.js.map +1 -1
  220. package/dist/lib/device-connect.d.ts +45 -0
  221. package/dist/lib/device-connect.js +60 -0
  222. package/dist/lib/device-connect.js.map +1 -1
  223. package/dist/lib/exporter-coverage.d.ts +1 -1
  224. package/dist/lib/exporter-coverage.js +1 -1
  225. package/dist/lib/goal-words.d.ts +38 -0
  226. package/dist/lib/goal-words.js +115 -0
  227. package/dist/lib/goal-words.js.map +1 -0
  228. package/dist/lib/hub-client.d.ts +87 -8
  229. package/dist/lib/hub-client.js +205 -70
  230. package/dist/lib/hub-client.js.map +1 -1
  231. package/dist/lib/init-handoff.d.ts +8 -0
  232. package/dist/lib/init-handoff.js +9 -2
  233. package/dist/lib/init-handoff.js.map +1 -1
  234. package/dist/lib/key-locations.d.ts +3 -3
  235. package/dist/lib/key-locations.js +3 -39
  236. package/dist/lib/key-locations.js.map +1 -1
  237. package/dist/lib/key-source.d.ts +3 -1
  238. package/dist/lib/key-source.js +7 -4
  239. package/dist/lib/key-source.js.map +1 -1
  240. package/dist/lib/knowledge-title.d.ts +3 -2
  241. package/dist/lib/knowledge-title.js +3 -2
  242. package/dist/lib/knowledge-title.js.map +1 -1
  243. package/dist/lib/machine-instance.d.ts +7 -5
  244. package/dist/lib/machine-instance.js +7 -5
  245. package/dist/lib/machine-instance.js.map +1 -1
  246. package/dist/lib/mcp-bridge.d.ts +50 -0
  247. package/dist/lib/mcp-bridge.js +232 -11
  248. package/dist/lib/mcp-bridge.js.map +1 -1
  249. package/dist/lib/mcp-bridge.test.js +224 -4
  250. package/dist/lib/mcp-bridge.test.js.map +1 -1
  251. package/dist/lib/mcp-call.d.ts +5 -1
  252. package/dist/lib/mcp-call.js +7 -6
  253. package/dist/lib/mcp-call.js.map +1 -1
  254. package/dist/lib/mcp-connector-url.test.d.ts +1 -0
  255. package/dist/lib/mcp-connector-url.test.js +13 -0
  256. package/dist/lib/mcp-connector-url.test.js.map +1 -0
  257. package/dist/lib/mcp-snippets.d.ts +16 -0
  258. package/dist/lib/mcp-snippets.js +20 -2
  259. package/dist/lib/mcp-snippets.js.map +1 -1
  260. package/dist/lib/now-line.d.ts +66 -0
  261. package/dist/lib/now-line.js +193 -0
  262. package/dist/lib/now-line.js.map +1 -0
  263. package/dist/lib/now-line.test.d.ts +5 -0
  264. package/dist/lib/now-line.test.js +177 -0
  265. package/dist/lib/now-line.test.js.map +1 -0
  266. package/dist/lib/package-updates.d.ts +66 -0
  267. package/dist/lib/package-updates.js +76 -0
  268. package/dist/lib/package-updates.js.map +1 -0
  269. package/dist/lib/pod-moved.d.ts +70 -0
  270. package/dist/lib/pod-moved.js +146 -0
  271. package/dist/lib/pod-moved.js.map +1 -0
  272. package/dist/lib/pod-reach.d.ts +39 -0
  273. package/dist/lib/pod-reach.js +117 -0
  274. package/dist/lib/pod-reach.js.map +1 -0
  275. package/dist/lib/pod-reach.test.d.ts +1 -0
  276. package/dist/lib/pod-reach.test.js +67 -0
  277. package/dist/lib/pod-reach.test.js.map +1 -0
  278. package/dist/lib/pod.d.ts +8 -4
  279. package/dist/lib/pod.js +12 -10
  280. package/dist/lib/pod.js.map +1 -1
  281. package/dist/lib/register-claude-code-mcp.test.d.ts +1 -0
  282. package/dist/lib/register-claude-code-mcp.test.js +97 -0
  283. package/dist/lib/register-claude-code-mcp.test.js.map +1 -0
  284. package/dist/lib/review-credential.js +3 -1
  285. package/dist/lib/review-credential.js.map +1 -1
  286. package/dist/lib/safe-open.d.ts +5 -3
  287. package/dist/lib/safe-open.js +9 -8
  288. package/dist/lib/safe-open.js.map +1 -1
  289. package/dist/lib/self-host.d.ts +17 -0
  290. package/dist/lib/self-host.js +24 -0
  291. package/dist/lib/self-host.js.map +1 -0
  292. package/dist/lib/session-feed.d.ts +185 -0
  293. package/dist/lib/session-feed.js +257 -0
  294. package/dist/lib/session-feed.js.map +1 -0
  295. package/dist/lib/targets.d.ts +52 -3
  296. package/dist/lib/targets.js +150 -209
  297. package/dist/lib/targets.js.map +1 -1
  298. package/dist/lib/term.d.ts +24 -0
  299. package/dist/lib/term.js +52 -0
  300. package/dist/lib/term.js.map +1 -0
  301. package/dist/lib/term.test.d.ts +1 -0
  302. package/dist/lib/term.test.js +29 -0
  303. package/dist/lib/term.test.js.map +1 -0
  304. package/dist/lib/transcript-index.d.ts +94 -0
  305. package/dist/lib/transcript-index.js +321 -0
  306. package/dist/lib/transcript-index.js.map +1 -0
  307. package/dist/lib/transcript-index.test.d.ts +10 -0
  308. package/dist/lib/transcript-index.test.js +200 -0
  309. package/dist/lib/transcript-index.test.js.map +1 -0
  310. package/dist/lib/wait-help.d.ts +11 -0
  311. package/dist/lib/wait-help.js +25 -0
  312. package/dist/lib/wait-help.js.map +1 -0
  313. package/dist/lib/wait-state.d.ts +57 -0
  314. package/dist/lib/wait-state.js +114 -0
  315. package/dist/lib/wait-state.js.map +1 -0
  316. package/dist/lib/wake-hook-settings.d.ts +57 -0
  317. package/dist/lib/wake-hook-settings.js +153 -0
  318. package/dist/lib/wake-hook-settings.js.map +1 -0
  319. package/dist/lib/wake-state.d.ts +65 -0
  320. package/dist/lib/wake-state.js +122 -0
  321. package/dist/lib/wake-state.js.map +1 -0
  322. package/package.json +5 -2
  323. package/skills/README.md +1 -1
  324. package/skills/agent-os/SKILL.md +8 -8
  325. package/skills/synap/SKILL.md +178 -88
  326. package/skills/synap/code-skills.md +84 -0
  327. package/skills/synap/concepts.md +38 -20
  328. package/skills/synap/creative-loop.md +1 -1
  329. package/skills/synap/document-embeds.md +17 -1
  330. package/skills/synap/focus-sessions.md +17 -8
  331. package/skills/synap/from-intent.md +33 -17
  332. package/skills/synap/governance-rules.md +3 -1
  333. package/skills/synap/lenses.md +4 -4
  334. package/skills/synap/linking.md +2 -2
  335. package/skills/synap/mental-model.md +1 -1
  336. package/skills/synap/operating-mode-rules.md +1 -1
  337. package/skills/synap/quick-reference.md +2 -1
  338. package/skills/synap/reflexes.md +8 -8
  339. package/skills/synap/viewframe-cells.md +1 -1
  340. package/skills/synap/work-model.md +24 -10
  341. package/skills/synap/workspace-design.md +5 -5
  342. package/skills/synap/writes.md +16 -9
  343. package/skills/synap/writing-titles.md +7 -0
  344. package/skills/synap-app/SKILL.md +148 -66
  345. package/skills/synap-app/creating-the-base-app.md +12 -7
  346. package/skills/synap-app/governance.md +27 -16
  347. package/skills/synap-app/install-commands.md +18 -9
  348. package/skills/synap-app/overview.md +16 -9
  349. package/skills/synap-app/using-the-sdk.md +75 -25
  350. package/skills/synap-market/SKILL.md +1 -1
  351. package/skills/synap-market/governance-and-catalog.md +1 -1
  352. package/skills/synap-schema/SKILL.md +24 -21
  353. package/skills/synap-schema/intent-vocabulary-ssot.md +24 -21
  354. package/templates/box/runner.py +206 -0
@@ -5,38 +5,49 @@ mutates a Synap pod.
5
5
 
6
6
  ## The contract
7
7
 
8
- Every mutation (`entities.create`, `entities.update`, `relations.create`, …)
9
- resolves to one of three outcomes, decided **by the pod**, not by your app:
8
+ Every governed mutation (`entities.create`, `entities.update`, `relations.create`, …)
9
+ has one of three outcomes, decided **by the pod**, not by your app:
10
10
 
11
- | Outcome | Meaning | What your code should do |
11
+ | Outcome | How it arrives | What your code should do |
12
12
  |---|---|---|
13
- | `approved` | applied immediately | proceed |
14
- | `proposed` | queued for the pod owner's review (like a PR) | **surface the review — do NOT treat as failure, do NOT retry** |
15
- | `denied` | the principal may not do this | show why; do not retry blindly |
13
+ | applied | resolves with the procedure's own status (`"created"`, `"updated"`, …) | proceed |
14
+ | proposed | resolves with `status: "proposed"`, `proposalId`, `reviewUrl`, `message` — queued for the pod owner's review (like a PR) | **surface the review — do NOT treat as failure, do NOT retry** |
15
+ | denied | **rejects** with a tRPC `FORBIDDEN` error whose message is the reason | show why; do not retry blindly |
16
16
 
17
17
  ## Why it matters for your code
18
18
 
19
19
  - **Never treat `proposed` as an error.** It is the normal, healthy path for any
20
20
  write that governance decides a human should see first. Retrying a `proposed`
21
21
  write creates duplicates.
22
- - **Never `try/catch` a `proposed` as if it threw.** It's a successful response
23
- with a status, not an exception.
22
+ - **A `proposed` result does not throw.** It is a successful response with a
23
+ status; only a denial throws.
24
24
  - **Surface the review link/id** so the user can approve it. An unreviewed
25
25
  proposal is not yet in the graph — it won't show up in reads until approved.
26
26
 
27
27
  ## Pattern
28
28
 
29
29
  ```ts
30
- const res = await synap.entities.create.mutate(input);
31
- switch (res.status) {
32
- case "approved": /* it's live */ break;
33
- case "proposed": /* tell the user: "queued for review" + show res.proposalId */ break;
34
- case "denied": /* show res.reason; don't retry */ break;
30
+ import { TRPCClientError } from "@trpc/client";
31
+
32
+ try {
33
+ const res = await synap.entities.create.mutate(input);
34
+ if (res.status === "proposed") {
35
+ // tell the user "queued for review"; link res.reviewUrl / res.proposalId.
36
+ // res.entity is null — nothing exists until the proposal is approved.
37
+ } else {
38
+ // "created": res.id / res.entity are live
39
+ }
40
+ } catch (err) {
41
+ if (err instanceof TRPCClientError && err.data?.code === "FORBIDDEN") {
42
+ // denied: show err.message; don't retry
43
+ } else throw err;
35
44
  }
36
45
  ```
37
46
 
38
47
  ## Idempotency
39
48
 
40
- If you must retry a create after a network error, pass an idempotency key where
41
- the API accepts one — a `proposed` write that you retry without one will
42
- duplicate on approval. When unsure, read back before re-creating.
49
+ `entities.create` takes no idempotency key. The pod dedups some repeats on its
50
+ own — an identical agent-authored retry within a short window, and creates that
51
+ match an existing entity by identity (a stable `externalId`, or identifying
52
+ properties) — but neither covers every case. Never resend a `proposed` write,
53
+ and when unsure, read back before re-creating.
@@ -2,11 +2,17 @@
2
2
 
3
3
  ## Scaffold a full app (recommended for a fresh start)
4
4
 
5
+ > **Not published yet.** The scaffolder (`@synap-core/create-synap-app`) is not
6
+ > on npm and the base template is not public, so `npx` returns 404 today. Use the
7
+ > SDK path below until a release exists.
8
+
9
+ Once published:
10
+
5
11
  ```bash
6
- npx create-synap-app my-app
12
+ npx @synap-core/create-synap-app my-app
7
13
  ```
8
14
 
9
- Creates a Next.js + HeroUI project from the base template, with the SDK, auth,
15
+ It creates a Next.js + HeroUI project from the base template, with the SDK, auth,
10
16
  app shell, command palette, realtime, and mock mode pre-wired. It also drops an
11
17
  `AGENTS.md` into the project so your AI coding tool knows how to work with it.
12
18
 
@@ -22,24 +28,27 @@ npm install @synap-core/sdk@^1.0.0 @synap-core/react@^1.0.0
22
28
  # realtime
23
29
  npm install @synap-core/sdk-realtime
24
30
 
25
- # auth (browser sign-in) or auth-bootstrap (server credential bootstrap)
26
- npm install @synap-core/auth # or @synap-core/auth-bootstrap
31
+ # REST client for API-key holders (agents, scripts); also has setupAgent()
32
+ npm install @synap-core/hub-rest-client
27
33
  ```
28
34
 
35
+ Do not install `@synap-core/auth` (retired; its last release calls a removed pod
36
+ route) or `@synap-core/auth-bootstrap` (not published).
37
+
29
38
  Pin `@^1.0.0` on `sdk`/`react`. **Do not** install unpinned `@synap-core/sdk` —
30
39
  older `0.x` versions on the registry are a different, abandoned package.
31
40
 
32
41
  ## Get this knowledge into your AI coding tool
33
42
 
34
43
  ```bash
35
- # add the app-creation skill (this skill) to a connected AI tool
36
- synap skills get synap-app
44
+ # print this skill from your pod's skill knowledge base
45
+ synap skill get synap-app
37
46
 
38
- # or wire a tool end-to-end (skills + MCP), any target
39
- synap connect --target=cursor # or claude, …
47
+ # or wire a tool end-to-end (skills + MCP)
48
+ synap connect --target=cursor # or claude-code, claude-desktop, codex, …
40
49
  ```
41
50
 
42
- Alternatively, the scaffolded project ships an `AGENTS.md` at its root — most AI
51
+ Once the scaffolder is published, the scaffolded project ships an `AGENTS.md` at its root — most AI
43
52
  coding tools read it automatically, so a freshly-scaffolded app teaches the AI
44
53
  how to build on it with zero extra setup.
45
54
 
@@ -9,8 +9,8 @@ pod, and **every write is governed** (see `governance.md`).
9
9
  | The user has… | Do this | Read |
10
10
  |---|---|---|
11
11
  | an existing app (any framework) | add the SDK | `using-the-sdk.md`, `install-commands.md` |
12
- | nothing yet, wants a full app fast | scaffold the base app | `creating-the-base-app.md`, `install-commands.md` |
13
- | a server / script / CLI (no browser) | SDK with a session token or API key | `using-the-sdk.md` |
12
+ | nothing yet, wants a full app fast | scaffold the base app (scaffolder not published yet) | `creating-the-base-app.md`, `install-commands.md` |
13
+ | a server / script / CLI (no browser) | SDK with a session token, or the REST client with an API key | `using-the-sdk.md` |
14
14
 
15
15
  Both paths use the same underlying packages; only the amount of scaffolding differs.
16
16
 
@@ -18,9 +18,11 @@ Both paths use the same underlying packages; only the amount of scaffolding diff
18
18
 
19
19
  - `@synap-core/sdk` — typed tRPC client. `createSynapClient({ podUrl, ... })`. **The one you almost always want.**
20
20
  - `@synap-core/react` — React/TanStack-Query bindings (`SynapProvider`, hooks).
21
- - `@synap-core/sdk-realtime` — Socket.IO presence/live-updates (`createRealtimeClient`).
22
- - `@synap-core/auth` — sign-in orchestration (Synap Cloud + pod handshake), framework-agnostic.
23
- - `@synap-core/auth-bootstrap` — zero-dep server helpers (`setupAgent`, `handshake`) for machine clients.
21
+ - `@synap-core/sdk-realtime` — Socket.IO "something changed" events (`createRealtimeClient`).
22
+ - `@synap-core/hub-rest-client` — zero-dep REST client for API-key holders (`HubRestClient`, `setupAgent`).
23
+
24
+ Do not install `@synap-core/auth`: that package was retired, and its last npm
25
+ release signs in through a pod route that no longer exists.
24
26
 
25
27
  Pin `@^1.0.0` on `sdk`/`react` so you never resolve an old, abandoned `0.x`.
26
28
 
@@ -30,13 +32,18 @@ Pin `@^1.0.0` on `sdk`/`react` so you never resolve an old, abandoned `0.x`.
30
32
  the change is queued for the pod owner's review (like a PR). Surface the review,
31
33
  don't retry. Full contract: `governance.md`.
32
34
  2. **The pod URL has no `/trpc` suffix and no trailing slash.** The SDK appends `/trpc`.
33
- 3. **Non-browser clients (server/CLI/mobile) authenticate with a token or API key**,
34
- never a cookie. Browser apps use the Synap Cloud sign-in flow (`@synap-core/auth`).
35
+ 3. **Non-browser clients (server/CLI/mobile) authenticate with a session token or
36
+ an API key**, never a cookie. An API key only works on the REST client and
37
+ realtime, not on the tRPC SDK. Browser apps use the Kratos session cookie.
35
38
 
36
39
  ## Get a pod to build against
37
40
 
38
- - **Self-host (free):** `git clone` the backend, `docker compose up -d` (run its
39
- `install.sh` to generate secrets). Pod answers on `:4000`.
41
+ - **Self-host (free):** run the backend's `install.sh` (it installs Docker if
42
+ needed, fetches the release bundle, checks it against the release manifest's
43
+ sha256 and runs `synap install`); see the backend's `deploy/README.md`. It
44
+ also checks the manifest's signature: by default
45
+ (`SYNAP_RELEASE_VERIFY=enforce`) it refuses a release whose signature is
46
+ missing or does not verify; `SYNAP_RELEASE_VERIFY=warn` only warns.
40
47
  - **Managed (paid):** sign up at synap.live and provision a pod.
41
48
 
42
49
  You can develop the base app with **no pod at all** using its mock mode
@@ -7,33 +7,41 @@ import { createSynapClient } from "@synap-core/sdk";
7
7
 
8
8
  const synap = createSynapClient({
9
9
  podUrl: "https://pod.example.com", // NO trailing slash, NO /trpc suffix
10
- apiKey: process.env.SYNAP_API_KEY, // or sessionToken (see auth below)
10
+ sessionToken: process.env.SYNAP_SESSION_TOKEN, // see auth below
11
11
  workspaceId: process.env.SYNAP_WORKSPACE_ID ?? "",
12
+ onAuthError: () => {/* 401: sign in again */},
12
13
  });
13
14
 
14
15
  // It's a typed tRPC client — full autocomplete over every router.
15
16
  const me = await synap.users.me.query();
16
17
  const entities = await synap.entities.list.query({ /* filter */ });
17
18
  const result = await synap.entities.create.mutate({ /* ... */ });
18
- // result may be { status: "proposed", ... } — that is success. See governance.md.
19
+ // result.status may be "proposed" — that is success. See governance.md.
19
20
  ```
20
21
 
21
22
  ### Auth modes (pick ONE)
22
- - **`apiKey`** — a Bearer key generated in the workspace's developer settings.
23
- Best for servers, scripts, CLIs.
24
- - **`sessionToken`** — an Ory Kratos session token, sent as `X-Session-Token`.
25
- Best for non-browser clients that completed a sign-in.
26
- - **cookie fallback** — omit both; the client uses the ambient Kratos cookie.
27
- Browser, same-origin only.
28
-
29
- For machine clients that need to bootstrap a credential, use
30
- `@synap-core/auth-bootstrap` (`setupAgent(provisioningToken)` → API key,
31
- `handshake(jwt)` → session token). Zero-dependency, safe in edge/serverless.
23
+ - **`sessionToken`** — an Ory Kratos session token from a Kratos API (native)
24
+ login flow, sent as `X-Session-Token`. The normal choice for servers, scripts,
25
+ CLIs and mobile.
26
+ - **cookie fallback** — omit both; the client sends the ambient Kratos cookie
27
+ (`credentials: "include"`). Browser only.
28
+ - **`apiKey`** — only the local-mode token of a desktop pod, sent as
29
+ `Authorization: Bearer`. `/trpc` refuses hub and user API keys
30
+ (keys starting with synap_hub_ or synap_user_), and `createSynapClient` throws if you pass
31
+ one. For an API key, use `@synap-core/hub-rest-client` (below).
32
+
33
+ Other options: `extraHeaders`, and `onAuthError({ status, message })`, called on a
34
+ 401 (it also turns the pod's plain-JSON 401 body into a clean tRPC error).
35
+
36
+ To mint an API key for a machine client, `setupAgent(podUrl, provisioningToken,
37
+ agentType)` from `@synap-core/hub-rest-client` returns `{ hubApiKey, agentUserId,
38
+ workspaceId }`. That key works with the REST client and the realtime client, not
39
+ with `/trpc`.
32
40
 
33
41
  ## React — `@synap-core/react`
34
42
 
35
43
  ```tsx
36
- import { SynapProvider, useSynapContext } from "@synap-core/react";
44
+ import { SynapProvider, useSynap, useEntities } from "@synap-core/react";
37
45
 
38
46
  function App() {
39
47
  return (
@@ -42,31 +50,73 @@ function App() {
42
50
  </SynapProvider>
43
51
  );
44
52
  }
53
+
54
+ function Dashboard() {
55
+ const { data } = useEntities("note", { limit: 20 }); // thin data hook
56
+ const create = useSynap.entities.create.useMutation(); // raw tRPC hooks
57
+ // …
58
+ }
45
59
  ```
46
60
 
47
- `SynapProvider` honours every `createSynapClient` option, incl. `sessionToken`
48
- and `onAuthError` (a 401 handler). `react` is a peer of your app's React — it
49
- does not bundle its own.
61
+ `useSynap` is the tRPC React hook tree itself — write
62
+ `useSynap.entities.list.useQuery(…)`, **not** `useSynap().entities…`: calling it
63
+ returns `undefined` at runtime (some package docs still show the call form).
64
+
65
+ `SynapProvider` takes every `createSynapClient` option (incl. `sessionToken` and
66
+ `onAuthError`) plus an optional `queryClient` to share an existing TanStack Query
67
+ client. Exports: `SynapProvider`, `useSynapContext` (`{ podUrl, workspaceId }`),
68
+ `useSynap`, the data hooks `useEntity`, `useEntities`, `useLinks`, `useView`,
69
+ and the `RouterInputs` / `RouterOutputs` types. React 18 or 19 is a peer
70
+ dependency — the package does not bundle its own.
50
71
 
51
72
  ## Realtime — `@synap-core/sdk-realtime`
52
73
 
53
74
  ```ts
54
75
  import { createRealtimeClient } from "@synap-core/sdk-realtime";
55
76
 
56
- const rt = createRealtimeClient({ podUrl, sessionToken });
57
- const stop = rt.onInvalidate((target) => {
58
- // e.g. refetch the affected query
77
+ const rt = createRealtimeClient({
78
+ url: podUrl, // pod base URL; the client adds the /presence namespace
79
+ workspaceId,
80
+ sessionToken, // a Kratos session token…
81
+ userId, // …and the identity id it belongs to (both required)
82
+ // or, as a service account: apiKey (scope realtime:observe), no userId
83
+ onInvalidate: (event) => {
84
+ // entity/relation changed — refetch, e.g.
85
+ queryClient.invalidateQueries({ queryKey: ["entities"] });
86
+ },
59
87
  });
88
+
89
+ rt.connect(); // nothing connects until you call this
90
+ // … later
91
+ rt.disconnect();
60
92
  ```
61
93
 
62
- Websocket-only transport (credentials never ride a long-poll URL) and it refuses
63
- to send credentials over non-TLS unless you explicitly pass `allowInsecure`.
94
+ - Callbacks (all options, no subscribe methods): `onInvalidate` (the
95
+ entity/relation-changing subset), `onEvent` (every known event, `{ name, data }`),
96
+ `onConnect`, `onDisconnect(reason)`, `onError(error)`.
97
+ - The returned client has `connect()`, `disconnect()`, and read-only `connected`
98
+ and `state` (`"idle" | "connecting" | "connected" | "disconnected"`).
99
+ - `SYNAP_EVENT_NAMES` lists the forwarded events; `ENTITY_INVALIDATING_EVENTS` is
100
+ the subset behind `onInvalidate`.
101
+ - Websocket-only transport (credentials never ride a long-poll URL), and
102
+ `connect()` throws on a non-TLS URL unless you pass `allowInsecure: true`
103
+ (localhost dev only).
104
+
105
+ **Version note.** `@synap-core/sdk-realtime@0.1.0` (the current npm release) sends
106
+ every credential as `auth.token`. The pod reads `{ userId, token, workspaceId }`
107
+ for a user and `{ apiKey, workspaceId }` for a service account, so on 0.1.0 a
108
+ session token works only when you also pass `userId`, and an `apiKey` is always
109
+ refused. The fix — correct handshake keys, plus an up-front throw for no
110
+ credential, both credentials, or `sessionToken` without `userId` — lands in the
111
+ first release after 0.1.0. Until then, authenticate realtime with
112
+ `sessionToken` + `userId`.
64
113
 
65
114
  ## Two transports, on purpose
66
- - `@synap-core/sdk` (tRPC) — for apps. Typed, hooks, realtime.
67
- - `@synap-core/hub-rest-client` (REST, zero-dep) — for agents/CLIs/scripts. Carries
68
- agent verbs the tRPC surface has no first-class path for (`ask`, `orient`,
69
- `discover`). Use it when you want a dependency-free REST client, not a full app.
115
+ - `@synap-core/sdk` (tRPC) — for apps signed in as a user. Typed, React hooks.
116
+ - `@synap-core/hub-rest-client` (REST against `/api/hub/*`, zero-dep) — for
117
+ agents/CLIs/scripts holding an API key. `new HubRestClient({ podUrl, apiKey,
118
+ workspaceId })`. Carries agent verbs the tRPC surface has no first-class path
119
+ for (`ask`, `orient`, `discover`).
70
120
 
71
121
  ## Keep types fresh
72
122
  The SDK is typed by `@synap-core/api-types`, generated from the pod's live router.
@@ -622,4 +622,4 @@ wired at every install site). The browser UI is **display-only**: it threads
622
622
  `requiredTier` through to a "requires <tier>" badge but does NOT hard-block, and
623
623
  its lock/upgrade CTA only engages once a host wires a billing destination — so
624
624
  don't add a UI-side gate expecting it to enforce anything, and don't rebuild the
625
- badge thread (it exists). Full current-state: repo `TEMPLATE-DEV-GUIDE.md`.
625
+ badge thread (it exists). Full current-state: the catalog README (`synap-app/packages/workspace-templates/README.md`).
@@ -60,4 +60,4 @@ wired at every install site). The browser UI is **display-only**: it threads
60
60
  `requiredTier` through to a "requires <tier>" badge but does NOT hard-block, and
61
61
  its lock/upgrade CTA only engages once a host wires a billing destination — so
62
62
  don't add a UI-side gate expecting it to enforce anything, and don't rebuild the
63
- badge thread (it exists). Full current-state: repo `TEMPLATE-DEV-GUIDE.md`.
63
+ badge thread (it exists). Full current-state: the catalog README (`synap-app/packages/workspace-templates/README.md`).
@@ -315,7 +315,7 @@ Defining a relation def is rarely worth it — `relates_to` + a property usually
315
315
 
316
316
  ---
317
317
 
318
- ## The intent vocabulary — one SSOT, three mirrors
318
+ ## The intent vocabulary — one SSOT, two mirrors
319
319
 
320
320
  **What a capability DOES** — `send_message`, `generate_media`, `publish_post`,
321
321
  `delegate_agent_task` — is the
@@ -346,15 +346,17 @@ This is why every code-side copy is a **mirror** and not the source. A slug that
346
346
  none of the existing rows leaves `intent` UNSET; the vocabulary is deliberately not
347
347
  open for developers to mint (cf. W3C Web Intents, abandoned partly for that reason).
348
348
 
349
- ### The three mirrors, and why each one must exist
349
+ ### The two mirrors, and why each one must exist
350
350
 
351
- There are exactly **three**. A fourth was deleted in 2026-10-01 (see below).
351
+ There are exactly **two**. Two more were removed: `AbstractVerb` in
352
+ `@synap/playbooks` (deleted 2026-10-01, see below), and the pod's own
353
+ `ABSTRACT_VERBS` copy (collapsed 2026-10-26 — `@synap/database` now depends on
354
+ `@synap-core/types`, so it re-exports mirror 1 under that name).
352
355
 
353
- | # | Where | Kind | Why it cannot import the SSOT |
354
- | --- | --------------------------------------------------------------------------------------------------------------------------------- | --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
355
- | 1 | `synap-backend/packages/database/src/schema/tools.ts` — `ABSTRACT_VERBS` | runtime, the 0283 SEED union (13) | `@synap-core/types` **devDepends on** `@synap/database`. A reverse import is a build cycle — the same constraint that forces `GUIDELINE_SCOPE_ORDER` to be mirrored. |
356
- | 2 | `synap-backend/packages/types/src/capability-intents/` — `ABSTRACT_INTENTS`, `REGISTERED_EXTRAS`, `CAPABILITY_INTENTS` | runtime, published leaf | Same cycle, other direction. It is a **leaf subpath** (`@synap-core/types/capability-intents`) rather than the `./` barrel because the barrel re-exports `@synap/database` types and a VALUE import from a barrel crashes Hermes (relay ships React Native). |
357
- | 3 | `synap-control-plane-api/src/seeds/capability-intent-vocabulary.ts` — `ABSTRACT_VERBS`, `REGISTERED_EXTRAS`, `CAPABILITY_INTENTS` | runtime, the CP catalog seeder | The control plane is a **separate deploy target with its own `pnpm-lock.yaml`**. It resolves neither `@synap-core/types` nor the pod's packages (verified: `require.resolve('@synap-core/types')` → `MODULE_NOT_FOUND`). |
356
+ | # | Where | Kind | Why it cannot import the SSOT |
357
+ | --- | --------------------------------------------------------------------------------------------------------------------------------- | ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
358
+ | 1 | `synap-backend/packages/types/src/capability-intents/` — `ABSTRACT_INTENTS`, `REGISTERED_EXTRAS`, `CAPABILITY_INTENTS` | runtime, published leaf | The SSOT is SQL rows; a TypeScript list is a mirror by construction. The pod reads it too: `ABSTRACT_VERBS` in `packages/database/src/schema/tools.ts` is this `ABSTRACT_INTENTS`, re-exported (the 0283 SEED union, 13). |
359
+ | 2 | `synap-control-plane-api/src/seeds/capability-intent-vocabulary.ts` — `ABSTRACT_VERBS`, `REGISTERED_EXTRAS`, `CAPABILITY_INTENTS` | runtime, the CP catalog seeder | The control plane is a **separate deploy target with its own `pnpm-lock.yaml`**. It resolves neither `@synap-core/types` nor the pod's packages (verified: `require.resolve('@synap-core/types')` → `MODULE_NOT_FOUND`). |
358
360
 
359
361
  Consumed by: `@synap/api` (`intent-registry`, `create-from-definition`,
360
362
  `schemas/playbook-definition.ts`), `synap-app/packages/workspace-templates`
@@ -377,7 +379,7 @@ Search for this chapter by the field you are holding, not by the word "intent":
377
379
  All four are validated against this ONE vocabulary, so a workspace and a playbook
378
380
  cannot be spelled from two different lists.
379
381
 
380
- ### THE FOURTH MIRROR WAS DELETED — do not re-add it
382
+ ### THE PLAYBOOKS MIRROR WAS DELETED — do not re-add it
381
383
 
382
384
  `AbstractVerb` in `@synap/playbooks/src/index.ts` was a TYPE-only mirror of all 13
383
385
  slugs. It was **stale** (no `publish_post`) and **dead** — a whole-repo scan across
@@ -396,18 +398,19 @@ a package that consumes nothing would go stale in the one direction that is invi
396
398
  `INSERT INTO "capability_intents" ("slug", "effect", "statement") VALUES (...) ON CONFLICT ("slug") DO NOTHING;`
397
399
  `effect` ∈ `read|write|act`. Never `drizzle-kit generate` — hand-write it, and mirror it into
398
400
  `0000_baseline_schema.sql` if it adds a table or column.
399
- 2. **Add it to mirror 1** — `ABSTRACT_VERBS` in `packages/database/src/schema/tools.ts`,
400
- **only if it is a SEED slug**. A post-seed row does not belong there (that is what
401
- `publish_post` / `REGISTERED_EXTRAS` is for).
402
- Post-seed rows today: `publish_post` (0284, write) and `delegate_agent_task`
403
- (0314, act — every verb of an external-agent binding, `tools.config.agentBinding`:
404
- start / send / status / cancel).
405
- 3. **Add it to mirror 2** — `ABSTRACT_INTENTS` or `REGISTERED_EXTRAS` in
406
- `packages/types/src/capability-intents/index.ts`. Then **`cd packages/types && pnpm build`** —
401
+ 2. **Add it to mirror 1** — `ABSTRACT_INTENTS` (a SEED slug) or `REGISTERED_EXTRAS`
402
+ (a post-seed row) in `packages/types/src/capability-intents/index.ts`. The pod's
403
+ `ABSTRACT_VERBS` is `ABSTRACT_INTENTS`, so it follows. Then **`cd packages/types && pnpm build`** —
407
404
  consumers typecheck against the built `dist/`, so a skipped rebuild makes your gate lie.
408
- 4. **Add it to mirror 3** — the same two lists in the control plane's
405
+ Post-seed rows today: `publish_post` (0284, write), `delegate_agent_task`
406
+ (0314, act — every verb of an external-agent binding, `tools.config.agentBinding`:
407
+ start / send / status / cancel), `read_webpage` (0329, read), `search_web`
408
+ (0360, read) and `edit_video` (0370, act — sends the person's video to an
409
+ outside service; its one input, `{ video }`, is seeded on `input_schema` in
410
+ the same file).
411
+ 3. **Add it to mirror 2** — the same two lists in the control plane's
409
412
  `capability-intent-vocabulary.ts`.
410
- 5. **Update the arity floors** — `SEED_SLUG_ARITY` / `EXTRA_SLUG_ARITY` in
413
+ 4. **Update the arity floors** — `SEED_SLUG_ARITY` / `EXTRA_SLUG_ARITY` in
411
414
  `packages/api/src/__tripwires__/intent-vocabulary-one-ssot.tripwire.test.ts`.
412
415
  These are the ONE place you must edit by hand, and the build stops until you do.
413
416
 
@@ -416,8 +419,8 @@ from the migration SQL, so it is a red build — never a divergence nobody notic
416
419
 
417
420
  ### What can still go wrong
418
421
 
419
- - **A mirror can be added.** Nothing structurally prevents a fifth copy; the tripwire
420
- only polices the names it knows. This is why step 5 exists as a hand-edit floor.
422
+ - **A mirror can be added.** Nothing structurally prevents a third copy; the tripwire
423
+ only polices the names it knows. This is why step 4 exists as a hand-edit floor.
421
424
  - **A guard proves SAMENESS, never CORRECTNESS.** If every mirror and the migration agree
422
425
  on a slug that should not exist, all four guards stay green. The vocabulary's MEANING is
423
426
  reviewed by a human, not derived.
@@ -1,4 +1,4 @@
1
- ## The intent vocabulary — one SSOT, three mirrors
1
+ ## The intent vocabulary — one SSOT, two mirrors
2
2
 
3
3
  **What a capability DOES** — `send_message`, `generate_media`, `publish_post`,
4
4
  `delegate_agent_task` — is the
@@ -29,15 +29,17 @@ This is why every code-side copy is a **mirror** and not the source. A slug that
29
29
  none of the existing rows leaves `intent` UNSET; the vocabulary is deliberately not
30
30
  open for developers to mint (cf. W3C Web Intents, abandoned partly for that reason).
31
31
 
32
- ### The three mirrors, and why each one must exist
32
+ ### The two mirrors, and why each one must exist
33
33
 
34
- There are exactly **three**. A fourth was deleted in 2026-10-01 (see below).
34
+ There are exactly **two**. Two more were removed: `AbstractVerb` in
35
+ `@synap/playbooks` (deleted 2026-10-01, see below), and the pod's own
36
+ `ABSTRACT_VERBS` copy (collapsed 2026-10-26 — `@synap/database` now depends on
37
+ `@synap-core/types`, so it re-exports mirror 1 under that name).
35
38
 
36
- | # | Where | Kind | Why it cannot import the SSOT |
37
- | --- | --------------------------------------------------------------------------------------------------------------------------------- | --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
38
- | 1 | `synap-backend/packages/database/src/schema/tools.ts` — `ABSTRACT_VERBS` | runtime, the 0283 SEED union (13) | `@synap-core/types` **devDepends on** `@synap/database`. A reverse import is a build cycle — the same constraint that forces `GUIDELINE_SCOPE_ORDER` to be mirrored. |
39
- | 2 | `synap-backend/packages/types/src/capability-intents/` — `ABSTRACT_INTENTS`, `REGISTERED_EXTRAS`, `CAPABILITY_INTENTS` | runtime, published leaf | Same cycle, other direction. It is a **leaf subpath** (`@synap-core/types/capability-intents`) rather than the `./` barrel because the barrel re-exports `@synap/database` types and a VALUE import from a barrel crashes Hermes (relay ships React Native). |
40
- | 3 | `synap-control-plane-api/src/seeds/capability-intent-vocabulary.ts` — `ABSTRACT_VERBS`, `REGISTERED_EXTRAS`, `CAPABILITY_INTENTS` | runtime, the CP catalog seeder | The control plane is a **separate deploy target with its own `pnpm-lock.yaml`**. It resolves neither `@synap-core/types` nor the pod's packages (verified: `require.resolve('@synap-core/types')` → `MODULE_NOT_FOUND`). |
39
+ | # | Where | Kind | Why it cannot import the SSOT |
40
+ | --- | --------------------------------------------------------------------------------------------------------------------------------- | ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
41
+ | 1 | `synap-backend/packages/types/src/capability-intents/` — `ABSTRACT_INTENTS`, `REGISTERED_EXTRAS`, `CAPABILITY_INTENTS` | runtime, published leaf | The SSOT is SQL rows; a TypeScript list is a mirror by construction. The pod reads it too: `ABSTRACT_VERBS` in `packages/database/src/schema/tools.ts` is this `ABSTRACT_INTENTS`, re-exported (the 0283 SEED union, 13). |
42
+ | 2 | `synap-control-plane-api/src/seeds/capability-intent-vocabulary.ts` — `ABSTRACT_VERBS`, `REGISTERED_EXTRAS`, `CAPABILITY_INTENTS` | runtime, the CP catalog seeder | The control plane is a **separate deploy target with its own `pnpm-lock.yaml`**. It resolves neither `@synap-core/types` nor the pod's packages (verified: `require.resolve('@synap-core/types')` → `MODULE_NOT_FOUND`). |
41
43
 
42
44
  Consumed by: `@synap/api` (`intent-registry`, `create-from-definition`,
43
45
  `schemas/playbook-definition.ts`), `synap-app/packages/workspace-templates`
@@ -60,7 +62,7 @@ Search for this chapter by the field you are holding, not by the word "intent":
60
62
  All four are validated against this ONE vocabulary, so a workspace and a playbook
61
63
  cannot be spelled from two different lists.
62
64
 
63
- ### THE FOURTH MIRROR WAS DELETED — do not re-add it
65
+ ### THE PLAYBOOKS MIRROR WAS DELETED — do not re-add it
64
66
 
65
67
  `AbstractVerb` in `@synap/playbooks/src/index.ts` was a TYPE-only mirror of all 13
66
68
  slugs. It was **stale** (no `publish_post`) and **dead** — a whole-repo scan across
@@ -79,18 +81,19 @@ a package that consumes nothing would go stale in the one direction that is invi
79
81
  `INSERT INTO "capability_intents" ("slug", "effect", "statement") VALUES (...) ON CONFLICT ("slug") DO NOTHING;`
80
82
  `effect` ∈ `read|write|act`. Never `drizzle-kit generate` — hand-write it, and mirror it into
81
83
  `0000_baseline_schema.sql` if it adds a table or column.
82
- 2. **Add it to mirror 1** — `ABSTRACT_VERBS` in `packages/database/src/schema/tools.ts`,
83
- **only if it is a SEED slug**. A post-seed row does not belong there (that is what
84
- `publish_post` / `REGISTERED_EXTRAS` is for).
85
- Post-seed rows today: `publish_post` (0284, write) and `delegate_agent_task`
86
- (0314, act — every verb of an external-agent binding, `tools.config.agentBinding`:
87
- start / send / status / cancel).
88
- 3. **Add it to mirror 2** — `ABSTRACT_INTENTS` or `REGISTERED_EXTRAS` in
89
- `packages/types/src/capability-intents/index.ts`. Then **`cd packages/types && pnpm build`** —
84
+ 2. **Add it to mirror 1** — `ABSTRACT_INTENTS` (a SEED slug) or `REGISTERED_EXTRAS`
85
+ (a post-seed row) in `packages/types/src/capability-intents/index.ts`. The pod's
86
+ `ABSTRACT_VERBS` is `ABSTRACT_INTENTS`, so it follows. Then **`cd packages/types && pnpm build`** —
90
87
  consumers typecheck against the built `dist/`, so a skipped rebuild makes your gate lie.
91
- 4. **Add it to mirror 3** — the same two lists in the control plane's
88
+ Post-seed rows today: `publish_post` (0284, write), `delegate_agent_task`
89
+ (0314, act — every verb of an external-agent binding, `tools.config.agentBinding`:
90
+ start / send / status / cancel), `read_webpage` (0329, read), `search_web`
91
+ (0360, read) and `edit_video` (0370, act — sends the person's video to an
92
+ outside service; its one input, `{ video }`, is seeded on `input_schema` in
93
+ the same file).
94
+ 3. **Add it to mirror 2** — the same two lists in the control plane's
92
95
  `capability-intent-vocabulary.ts`.
93
- 5. **Update the arity floors** — `SEED_SLUG_ARITY` / `EXTRA_SLUG_ARITY` in
96
+ 4. **Update the arity floors** — `SEED_SLUG_ARITY` / `EXTRA_SLUG_ARITY` in
94
97
  `packages/api/src/__tripwires__/intent-vocabulary-one-ssot.tripwire.test.ts`.
95
98
  These are the ONE place you must edit by hand, and the build stops until you do.
96
99
 
@@ -99,8 +102,8 @@ from the migration SQL, so it is a red build — never a divergence nobody notic
99
102
 
100
103
  ### What can still go wrong
101
104
 
102
- - **A mirror can be added.** Nothing structurally prevents a fifth copy; the tripwire
103
- only polices the names it knows. This is why step 5 exists as a hand-edit floor.
105
+ - **A mirror can be added.** Nothing structurally prevents a third copy; the tripwire
106
+ only polices the names it knows. This is why step 4 exists as a hand-edit floor.
104
107
  - **A guard proves SAMENESS, never CORRECTNESS.** If every mirror and the migration agree
105
108
  on a slug that should not exist, all four guards stay green. The vocabulary's MEANING is
106
109
  reviewed by a human, not derived.