@synap-core/cli 1.8.0 → 1.10.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 (332) hide show
  1. package/dist/commands/agent-chat.d.ts +47 -0
  2. package/dist/commands/agent-chat.js +250 -0
  3. package/dist/commands/agent-chat.js.map +1 -0
  4. package/dist/commands/agent-run.js +3 -4
  5. package/dist/commands/agent-run.js.map +1 -1
  6. package/dist/commands/agents.js +85 -46
  7. package/dist/commands/agents.js.map +1 -1
  8. package/dist/commands/automation.js +2 -1
  9. package/dist/commands/automation.js.map +1 -1
  10. package/dist/commands/bridge-setup.d.ts +66 -0
  11. package/dist/commands/bridge-setup.js +1112 -0
  12. package/dist/commands/bridge-setup.js.map +1 -0
  13. package/dist/commands/cap-connections.d.ts +28 -0
  14. package/dist/commands/cap-connections.js +224 -0
  15. package/dist/commands/cap-connections.js.map +1 -0
  16. package/dist/commands/capability.d.ts +89 -0
  17. package/dist/commands/capability.js +1523 -0
  18. package/dist/commands/capability.js.map +1 -0
  19. package/dist/commands/centrality.d.ts +20 -0
  20. package/dist/commands/centrality.js +94 -0
  21. package/dist/commands/centrality.js.map +1 -0
  22. package/dist/commands/connect.d.ts +2 -0
  23. package/dist/commands/connect.js +16 -35
  24. package/dist/commands/connect.js.map +1 -1
  25. package/dist/commands/context.js +4 -6
  26. package/dist/commands/context.js.map +1 -1
  27. package/dist/commands/data-extra.d.ts +1 -0
  28. package/dist/commands/data-extra.js +34 -10
  29. package/dist/commands/data-extra.js.map +1 -1
  30. package/dist/commands/data.d.ts +4 -4
  31. package/dist/commands/data.js +241 -184
  32. package/dist/commands/data.js.map +1 -1
  33. package/dist/commands/diagnose.d.ts +20 -0
  34. package/dist/commands/diagnose.js +143 -0
  35. package/dist/commands/diagnose.js.map +1 -0
  36. package/dist/commands/digest.d.ts +17 -0
  37. package/dist/commands/digest.js +121 -0
  38. package/dist/commands/digest.js.map +1 -0
  39. package/dist/commands/discover.d.ts +2 -0
  40. package/dist/commands/discover.js +25 -8
  41. package/dist/commands/discover.js.map +1 -1
  42. package/dist/commands/events.js +2 -1
  43. package/dist/commands/events.js.map +1 -1
  44. package/dist/commands/explain.d.ts +1 -1
  45. package/dist/commands/explain.js +18 -15
  46. package/dist/commands/explain.js.map +1 -1
  47. package/dist/commands/facet.d.ts +29 -0
  48. package/dist/commands/facet.js +113 -0
  49. package/dist/commands/facet.js.map +1 -0
  50. package/dist/commands/graph.js +2 -1
  51. package/dist/commands/graph.js.map +1 -1
  52. package/dist/commands/import.d.ts +38 -0
  53. package/dist/commands/import.js +440 -0
  54. package/dist/commands/import.js.map +1 -0
  55. package/dist/commands/init.js +35 -16
  56. package/dist/commands/init.js.map +1 -1
  57. package/dist/commands/keys.js +3 -4
  58. package/dist/commands/keys.js.map +1 -1
  59. package/dist/commands/knowledge.d.ts +2 -0
  60. package/dist/commands/knowledge.js +61 -9
  61. package/dist/commands/knowledge.js.map +1 -1
  62. package/dist/commands/launch.d.ts +50 -0
  63. package/dist/commands/launch.js +790 -0
  64. package/dist/commands/launch.js.map +1 -0
  65. package/dist/commands/lens.d.ts +45 -0
  66. package/dist/commands/lens.js +386 -0
  67. package/dist/commands/lens.js.map +1 -0
  68. package/dist/commands/market.d.ts +209 -0
  69. package/dist/commands/market.js +1368 -0
  70. package/dist/commands/market.js.map +1 -0
  71. package/dist/commands/mcp.d.ts +42 -0
  72. package/dist/commands/mcp.js +184 -0
  73. package/dist/commands/mcp.js.map +1 -0
  74. package/dist/commands/observe.js +14 -11
  75. package/dist/commands/observe.js.map +1 -1
  76. package/dist/commands/open.d.ts +21 -4
  77. package/dist/commands/open.js +45 -4
  78. package/dist/commands/open.js.map +1 -1
  79. package/dist/commands/pod.d.ts +100 -0
  80. package/dist/commands/pod.js +359 -0
  81. package/dist/commands/pod.js.map +1 -0
  82. package/dist/commands/pods.d.ts +1 -0
  83. package/dist/commands/pods.js +77 -15
  84. package/dist/commands/pods.js.map +1 -1
  85. package/dist/commands/proposals-actions.js +40 -3
  86. package/dist/commands/proposals-actions.js.map +1 -1
  87. package/dist/commands/purge.d.ts +23 -0
  88. package/dist/commands/purge.js +148 -0
  89. package/dist/commands/purge.js.map +1 -0
  90. package/dist/commands/raycast.d.ts +15 -0
  91. package/dist/commands/raycast.js +153 -0
  92. package/dist/commands/raycast.js.map +1 -0
  93. package/dist/commands/sessions.js +45 -21
  94. package/dist/commands/sessions.js.map +1 -1
  95. package/dist/commands/skill-manage.d.ts +21 -0
  96. package/dist/commands/skill-manage.js +195 -0
  97. package/dist/commands/skill-manage.js.map +1 -0
  98. package/dist/commands/skills-commands.js +2 -1
  99. package/dist/commands/skills-commands.js.map +1 -1
  100. package/dist/commands/skills.js +4 -3
  101. package/dist/commands/skills.js.map +1 -1
  102. package/dist/commands/status.d.ts +3 -1
  103. package/dist/commands/status.js +166 -14
  104. package/dist/commands/status.js.map +1 -1
  105. package/dist/commands/statusline.d.ts +24 -0
  106. package/dist/commands/statusline.js +450 -0
  107. package/dist/commands/statusline.js.map +1 -0
  108. package/dist/commands/subscribe.js +2 -2
  109. package/dist/commands/subscribe.js.map +1 -1
  110. package/dist/commands/tools.d.ts +30 -0
  111. package/dist/commands/tools.js +315 -0
  112. package/dist/commands/tools.js.map +1 -0
  113. package/dist/commands/versions.d.ts +27 -0
  114. package/dist/commands/versions.js +277 -0
  115. package/dist/commands/versions.js.map +1 -0
  116. package/dist/commands/view.js +3 -2
  117. package/dist/commands/view.js.map +1 -1
  118. package/dist/commands/whoami.d.ts +21 -0
  119. package/dist/commands/whoami.js +129 -0
  120. package/dist/commands/whoami.js.map +1 -0
  121. package/dist/index.js +776 -60
  122. package/dist/index.js.map +1 -1
  123. package/dist/lib/approval-poll.d.ts +42 -0
  124. package/dist/lib/approval-poll.js +52 -0
  125. package/dist/lib/approval-poll.js.map +1 -0
  126. package/dist/lib/auth.d.ts +29 -6
  127. package/dist/lib/auth.js +105 -94
  128. package/dist/lib/auth.js.map +1 -1
  129. package/dist/lib/bundle-version.d.ts +11 -0
  130. package/dist/lib/bundle-version.js +33 -0
  131. package/dist/lib/bundle-version.js.map +1 -0
  132. package/dist/lib/capture-lane.d.ts +30 -12
  133. package/dist/lib/capture-lane.js +71 -14
  134. package/dist/lib/capture-lane.js.map +1 -1
  135. package/dist/lib/cp-packages.d.ts +121 -0
  136. package/dist/lib/cp-packages.js +218 -0
  137. package/dist/lib/cp-packages.js.map +1 -0
  138. package/dist/lib/describe-lens.d.ts +46 -0
  139. package/dist/lib/describe-lens.js +33 -0
  140. package/dist/lib/describe-lens.js.map +1 -0
  141. package/dist/lib/hub-client.d.ts +78 -1
  142. package/dist/lib/hub-client.js +474 -22
  143. package/dist/lib/hub-client.js.map +1 -1
  144. package/dist/lib/installed.d.ts +51 -0
  145. package/dist/lib/installed.js +70 -0
  146. package/dist/lib/installed.js.map +1 -0
  147. package/dist/lib/next-steps.d.ts +61 -0
  148. package/dist/lib/next-steps.js +146 -0
  149. package/dist/lib/next-steps.js.map +1 -0
  150. package/dist/lib/pod.d.ts +91 -19
  151. package/dist/lib/pod.js +222 -56
  152. package/dist/lib/pod.js.map +1 -1
  153. package/dist/lib/project-ref.d.ts +95 -0
  154. package/dist/lib/project-ref.js +144 -0
  155. package/dist/lib/project-ref.js.map +1 -0
  156. package/dist/lib/project.d.ts +35 -0
  157. package/dist/lib/project.js +39 -0
  158. package/dist/lib/project.js.map +1 -0
  159. package/dist/lib/session-lens.d.ts +33 -0
  160. package/dist/lib/session-lens.js +76 -0
  161. package/dist/lib/session-lens.js.map +1 -0
  162. package/dist/lib/skill-import.d.ts +40 -0
  163. package/dist/lib/skill-import.js +203 -0
  164. package/dist/lib/skill-import.js.map +1 -0
  165. package/dist/lib/skills-installer.d.ts +17 -6
  166. package/dist/lib/skills-installer.js +133 -19
  167. package/dist/lib/skills-installer.js.map +1 -1
  168. package/dist/lib/suite.d.ts +28 -0
  169. package/dist/lib/suite.js +29 -0
  170. package/dist/lib/suite.js.map +1 -0
  171. package/dist/lib/superwhisper-import.d.ts +40 -0
  172. package/dist/lib/superwhisper-import.js +389 -0
  173. package/dist/lib/superwhisper-import.js.map +1 -0
  174. package/dist/lib/targets.d.ts +49 -1
  175. package/dist/lib/targets.js +420 -288
  176. package/dist/lib/targets.js.map +1 -1
  177. package/dist/lib/unwrapList.d.ts +1 -0
  178. package/dist/lib/unwrapList.js +20 -0
  179. package/dist/lib/unwrapList.js.map +1 -0
  180. package/dist/utils/logger.d.ts +2 -0
  181. package/dist/utils/logger.js +10 -2
  182. package/dist/utils/logger.js.map +1 -1
  183. package/package.json +9 -4
  184. package/skills/agent-os/SKILL.md +212 -0
  185. package/skills/manifest.json +5 -0
  186. package/skills/onboard/SKILL.md +186 -0
  187. package/skills/synap/SKILL.md +1182 -37
  188. package/skills/synap/_order.txt +37 -0
  189. package/skills/synap/_skill.yaml +24 -0
  190. package/skills/synap/authentication.md +10 -0
  191. package/skills/synap/automations.md +264 -0
  192. package/skills/synap/capabilities.md +107 -0
  193. package/skills/synap/capture.md +10 -0
  194. package/skills/synap/cli-operations.md +107 -0
  195. package/skills/synap/common-mistakes.md +12 -0
  196. package/skills/synap/creative-loop.md +43 -0
  197. package/skills/synap/crm.md +117 -0
  198. package/skills/synap/diagnostics.md +35 -0
  199. package/skills/synap/digesting-a-channel.md +38 -0
  200. package/skills/synap/document-embeds.md +123 -0
  201. package/skills/synap/escalation-ladder.md +71 -0
  202. package/skills/synap/focus-sessions.md +47 -0
  203. package/skills/synap/governance-rules.md +64 -0
  204. package/skills/synap/governance.md +6 -6
  205. package/skills/synap/graph-gardening.md +21 -0
  206. package/skills/synap/inline-patterns.md +38 -0
  207. package/skills/synap/lenses.md +38 -0
  208. package/skills/synap/linking-principle.md +39 -0
  209. package/skills/synap/linking.md +1 -0
  210. package/skills/synap/mental-model.md +45 -0
  211. package/skills/synap/more.md +7 -0
  212. package/skills/synap/multi-entity-capture.md +10 -0
  213. package/skills/synap/next-action.md +37 -0
  214. package/skills/synap/north-star.md +17 -0
  215. package/skills/synap/operating-mode-rules.md +45 -0
  216. package/skills/synap/operating-mode.md +9 -0
  217. package/skills/synap/quick-reference.md +67 -0
  218. package/skills/synap/reading.md +43 -0
  219. package/skills/synap/recap.md +21 -0
  220. package/skills/synap/reflexes.md +46 -0
  221. package/skills/synap/response-style.md +21 -0
  222. package/skills/synap/scope.md +13 -0
  223. package/skills/synap/showing.md +17 -0
  224. package/skills/synap/user-model.md +25 -0
  225. package/skills/synap/viewframe-cells.md +290 -0
  226. package/skills/synap/work-flow.md +113 -0
  227. package/skills/synap/worked-examples.md +53 -0
  228. package/skills/synap/workspace-design.md +45 -0
  229. package/skills/synap/workspace-edges.md +49 -0
  230. package/skills/synap/writes.md +156 -0
  231. package/skills/synap-schema/SKILL.md +97 -6
  232. package/skills/synap-schema/_order.txt +13 -0
  233. package/skills/synap-schema/_skill.yaml +22 -0
  234. package/skills/synap-schema/before-you-touch.md +14 -0
  235. package/skills/synap-schema/common-mistakes.md +9 -0
  236. package/skills/synap-schema/creating-profiles.md +17 -0
  237. package/skills/synap-schema/discover-profiles.md +24 -0
  238. package/skills/synap-schema/entity-scope.md +8 -0
  239. package/skills/synap-schema/extend-vs-create.md +31 -0
  240. package/skills/synap-schema/more.md +6 -0
  241. package/skills/synap-schema/overlay-properties.md +22 -0
  242. package/skills/synap-schema/profiles.md +3 -0
  243. package/skills/synap-schema/property-defs.md +29 -0
  244. package/skills/synap-schema/read-before-write.md +50 -0
  245. package/skills/synap-schema/relations.md +18 -0
  246. package/skills/synap-schema/worked-example.md +30 -0
  247. package/skills/synap-ui/SKILL.md +26 -2
  248. package/skills/synap-ui/_order.txt +13 -0
  249. package/skills/synap-ui/_skill.yaml +23 -0
  250. package/skills/synap-ui/ai-companion.md +27 -0
  251. package/skills/synap-ui/arranging-bento.md +8 -0
  252. package/skills/synap-ui/before-you-build.md +20 -0
  253. package/skills/synap-ui/bento-layouts.md +35 -0
  254. package/skills/synap-ui/common-mistakes.md +10 -0
  255. package/skills/synap-ui/creating-views.md +26 -0
  256. package/skills/synap-ui/four-builds.md +24 -0
  257. package/skills/synap-ui/more.md +7 -0
  258. package/skills/synap-ui/proposing-vs-extending.md +11 -0
  259. package/skills/synap-ui/view-list.md +24 -0
  260. package/skills/synap-ui/views.md +3 -0
  261. package/skills/synap-ui/worked-example.md +18 -0
  262. package/skills/synap-ui/workspaces.md +47 -0
  263. package/templates/automations/bookmark-enrichment.automation.json +44 -0
  264. package/templates/automations/mail-feed.automation.json +86 -0
  265. package/templates/automations/url-bookmark-capture.automation.json +70 -0
  266. package/templates/skills/README.md +21 -0
  267. package/templates/skills/authoring-automations/SKILL.md +95 -0
  268. package/templates/skills/authoring-automations/reference/node-matrix.md +49 -0
  269. package/templates/skills/authoring-capabilities/SKILL.md +93 -0
  270. package/templates/skills/authoring-capabilities/reference/template-anatomy.md +70 -0
  271. package/templates/skills/authoring-playbooks/SKILL.md +102 -0
  272. package/templates/skills/authoring-playbooks/reference/stages-contract.md +35 -0
  273. package/templates/skills/authoring-playbooks/reference/worked-example.md +64 -0
  274. package/templates/skills/bookmark-enrichment/SKILL.md +40 -0
  275. package/templates/skills/coordinating-agents/SKILL.md +32 -0
  276. package/templates/skills/coordinating-branches/SKILL.md +27 -0
  277. package/templates/skills/mapping-the-server/SKILL.md +54 -0
  278. package/templates/skills/reading-client-progress/SKILL.md +65 -0
  279. package/templates/skills/routing-to-channels/SKILL.md +54 -0
  280. package/templates/skills/stellar-scf-grants-advisor/ASSOCIATE-CHEATSHEET.md +133 -0
  281. package/templates/skills/stellar-scf-grants-advisor/ASSOCIATE-ONBOARDING-GUIDE.md +761 -0
  282. package/templates/skills/stellar-scf-grants-advisor/CLIENT-FAQ.md +181 -0
  283. package/templates/skills/stellar-scf-grants-advisor/PARTNER-HANDOVER.md +130 -0
  284. package/templates/skills/stellar-scf-grants-advisor/SKILL.md +102 -0
  285. package/templates/skills/stellar-scf-grants-advisor/examples/README.md +43 -0
  286. package/templates/skills/stellar-scf-grants-advisor/examples/chat-summary-templates.md +158 -0
  287. package/templates/skills/stellar-scf-grants-advisor/examples/corpus-comparable-rows.md +136 -0
  288. package/templates/skills/stellar-scf-grants-advisor/examples/q10-team-rewrites.md +109 -0
  289. package/templates/skills/stellar-scf-grants-advisor/examples/q2-description-rewrites.md +88 -0
  290. package/templates/skills/stellar-scf-grants-advisor/examples/q6-integration-rewrites.md +107 -0
  291. package/templates/skills/stellar-scf-grants-advisor/examples/tranche-structures.md +176 -0
  292. package/templates/skills/stellar-scf-grants-advisor/reference/01-methodology.md +96 -0
  293. package/templates/skills/stellar-scf-grants-advisor/reference/02-scoring-framework.md +199 -0
  294. package/templates/skills/stellar-scf-grants-advisor/reference/03-rejection-patterns.md +241 -0
  295. package/templates/skills/stellar-scf-grants-advisor/reference/04-winner-patterns.md +120 -0
  296. package/templates/skills/stellar-scf-grants-advisor/reference/05-comeback-patterns.md +92 -0
  297. package/templates/skills/stellar-scf-grants-advisor/reference/06-budget-benchmarks.md +125 -0
  298. package/templates/skills/stellar-scf-grants-advisor/reference/07-integration-list.md +147 -0
  299. package/templates/skills/stellar-scf-grants-advisor/reference/08-abstract-fields.md +258 -0
  300. package/templates/skills/stellar-scf-grants-advisor/reference/09-build-fields.md +259 -0
  301. package/templates/skills/stellar-scf-grants-advisor/reference/10-corpus-winners.md +135 -0
  302. package/templates/skills/stellar-scf-grants-advisor/reference/11-corpus-rejects.md +267 -0
  303. package/templates/skills/stellar-scf-grants-advisor/reference/12-client-communication.md +131 -0
  304. package/templates/skills/stellar-scf-grants-advisor/reference/13-excel-output-spec.md +199 -0
  305. package/templates/skills/stellar-scf-grants-advisor/reference/14-worked-examples.md +160 -0
  306. package/templates/skills/stellar-scf-grants-advisor/templates/abstract-review-builder.py +253 -0
  307. package/templates/skills/stellar-scf-grants-advisor/templates/build-review-builder.py +213 -0
  308. package/dist/commands/connectors.d.ts +0 -31
  309. package/dist/commands/connectors.js +0 -276
  310. package/dist/commands/connectors.js.map +0 -1
  311. package/dist/commands/finish.d.ts +0 -25
  312. package/dist/commands/finish.js +0 -274
  313. package/dist/commands/finish.js.map +0 -1
  314. package/dist/commands/openclaw.d.ts +0 -40
  315. package/dist/commands/openclaw.js +0 -1148
  316. package/dist/commands/openclaw.js.map +0 -1
  317. package/dist/commands/security-audit.d.ts +0 -12
  318. package/dist/commands/security-audit.js +0 -100
  319. package/dist/commands/security-audit.js.map +0 -1
  320. package/dist/lib/hardening.d.ts +0 -18
  321. package/dist/lib/hardening.js +0 -214
  322. package/dist/lib/hardening.js.map +0 -1
  323. package/node_modules/@synap/hub-rest-client/dist/index.cjs +0 -881
  324. package/node_modules/@synap/hub-rest-client/dist/index.d.cts +0 -907
  325. package/node_modules/@synap/hub-rest-client/dist/index.d.ts +0 -907
  326. package/node_modules/@synap/hub-rest-client/dist/index.js +0 -851
  327. package/node_modules/@synap/hub-rest-client/package.json +0 -45
  328. package/node_modules/@synap/hub-rest-client/src/client.ts +0 -1143
  329. package/node_modules/@synap/hub-rest-client/src/errors.ts +0 -30
  330. package/node_modules/@synap/hub-rest-client/src/index.ts +0 -111
  331. package/node_modules/@synap/hub-rest-client/src/setup.ts +0 -77
  332. package/node_modules/@synap/hub-rest-client/src/types.ts +0 -639
@@ -35,6 +35,131 @@ You are connected to a **Synap Data Pod** at `{SYNAP_POD_URL}`. All requests use
35
35
 
36
36
  Your job is to turn unstructured input into a **connected** knowledge graph. Isolated entities are anti-value. Every entity you create should link to at least one other entity.
37
37
 
38
+ ---
39
+
40
+ ## Reflexes — the two habits, every session
41
+
42
+ > Canonical source — MCP instructions and other surfaces derive from this file.
43
+
44
+ You are connected to the user's Synap pod — their sovereign personal data brain
45
+ (the source of truth about their life, work, projects, people, and preferences).
46
+ Two reflexes hold on every session, on every door (MCP, IS, CLI, HTTP):
47
+
48
+ - **Recall — BEFORE any non-trivial task.** Before answering anything about the
49
+ user's own world, and before creating anything, call `synap_ask` (CLI:
50
+ `synap ask`, REST: `POST /api/hub/knowledge/ask`) to recall what the pod
51
+ already knows. Prefer it over your own assumptions or training data — asking
52
+ first also avoids duplicate creates.
53
+ - **Capture — AFTER you learn something durable.** A fact, a decision, a new
54
+ person/company/task, a stated preference — call `synap_capture` (CLI:
55
+ `synap capture`) to write it back. Don't wait to be asked; this is how the
56
+ second brain grows.
57
+
58
+ Run `synap_orient` (CLI: `synap orient`) once per session to see the available
59
+ workspaces, projects, and entity types before acting.
60
+
61
+ **Writes are governed: a `"proposed"` response is normal, never an error.** It
62
+ means the write is queued for the user's review — like a PR, not a failure. Keep
63
+ working; see `writes.md` for the full governance contract and `inline-patterns.md`
64
+ for how to surface a proposal's review link in a Companion reply.
65
+
66
+ **No private scratchpad.** Everything you learn goes into the shared graph, not a hidden note. Capture a proven tool-fact into `knowledge` immediately; PROMOTE it into a curated skill only once it's proven reusable — a skill is a versioned artifact (one capability, when-to-use + do/don't), never an append-anything log.
67
+
68
+ ## Escalation ladder (keep in a corner of your head)
69
+
70
+ You can always escalate — never dead-end on "I can't." Full detail: `escalation-ladder.md`.
71
+
72
+ - **L0 Reflexes** — recall before, capture after, proposed ≠ error
73
+ - **L1 OPERATE on data** — capture, create_entity, link, attach KNOWN facets, sessions
74
+ - **L2 DISCOVER before invent** — list_profiles, list_capabilities, market.search (capability|template|automation|cell)
75
+ - **L3 MUTATE meta-model (proposal-gated)** — only if L2 empty for the need:
76
+ define_role, create_property_def/profile, create_view, create_workspace, market.install.
77
+ **Template FIRST for new domains:** market.search(kind:template) before freehand create_workspace
78
+ - **L4 CRYSTALLIZE after proof** — promote_session_to_playbook, promote_cell_to_renderer, create_playbook.
79
+ Never crystallize a one-off that hasn't succeeded once
80
+
81
+ **Gates:**
82
+
83
+ - Blocked / can't express need → L2 then L3 propose (never dead-end error; never silent invent)
84
+ - Success / repeatable pattern → one structural suggestion (question first if speculative)
85
+ - Capture placement routes to EXISTING lenses only — never invent a workspace from capture
86
+
87
+ ---
88
+
89
+ # Escalation ladder — discover → invent under proposal → crystallize after proof
90
+
91
+ The always-on brief lives in `reflexes.md`. This file is the full HOW when you need more than the corner-of-your-head reminder.
92
+
93
+ ## Why it exists
94
+
95
+ Agents fail in two ways: (1) dead-end ("I can't do that") when the substrate could express the need after discovery or a proposed meta change, and (2) silent invent (minting workspaces/profiles/views without checking what already exists). The ladder is the habit that prevents both. Soft teaching only — no hard tool filtering by tier.
96
+
97
+ ## Levels
98
+
99
+ ### L0 — Reflexes (always)
100
+
101
+ Recall before non-trivial work (`synap_ask` / search). Capture durable learning after. Treat `"proposed"` as success-in-review, not an error. Orient once per session.
102
+
103
+ ### L1 — OPERATE on data
104
+
105
+ Default mode: work with what already exists.
106
+
107
+ - Capture free text, create/update entities, link, attach **known** facets
108
+ - Start/update sessions when the work is a unit with a deliverable
109
+ - Prefer existing profiles, views, capabilities, playbooks over inventing structure
110
+
111
+ ### L2 — DISCOVER before invent
112
+
113
+ When the tool list or current schema doesn't express the need — **search before minting**:
114
+
115
+ 1. `list_profiles` / `list_views` / `list_capabilities({query})` in the active lenses
116
+ 2. `market.search({query, kind?})` over `capability` | `template` | `automation` | `cell`
117
+ 3. Load the relevant skill (`load_skill` / discover_tools) if the HOW is unclear
118
+
119
+ Only if L2 returns empty for the real need do you climb to L3.
120
+
121
+ ### L3 — MUTATE the meta-model (proposal-gated)
122
+
123
+ Extend the substrate so the need becomes expressible. Always governed — expect `"proposed"`.
124
+
125
+ | Need | Prefer | Tool sketch |
126
+ | ------------------ | -------------------- | --------------------------------------------------------------------------------------- |
127
+ | Role/hat missing | Existing role attach | `define_role` only after `list_profiles` empty for that role |
128
+ | Field missing | Existing property | `create_property_def` |
129
+ | Kind missing | Closest parent kind | `create_profile` (extend, don't fork) |
130
+ | View missing | Existing view | `list_views` first, then `create_view` (recovery or proactive) |
131
+ | Domain missing | **Template** | `market.search(kind:template)` → install/propose **before** freehand `create_workspace` |
132
+ | Capability missing | Marketplace | `market.install` (always proposes for agents) |
133
+
134
+ **Template-before-workspace (hard rule in teaching):** new operational domains start as marketplace templates when one fits. Freehand workspace creation is last resort after the four workspace-design conditions hold (`workspace-design.md`). Capture never invents a workspace — placement only routes into existing lenses.
135
+
136
+ ### L4 — CRYSTALLIZE after proof
137
+
138
+ After a one-off has succeeded and is clearly repeatable:
139
+
140
+ - Session that worked → `promote_session_to_playbook`
141
+ - Cell that presents well for a type/slot → `promote_cell_to_renderer`
142
+ - Repeatable process authored deliberately → `create_playbook`
143
+
144
+ Never crystallize a speculative or failed one-off. One structural suggestion at a time; if speculative, ask first (`creative-loop.md`).
145
+
146
+ ## Decision gates (cheat sheet)
147
+
148
+ ```
149
+ Can I do it with existing data/tools?
150
+ yes → L1
151
+ no → L2 discover
152
+ found → use / install (propose) / enable
153
+ empty → L3 propose meta change (never silent invent)
154
+ Did a one-off just succeed and will recur?
155
+ yes → offer L4 (question if speculative)
156
+ no → leave it as a one-off
157
+ ```
158
+
159
+ Blocked path: **never** invent silently; **never** stop at a dead-end error — climb L2→L3 and propose. Success path: one clear structural nudge, not a cascade of schema changes.
160
+
161
+ ---
162
+
38
163
  ## Mental model
39
164
 
40
165
  Synap is a typed knowledge graph. **Reading is one verb (`synap ask`) — it routes for you.** Writing is where you must pick the right lane: the destination is decided by the **KIND** of knowledge, not by whichever workspace happens to be active.
@@ -43,6 +168,8 @@ Synap is a typed knowledge graph. **Reading is one verb (`synap ask`) — it rou
43
168
 
44
169
  Ask yourself: _who does this knowledge serve?_ **There is no private AI scratchpad** — structuring knowledge into a real lane IS your job. Never write a `note` (that's the human's raw inbox); always `capture` into a lane.
45
170
 
171
+ **Known fields → typed create.** If you already know the profileSlug and the values, use `synap create entity` / `synap_create_entity` (or typed `capture --type` for a knowledge entry). Reach for free-text `capture "…"` only for an unstructured blob you haven't parsed — it runs an AI pipeline that can degrade to one flat `note`. 'Always capture into a lane' means _don't leave it unstructured_, not _always use the free-text pipeline_.
172
+
46
173
  | If it… | Lane | Where it goes | Governance |
47
174
  | ------------------------------------------------------------------------------------------------------------------------------- | -------------------- | ---------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- |
48
175
  | **is about the CURRENT WORK** — domain know-how for the project/task you're on (incl. a domain-specific gotcha/lesson/decision) | **Work** _(default)_ | a `knowledge` entity in the **active workspace** (`synap capture --type …`) | proposal-gated (it's the user's real data; the workspace IS the domain — Builder ≠ marketing) |
@@ -51,6 +178,11 @@ Ask yourself: _who does this knowledge serve?_ **There is no private AI scratchp
51
178
 
52
179
  > **Why this matters:** writing to the wrong lane degrades the graph. A gotcha you learned about the **current project** is **Work** (the active workspace — its domain). A best-practice that holds **everywhere** is **Global** (`--global`, pod-wide). A fact about **how the user works** is **User** (pod-wide, inferences proposed). `synap capture` echoes which lane + governance it used; check it.
53
180
 
181
+ > **Read the write outcome — it guides your next move (it never blocks you).** Every write (`capture`, `observe`, `create entity`, `create relation`, `note`) reports one of two outcomes (and `--json` carries `"outcome"`):
182
+ >
183
+ > - **`stored`** → it's **live now**, recallable via `synap ask`.
184
+ > - **`proposed`** → queued for the human's review, **like a git PR — not a failure, not a block.** Keep working: compose a whole graph of proposed changes in one session (reference the proposed entities, link them, add more) — they're staged together and go live when the human approves the batch. The only thing to remember: it's _under review_, so don't tell the user it's already applied. (Inferences about the user and writes to real workspaces are gated by design — expected, normal.)
185
+
54
186
  > **Substrate names (tables under the hood):** _semantic_ = `entities` (the `knowledge` profile, workspace-scoped = domain separation), _episodic_ = `knowledge_facts`, _procedural_ = `knowledge_keys` (pod-wide runbooks). `ask` queries across them so you never pick on read.
55
187
 
56
188
  ### Data layers — the graph itself
@@ -74,12 +206,15 @@ Ask yourself: _who does this knowledge serve?_ **There is no private AI scratchp
74
206
  | `research` | pod | AI | Investigation with sources + conclusion |
75
207
  | `question` | pod | human + AI | Open inquiry, closed when a decision answers it |
76
208
 
209
+ ---
210
+
77
211
  ## Quick reference — 90% of tasks in 30 lines
78
212
 
79
213
  ```bash
80
214
  # CLI (preferred — auth automatic, --json = clean output)
81
- synap orient --json # discover userId + workspaces
82
- synap use <workspace-name-or-id> # set active workspace
215
+ synap orient --json # discover userId + workspaces + projects
216
+ synap lens # where am I? workspace + project + session (this Claude session)
217
+ synap use <workspace-name-or-id> # focus a workspace (this session)
83
218
  synap create entity --profile=task --name="…" --props='{"status":"todo","priority":"high"}' --json
84
219
  synap set entity <id> --props='{"status":"done"}' --json # merge-patch (only changed keys)
85
220
  synap ask "your question" --json # THE read verb — routes to the right store(s) + shows which answered
@@ -101,7 +236,7 @@ or `recall` — `ask` is the door.)
101
236
 
102
237
  ```bash
103
238
  # REST (when no Bash access)
104
- POST /api/hub/entities body: { userId, workspaceId?, profileSlug, title, properties }
239
+ POST /api/hub/entities body: { userId, workspaceId?, profileSlug, title, description?, properties?, content?, projectId?, facets?, source? }
105
240
  PATCH /api/hub/entities/{id} body: { userId, properties } ← deep-merges, send only changed keys
106
241
  POST /api/hub/documents body: { userId, workspaceId?, title, content, entityId? }
107
242
  PATCH /api/hub/documents/{id} body: { userId, title?, content? } ← full content replacement
@@ -121,11 +256,13 @@ synap discover --profiles --json # CLI: profiles only
121
256
  ```
122
257
 
123
258
  ```
124
- GET /api/hub/discover?userId={userId}&workspaceId={workspaceId}
125
- → { profiles: [{ slug, displayName, scope, properties: [{ slug, type, options? }], createCommand }], commands: {...} }
259
+ GET /api/hub/discover?userId={userId}&profileSlugs=task
260
+ → { profiles: [{ slug, displayName, scope, properties: [{ slug, type, required, defaultValue?, constraints?, targetProfileSlug? }], createCommand }], commands: {...} }
126
261
  ```
127
262
 
128
- Call this once at session start. The response includes every system profile and any custom workspace profiles the user has created, each with its full property schema. Use `createCommand` per profile as a copy-paste template. Do not rely on a static property list — it will drift.
263
+ Call the summary tier once at session start, then load only the profile schemas
264
+ needed for a write. Omit `workspaceId` for base/pod schema; add it only to see
265
+ that workspace's overlays. Do not rely on a static property list — it will drift.
129
266
 
130
267
  **Load more detail on demand** (`GET /api/hub/skills/system?sections=<id>`):
131
268
 
@@ -139,13 +276,56 @@ Call this once at session start. The response includes every system profile and
139
276
  | `synap-ui:widget-catalog` | Available widget kinds and their configSchema |
140
277
  | `synap-schema:SKILL` | Creating custom profiles or property definitions |
141
278
 
279
+ ---
280
+
281
+ ## Lenses — where you are vs. what you can reach
282
+
283
+ You don't work "inside a workspace" the way you'd work inside a folder. You operate **across the whole pod**, and you **focus** through up to three composable lenses. **Lenses narrow; they never silo.** Omitting them is legal and common — that's pod-wide.
284
+
285
+ | Lens | What it is | How to set (this session) |
286
+ | ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ |
287
+ | **Project** | a **company or initiative** — the thing that ties the work together (Synap, a client, a launch). The lens you usually _organize by_. | `synap project use <id>` / `clear` |
288
+ | **Workspace** | an **operational domain** — where data lives (Foundation, CRM, Marketing, Finance, Builder). How the work is separated; the default home for writes. | `synap use <name-or-id>` |
289
+ | **Session** | the **work room** for the current goal (holds goal, deliverables, progress) | `synap session start --goal "…"` / `attach <id>` |
290
+
291
+ **How they compose — this is the whole model:**
292
+
293
+ - A **project spans workspaces**: one company/initiative has a Foundation, a CRM, a Marketing, a Finance… each a different operational lens on the _same_ project.
294
+ - A **workspace spans projects**: the Marketing workspace can hold work for several clients/projects at once.
295
+ - **Membership is per-entity, filed on write.** An entity belongs to a project because it was created/filed **under the project lens** — not because its workspace is "in" the project (there is no workspace→project link). So **set the project lens before writing** work that belongs to an initiative, and it composes into that project from any workspace.
296
+ - Compose either way, or both. That's why they're lenses, not folders: **workspaces exist so that development, finance, marketing, and operations don't pile into one undifferentiated place** — they're the separation that makes the work legible.
297
+
298
+ - **The connection is pod-wide by design.** Your MCP/CLI link is _not_ welded to a workspace — reads default pod-wide, writes default to a sensible workspace. Pass a lens to narrow a single call; the lens is a focus, not a fence.
299
+ - **These are per-Claude-session.** Two concurrent Claude sessions can sit on different projects/workspaces/sessions without colliding. `synap use` here rebinds **this** session only.
300
+ - **Inspect anytime:** `synap lens` → the project + workspace + session this session resolves to.
301
+
302
+ ### The "am I in the right place?" reflex
303
+
304
+ **Before the FIRST write of a new unit of work**, check your lens and orient if you're unsure:
305
+
306
+ 1. `synap lens` — am I scoped where this work belongs?
307
+ 2. If unsure what exists → `synap orient` — it returns a **light lens map**: the projects and the workspaces (names + ids), so you see the shape without a data dump. Never guess IDs. Drill into a workspace's profiles or a project's contents only when you actually need them.
308
+ 3. **Connect or create:** if the right project / workspace / session doesn't exist yet, create it. A **session is the normal per-task move**. Creating a **workspace (a new operational domain) is a deliberate, expected move as the work grows** — not something to avoid. A **project, though, is a COMMITMENT WITH GRAVITY**: search existing projects first (`synap orient`) and prefer **linking into an existing one** via `belongs_to_project`. Only create a new project for a real initiative that ties work together — never for a task, plan, repo, or theme (those are entities), and **never for the pod owner's own company** (the company _is_ the pod, not a project inside it). An agent-created project must cite **≥5 existing entities** as evidence or the backend rejects it, and near-duplicate names are rejected with the existing candidates.
309
+
310
+ **Don't re-orient mid-flow.** Once you've oriented and you're in a run of related writes, keep going — re-check only when you **start a new piece of work** or switch domains. The reflex guards the _start_ of work, not every call.
311
+
312
+ ### Notice a missing domain — and offer it
313
+
314
+ Because workspaces are how a company separates its operations, a project is sometimes **missing an operational domain it clearly needs**. If the conversation is squarely about an area — sales, content, finance, hiring, ops — and the active project has **no workspace for it**, say so **once, at the end, in one line**, and offer to set it up:
315
+
316
+ > _"This project doesn't have a Marketing workspace yet — want me to spin one up and capture the essentials?"_
317
+
318
+ If they say yes, provision that **one** domain and run its onboarding interview **with the project lens active** (so its entities file into the project) (see the `agent-os` skill — it handles both the whole-company setup and adding a single domain to an existing project). **Offer, don't auto-build.** One nudge per response, and only when the gap is real — never a checklist of everything the project "could" have. **If the user has already declined a domain (this session or before), drop it — don't re-offer.**
319
+
320
+ ---
321
+
142
322
  ## Synap-first operating mode
143
323
 
144
324
  > **MCP clients** (Claude Desktop, Raycast, OpenClaw with MCP): use `synap_*` tool names — they wrap auth and governance automatically. **REST / HTTP clients**: use the endpoints below.
145
325
 
146
326
  These five rules override default assistant behavior when connected to a Synap pod:
147
327
 
148
- **1. Orient before acting**
328
+ **1. Orient before acting** _(and check your lens — see "am I in the right place?" above)_
149
329
  Run `scripts/orient.sh` or call these endpoints at the start of every session — before searching, before creating, before answering any question about the user's data:
150
330
 
151
331
  ```
@@ -185,17 +365,21 @@ Facts about the user — preferences, team, working style, recurring context —
185
365
 
186
366
  Properties with `valueType: "entity_id"` are typed links to other entities — see **Linking** below.
187
367
 
368
+ ---
369
+
188
370
  ## CLI Data Operations (Bash tool)
189
371
 
190
372
  When Claude Code (or any agent with Bash access) is using this skill, prefer the `synap` CLI over raw HTTP calls — auth is automatic, output is clean JSON, no spinners in `--json` mode.
191
373
 
192
374
  **Session context — set once, never repeat:**
193
375
 
194
- The CLI reads the active pod and workspace from `~/.synap/config.json`. Set context once at the start of a session; all subsequent commands inherit it automatically. Do NOT pass `--pod-url`, `--api-key`, or `--workspace` on every command.
376
+ The CLI inherits your pod + lens automatically; set them once and every later command picks them up. Do NOT pass `--pod-url`, `--api-key`, or `--workspace` on every command. Inside a Claude session, `synap use` / `synap project use` bind **this session's lens** (`~/.synap/lenses/<session_id>.json`) — so concurrent sessions stay independent; outside one, they set the global default (`~/.synap/config.json`).
195
377
 
196
378
  ```bash
197
379
  synap pods use <profile-name> # switch active pod
198
- synap use <workspace-id> # switch active workspace (captures land here — it IS the domain)
380
+ synap use <workspace-id> # focus a workspace (this session) — captures land here; it IS the domain
381
+ synap project use <id> # add the project lens (composable)
382
+ synap lens # inspect: workspace + project + session this session resolves to
199
383
  ```
200
384
 
201
385
  **Always orient first:**
@@ -260,6 +444,19 @@ synap ask "hono routing gotcha" --json
260
444
  `synap capture --type` writes a typed **`knowledge`** entity in the **active workspace** (the Work lane); `ek_type` (gotcha|lesson|decision|reference) discriminates the kind — **one store, type tags, not a residual dump**. It's workspace-scoped, so the active workspace supplies the domain — a Builder gotcha ≠ a marketing one (there is no `engineering_knowledge`). Add **`--global`** to write a pod-wide cross-cutting runbook to `knowledge_keys` instead. A formal **decision RECORD** (rationale, alternatives, superseded-by lifecycle) is a different artifact — use smart `synap capture "<free text>"` or `synap create entity --profile=decision`. Retrieve everything with `synap ask`.
261
445
  Use `capture` for anything worth remembering across sessions and projects.
262
446
 
447
+ **Open (the one display door):**
448
+
449
+ ```bash
450
+ synap open <id> # resolves type automatically, opens in browser
451
+ synap open entity <id> # open entity detail
452
+ synap open proposal <id> # open proposal review
453
+ synap open view <id> # open a view
454
+ synap open cell <typeKey> # open a registered cell by typeKey
455
+ synap open document <id> # open a document
456
+ ```
457
+
458
+ The bare-ID form calls `GET /api/hub/resolve/:id` to determine the type before dispatching. Use this when you don't know what type a UUID is — `synap open <id>` always works.
459
+
263
460
  **Write:**
264
461
 
265
462
  ```bash
@@ -278,6 +475,8 @@ synap set entity <id> --props='{"status":"done"}' --json
278
475
 
279
476
  ---
280
477
 
478
+ ---
479
+
281
480
  ## Scope — default pod-wide
282
481
 
283
482
  **Default: pod-wide.** 13 of 17 system profiles (`note`, `task`, `project`, `event`, `person`, `contact`, `company`, `bookmark`, `article`, `website`, `decision`, `question`, `research`) are pod-scoped — entities you create show up in _every_ workspace the user owns. The backend handles this automatically when the profile is pod-scoped: you don't need to pass `workspaceId`.
@@ -292,6 +491,8 @@ synap set entity <id> --props='{"status":"done"}' --json
292
491
 
293
492
  When you do scope to a workspace, pass `workspaceId` in the create body — the backend respects it. Never pass `workspaceId: null` explicitly to force pod-wide; the profile's `entityScope` decides.
294
493
 
494
+ ---
495
+
295
496
  ## The work flow — question → research → decision → action
296
497
 
297
498
  AI-assisted work has a shape. When the user is actually _thinking about something_, it flows through four structural nodes. Each is a first-class entity. None of these are optional "nice-to-have" labels — they're the graph that makes the work _durable_ and transferrable between AIs.
@@ -400,11 +601,13 @@ User: _"I'm trying to figure out whether we should build our own orchestrator or
400
601
 
401
602
  Don't interrupt the conversation to ask "should I log this as a question?" — just do it and add a one-line trailer at the end of your response:
402
603
 
403
- > (Logged as question on Project Eve. Review: https://studio.synap.live/proposals/…)
604
+ > (Logged as question on Project Eve. Review: synap://open/proposal/…)
404
605
 
405
606
  If the creation was auto-approved (entity.create is on the whitelist), there's no proposal; just show a link to the entity:
406
607
 
407
- > (Logged as question → https://studio.synap.live/entities/ent_question_1)
608
+ > (Logged as question → synap://open/entity/ent_question_1)
609
+
610
+ ---
408
611
 
409
612
  ## Linking — the core principle
410
613
 
@@ -442,8 +645,12 @@ POST /api/hub/relations
442
645
  }
443
646
  ```
444
647
 
648
+ **Link entities you reference back to the thread.** When your reply cites an entity or document, connect it to the current conversation with `link_entity_to_thread` / `link_document_to_thread` — one line of why ("linking this because it's directly relevant to what you're building"). It should feel like keeping notes, not running a pipeline.
649
+
445
650
  For auto-sync mapping, conventional relation types, and edge cases, read **`linking.md`**.
446
651
 
652
+ ---
653
+
447
654
  ## Writing — governance in one paragraph
448
655
 
449
656
  Every write returns a `status` field:
@@ -503,27 +710,158 @@ Rules:
503
710
 
504
711
  Example response to the user:
505
712
 
506
- > I queued **Delete task "Q2 plan review"** for your review. Destructive actions need your approval. Open it: https://studio.synap.live/proposals/prp_abc
713
+ > I queued **Delete task "Q2 plan review"** for your review. Destructive actions need your approval. Open it: synap://open/proposal/prp_abc
507
714
 
508
715
  Auto-approved by default (for agent API keys): `entity.create`, `entity.update`, `document.create`, `relation.create`, `view.create`, `profile.create`, `property_def.create`, `channel.create`, `memory.*`, all reads. Destructive actions (`delete`, `archive`, `purge`) always propose in agent-owned workspaces.
509
716
 
510
717
  For the full whitelist, agent-user semantics, and workspace overrides, read **`governance.md`**.
511
718
 
719
+ ---
720
+
721
+ # Capabilities — discover, run, and the when-blocked reflex
722
+
723
+ Capabilities are the verbs a workspace's connected services and applied
724
+ templates unlock — `gmail_send`, `gmail_search`, `calendar_create`,
725
+ `drive_search`, and so on. They are the bridge between "I can talk about it"
726
+ and "I can actually do it."
727
+
728
+ ## Discover: `list_capabilities`
729
+
730
+ MCP: `synap_list_capabilities`. IS: `list_capabilities`. Takes
731
+ `{ query, kind?, limit? }` (plus `workspaceId`).
732
+
733
+ **Search first — never dump-and-eyeball.** A workspace can carry 100+
734
+ capability entries; call with a `query` describing the action you're after
735
+ ("send email", "search calendar") and read the ranked, compact results. Don't
736
+ fetch the unfiltered list and scan it yourself.
737
+
738
+ Each entry carries:
739
+
740
+ - `name` (the `verbId` you pass to `run_capability`), `label`, backing tool
741
+ - `paramsSchema` — the shape `parameters` must satisfy; check it before calling
742
+ - `enabled` — `true` means it will run right now. `false`/DRAFT means it's
743
+ installed but the user hasn't approved it yet (Settings → Capabilities)
744
+
745
+ ## Run: `run_capability`
746
+
747
+ MCP: `synap_run_capability`. IS: `run_capability`. Pass `verbId` (or
748
+ `skillId`) + `parameters` + `workspaceId`.
749
+
750
+ ```json
751
+ {
752
+ "verbId": "gmail_send",
753
+ "parameters": { "to": "…", "subject": "…", "body": "…" },
754
+ "workspaceId": "{workspaceId}"
755
+ }
756
+ ```
757
+
758
+ Check `paramsSchema` from discovery before calling — a missing required
759
+ parameter is refused, not guessed.
760
+
761
+ **`proposed` is a normal outcome, not a failure.** A capability run can land
762
+ as a proposal exactly like any other governed write (see `governance.md`):
763
+ tell the user why, share the `reviewUrl`, and don't retry.
764
+
765
+ **Provider results can be 200-with-error-body.** A successful HTTP call to
766
+ Gmail/Calendar/Drive can still carry `result.success: false` +
767
+ `result.error` — an auth expiry, a bad recipient, a quota limit. **Always
768
+ check `result.success`/`result.error` in the response before telling the
769
+ user the action worked.** A 200 status is not proof of success here.
770
+
771
+ ## The when-blocked reflex
772
+
773
+ When you cannot do something the user asked for — no matching tool, a
774
+ "not found" verb, "no connection", "not enabled" — do not fabricate a result
775
+ and do not silently give up. Follow this order:
776
+
777
+ 1. **Search first.** `list_capabilities({ query })` for what the user actually
778
+ wants. Capabilities are added over time; don't assume today's tool list is
779
+ the ceiling.
780
+ 2. **Found but blocked?** If the verb exists but is DRAFT (not enabled) or its
781
+ backing connection is missing, tell the user exactly what to do and where:
782
+ "This needs Gmail connected — enable it in Settings → Capabilities" (or
783
+ the equivalent connect deep-link the error hands you). Don't attempt the
784
+ run again until they've acted.
785
+ 3. **Still nothing? Search the marketplace.** `market.search({query, kind?})`
786
+ over what could be _installed_ (capabilities, automations, workspace
787
+ templates, cells) — a cache read, not a live fetch, so it's always fast.
788
+ 4. **Found in the marketplace?** `market.install({slug, kind, version?})`. As
789
+ an agent this ALWAYS lands as a reviewable proposal — never auto-installs,
790
+ even with a grant on the verb itself. Share the `reviewUrl`; don't retry.
791
+ 5. **Truly nothing, anywhere?** That is escalation-ladder **L2 empty → L3**:
792
+ say precisely what's missing (the action, not "I can't"), offer to capture
793
+ the gap, and if the need is structural (new capability package, template,
794
+ automation), propose via marketplace/install or meta tools — never
795
+ dead-end and never fabricate a result.
796
+
797
+ <!-- brief:start -->
798
+
799
+ When blocked (ladder L2→L3): (1) `list_capabilities({query})` first — never
800
+ assume today's list is the ceiling. (2) Found but DRAFT/no connection → tell
801
+ the user exactly what to enable/connect and where; don't retry until they've
802
+ acted. (3) Still nothing → `market.search({query, kind?})`; `market.install`
803
+ on a hit always proposes for an agent. (4) Truly nothing → say precisely
804
+ what's missing, offer to capture the gap or propose L3 structure — never
805
+ fabricate, never silent give-up. Provider 200-with-error-body: always check
806
+ `result.success`/`result.error` before claiming success.
807
+
808
+ <!-- brief:end -->
809
+
810
+ ## Errors — what they mean, what to do next
811
+
812
+ | You see roughly... | Meaning | Next step |
813
+ | ------------------------------------------- | ------------------------------------------------------------------------- | ------------------------------------------------------------------------------- |
814
+ | verb/skill "not found" in this workspace | No capability by that name is registered here | `list_capabilities({query})`; if nothing, tell the user what's missing |
815
+ | capability "not approved" / DRAFT | It exists but the user hasn't enabled it yet | Tell the user to enable it (Settings → Capabilities); don't retry |
816
+ | a required parameter is missing | Your `parameters` didn't satisfy the verb's `paramsSchema` | Re-check `paramsSchema` from discovery, fill the gap, retry once |
817
+ | no connection / credential for this service | The verb needs a connected account (Gmail, Calendar, …) that isn't set up | Hand the user the connect link; don't retry until connected |
818
+ | `status: "proposed"` | Normal governed outcome — this run needs human approval | Share `summary` + `reviewUrl`; don't retry (see `governance.md`) |
819
+ | `status: "denied"` | Workspace policy blocked it outright | Explain the reason; don't retry |
820
+ | provider result with `success: false` | The call reached the provider but the provider itself rejected it | Read `result.error`; tell the user what actually happened, don't report success |
821
+
822
+ ## What NOT to do
823
+
824
+ - Don't dump the full unfiltered capability list and eyeball it — search.
825
+ - Don't retry a `proposed` or `denied` result as if it were a transient error.
826
+ - Don't tell the user an action "worked" without checking `result.success`.
827
+ - Don't invent a capability, verb, or connection that discovery didn't return.
828
+
829
+ ---
830
+
512
831
  ## Core writes
513
832
 
514
- ### Create an entity (always with links)
833
+ ### Create an entity (schema-first, complete intent)
834
+
835
+ Before this call, use `/discover?userId=…&profileSlugs=<kind>` to read the
836
+ real fields, required/default values, constraints and reference targets. Omit
837
+ `workspaceId` for the pod/base schema and normal profile placement; pass it
838
+ only when the user or routing decision explicitly selected that workspace.
515
839
 
516
840
  ```json
517
841
  POST /api/hub/entities
518
842
  {
519
843
  "userId": "{userId}",
520
- "workspaceId": "{workspaceId}",
521
- "profileSlug": "task", // from /profiles — never guess
844
+ "profileSlug": "task", // from /discover — never guess
522
845
  "title": "Weekly team sync",
523
- "properties": { "status": "todo", "projectId": "ent_..." }
846
+ "description": "Recurring planning sync",
847
+ "properties": { "status": "todo", "dueDate": "2026-07-21" },
848
+ "content": "# Agenda\n- Priorities\n- Risks",
849
+ "projectId": "{existingProjectId}",
850
+ "source": "agent"
524
851
  }
525
852
  ```
526
853
 
854
+ The response has legacy `status`/`id` fields plus `writeReceipt`:
855
+ `pending` means a proposal exists and no entity is live; `applied` means the
856
+ reported direct write completed; `partial` means a follow-up (for example a
857
+ facet) failed after the entity applied. Never claim completion from `pending`,
858
+ and only enrich again when the receipt identifies a real missing fact.
859
+
860
+ For several entities, creation-time roles/facets, or relations that need one
861
+ review, submit one `POST /api/hub/capture/graph` plan instead of sequencing
862
+ independent creates. It returns a pending receipt and materializes on human
863
+ approval.
864
+
527
865
  ### Update an entity
528
866
 
529
867
  ```json
@@ -647,6 +985,244 @@ POST /api/hub/threads/{threadId}/messages
647
985
  { "userId": "{userId}", "role": "user", "content": "…" }
648
986
  ```
649
987
 
988
+ ---
989
+
990
+ ## Document embeds — live entities/views/cells inside markdown
991
+
992
+ Documents (`type: "markdown"`) can embed **live, rendering** Synap objects inline, not
993
+ just links. The browser's markdown engine parses a small set of remark **container
994
+ directives** and swaps them for real components — an entity card, a view, or a cell
995
+ — wherever they appear in the prose.
996
+
997
+ **This is a DOCUMENTS-only mechanism.** It is unrelated to the `[[kind:id|label]]`
998
+ inline chips described in `inline-patterns.md` — those render **only** in Companion
999
+ chat replies. Never put a `:::synap-*` directive in a chat reply, and never put a
1000
+ `[[…]]` chip in a document's `content`. Different surface, different grammar.
1001
+
1002
+ ### Syntax
1003
+
1004
+ <!-- brief:start -->
1005
+
1006
+ A container directive: three colons, the directive name, `{attrs}` on the opening
1007
+ line, three colons alone on the closing line.
1008
+
1009
+ ```
1010
+ :::synap-entity{id="ent_abc123"}
1011
+ :::
1012
+ ```
1013
+
1014
+ | Directive | Required attrs | Optional attrs | Renders |
1015
+ | -------------- | --------------------------------------------- | -------------- | ------------------------------------------------- |
1016
+ | `synap-entity` | `id` (entity UUID) | — | Compact entity card (`__entity-block` cell) |
1017
+ | `synap-view` | `viewId` (view UUID) | — | Embedded, read-only view (`__embedded-view` cell) |
1018
+ | `synap-cell` | `instanceId` **OR** (`cellKey` + `cellProps`) | — | A persisted cell instance, or an inline cell ref |
1019
+
1020
+ Only real IDs from prior tool results — never invent one. This is a
1021
+ DOCUMENTS-only grammar: never use it in a chat reply, and never use a
1022
+ `[[kind:id|label]]` chip inside a document's `content`.
1023
+
1024
+ <!-- brief:end -->
1025
+
1026
+ For `synap-cell`: an explicit `instanceId` always wins if present — it renders a
1027
+ persisted cell instance from `/api/hub/cells`. Otherwise the pair `cellKey` +
1028
+ `cellProps` builds an inline ref (`cellRefFromLegacy`). `cellProps` is a JSON string,
1029
+ e.g. `cellProps='{"profileSlug":"task"}'`.
1030
+
1031
+ When you write a document's `content` directly (via `synap_create_document` /
1032
+ `POST /api/hub/documents`), you're writing raw markdown text — quote `cellProps`'
1033
+ JSON with single quotes (`cellProps='{"profileSlug":"task"}'`) so the inner `"`
1034
+ characters don't collide with the directive's own `key="value"` quoting; the
1035
+ markdown renderer (remark-directive) parses this directly, no escaping needed.
1036
+
1037
+ One caveat: if a human later opens the document in the rich-text editor, its
1038
+ Tiptap round-trip serializer re-emits every directive as `key="value"` and
1039
+ **drops any attribute value containing `"` or `}`** (it would otherwise corrupt
1040
+ the directive) — so an inline `cellProps` blob can be silently lost on the next
1041
+ editor save. For a cell you expect to survive editing, create it first
1042
+ (`synap_create_cell` / `POST /api/hub/cells`) and embed it by `instanceId`
1043
+ instead of inlining `cellProps`.
1044
+
1045
+ ### Rules
1046
+
1047
+ - **Only real IDs from prior tool results.** Never invent an entity/view/instance
1048
+ ID. Create or look it up first (`synap_create_entity`, `synap_get_entities`,
1049
+ `synap_create_view`, `synap_create_cell`), then embed the ID you got back.
1050
+ - **Embeds are for DOCUMENTS.** The `[[…]]` inline chips are for Companion chat
1051
+ replies. Do not mix the two grammars across surfaces.
1052
+ - **Embed vs. link:** embed when the reader benefits from seeing the live
1053
+ object in place — a stat card inside a report, the linked meeting entity inside
1054
+ meeting notes, a pipeline view inside a status update. Link (`entities WHERE
1055
+ documentId = ?` attachment, or a plain reference to the ID) when you just need
1056
+ traceability and the reader doesn't need to see it rendered inline — most
1057
+ documents should still be _attached_ to one entity (see `writes.md`) regardless
1058
+ of whether they also embed others inline.
1059
+ - A directive with a missing/invalid required attribute renders a visible error
1060
+ block in the browser (`Error: View ID is required` / `Error: Cell type is
1061
+ required`) — always double-check the ID before writing the directive.
1062
+
1063
+ ### Worked example 1 — meeting notes embedding the meeting entity
1064
+
1065
+ ```json
1066
+ POST /api/hub/documents
1067
+ {
1068
+ "userId": "{userId}",
1069
+ "workspaceId": "{workspaceId}",
1070
+ "title": "Meeting notes — 2026-07-12",
1071
+ "type": "markdown",
1072
+ "entityId": "ent_event_kickoff",
1073
+ "content": "# Kickoff meeting\n\n:::synap-entity{id=\"ent_event_kickoff\"}\n:::\n\n## Decisions\n- Ship the pilot by August 1\n\n## Action items\n- [ ] Draft the rollout plan"
1074
+ }
1075
+ ```
1076
+
1077
+ The event entity renders as a live card at the top of the notes — attendees,
1078
+ time, status stay current even if the entity changes later.
1079
+
1080
+ ### Worked example 2 — status report embedding a pipeline view
1081
+
1082
+ ```json
1083
+ POST /api/hub/documents
1084
+ {
1085
+ "userId": "{userId}",
1086
+ "workspaceId": "{workspaceId}",
1087
+ "title": "Weekly deals update",
1088
+ "type": "markdown",
1089
+ "entityId": "ent_project_eve",
1090
+ "content": "# Weekly update\n\nThree deals moved to negotiating this week.\n\n:::synap-view{viewId=\"view_deals_pipeline\"}\n:::\n\nSee the board above for the live state."
1091
+ }
1092
+ ```
1093
+
1094
+ ### Worked example 3 — report embedding an inline stat cell
1095
+
1096
+ ```json
1097
+ POST /api/hub/documents
1098
+ {
1099
+ "userId": "{userId}",
1100
+ "workspaceId": "{workspaceId}",
1101
+ "title": "Q2 task summary",
1102
+ "type": "markdown",
1103
+ "entityId": "ent_project_eve",
1104
+ "content": "# Q2 summary\n\n:::synap-cell{cellKey=\"stat-card\" cellProps='{\"profileSlug\":\"task\"}'}\n:::\n\nOpen tasks are trending down."
1105
+ }
1106
+ ```
1107
+
1108
+ Note the mixed quoting: the directive's own attribute values use double quotes
1109
+ (`cellKey="stat-card"`), so the outer `cellProps` value uses single quotes to hold
1110
+ its JSON — the JSON itself must not contain any `"` once flattened into the
1111
+ attribute string, or the serializer will drop it. When in doubt, prefer a
1112
+ persisted `instanceId` over inline `cellProps`.
1113
+
1114
+ ---
1115
+
1116
+ ## The Creative Loop — do once, then crystallize to reusable config
1117
+
1118
+ The core rhythm of real work in Synap: **do the thing once by hand, then crystallize what worked into reusable configuration.** You author; the user curates. Every crystallization is governed — a `proposed` result is the normal, successful outcome, never an error.
1119
+
1120
+ The loop has three moves that compound. Each takes a one-off act and, if it's worth repeating, turns it into standing structure:
1121
+
1122
+ | Do-once (author) | → Crystallize (curate) | Tool |
1123
+ | --------------------------------------- | ----------------------------------- | ----------------------------- |
1124
+ | Work a multi-step goal in a **session** | → a **playbook** (the process) | `promote_session_to_playbook` |
1125
+ | Show a result in a **cell** | → a **renderer** for an entity type | `promote_cell_to_renderer` |
1126
+
1127
+ ### 1. Open a session for real work
1128
+
1129
+ When the task is a unit of work with a deliverable — research, a build, an investigation, a sprint — and there's no active session, **`start_session`** with a clear `goal` and `expectedOutputs`. The session is the spine that accrues results (see the focus-sessions skill). Don't open one for a one-shot lookup or a casual reply.
1130
+
1131
+ ### 2. Create a cell to REPORT — don't dump data into chat
1132
+
1133
+ When you have something to _show_ the user — a list of leads, a summary, a comparison, a chart — author a **cell** with `create_cell` instead of pasting rows into the message.
1134
+
1135
+ - **Declare the data intent in `rendererSource`; do NOT pre-fetch.** Cells use a dynamic data-binding SDK: you describe what the cell needs (e.g. "the open leads in this workspace"), and the runtime binds the live data at render time. Never fetch rows yourself and inline them — that snapshot goes stale and defeats the cell.
1136
+ - Keep one cell to one job. A good, focused cell is the raw material for the next move.
1137
+
1138
+ ### 3. Promote a good cell to a renderer — recurring presentation
1139
+
1140
+ When a cell is a _good, recurring way to present a whole entity type or step_ — e.g. every `bookmark`'s detail view, every `lead`'s list row — promote it with `promote_cell_to_renderer`:
1141
+
1142
+ - Pick the `profileSlug` (the entity type), the `slot` (`list` | `detail` | `dashboard`), and the `cellKey` from `create_cell`.
1143
+ - This is **governed**: for you it returns `{ status: "proposed", proposalId }`. That is the point — you author the renderer, the user reviews and curates it before it becomes every entity's view. Surface the proposal plainly ("I've proposed this as the detail view for bookmarks — review it when you like"), don't treat it as a failure.
1144
+ - Use `scope: "pod"` only when the presentation should apply in every workspace; default to workspace scope.
1145
+
1146
+ ### 4. Promote a finished session to a playbook — recurring process
1147
+
1148
+ When the session is done **and the work was a repeatable process** (not a one-off), promote it with `promote_session_to_playbook({ sessionId })`. This captures the goal, tasks, expected outputs, and steps as a reusable session template — so next time the process starts pre-built instead of from scratch.
1149
+
1150
+ - Do this at the _end_, once the promised outputs are produced and verified.
1151
+ - Judge repeatability honestly: a bespoke, never-again investigation is not a playbook. A "weekly competitor scan" or "new-client onboarding" is.
1152
+ - Governed like the others — `promoted` (applied) or `proposed` (awaiting review) are both normal.
1153
+
1154
+ ### The symmetry
1155
+
1156
+ Sessions and cells are the two things you _do_; playbooks and renderers are the two things you _keep_. The instinct to build: **first do it once concretely, watch it work, then offer to crystallize it** — and let the user decide what becomes standing config. Never crystallize speculatively before the one-off has proven itself.
1157
+
1158
+ This is escalation ladder **L4**: crystallize only after proof. Blocked/missing structure climbs L2→L3 first (`escalation-ladder.md`); L4 is the success path, not a substitute for discovery.
1159
+
1160
+ ---
1161
+
1162
+ ## Garden the graph — writes tell you their impact (the circling of action)
1163
+
1164
+ A write is no longer blind. When you create or update an entity, the response tells you what it did to the graph — **READ it and act**, so the graph stays connected instead of accumulating isolated, duplicated nodes.
1165
+
1166
+ **On create**, the response carries a `resolution` block:
1167
+
1168
+ - `existingSameProfile` — an entity with this exact name ALREADY exists as the same profile. Prefer **updating** it (`synap set entity <id>` · `PATCH /api/hub/entities/:id` · `update_entity`) over creating a duplicate.
1169
+ - `autoConnected` — same-name entities of a DIFFERENT profile are facets of one real thing, and the system has already woven them together with a `same_subject` relation. Acknowledge it; add a more specific relation if the real relationship is narrower than "same subject".
1170
+ - `suggestions` — entities worth linking. Create the relations that genuinely apply (`synap create relation` · `POST /api/hub/relations` · `create_relation`).
1171
+
1172
+ **On update**, the response carries an `impact` block — the entity's immediate relation neighbors. Read it and resolve secondary effects: supersede a now-stale entity, update its dependents, re-link.
1173
+
1174
+ **The circling of action** for any structural write (updating the vision, the architecture, a decision record, a codebase map):
1175
+
1176
+ 1. **Write** — the response shows collisions + connections; don't ignore it.
1177
+ 2. **If it already exists** (`existingSameProfile`) → update it, don't duplicate.
1178
+ 3. **The same-name facets are auto-woven** (`autoConnected`) → extend the links where the relationship is more specific than `same_subject`.
1179
+ 4. **Act on `suggestions`** → link what genuinely relates.
1180
+ 5. **On update** → resolve the `impact` neighbors (supersede / update / re-link).
1181
+
1182
+ The principle: **more data is better when it is structured.** The graph now helps you keep it structured — never leave an entity isolated, never blindly overwrite. (Matching is exact-name today; it will deepen over time — so still link deliberately when you know two things relate but their names differ.)
1183
+
1184
+ ---
1185
+
1186
+ ## Decide the next action — advance the goal, don't just answer
1187
+
1188
+ You are an adjunct, not a reply box. A good assistant doesn't wait to be told the next step — after you answer, you ask **what is the highest-leverage next move toward the goal, and should I make it now?** — then you make it. Proactivity is this habit, run every turn; it is not a tone.
1189
+
1190
+ ### The session is the spine of real work
1191
+
1192
+ A **focus session** is a goal-bound work room. It holds the goal, the promised outputs (deliverables), the tools/skills in play, and what's been produced so far — so it answers both _"where am I"_ and _"what am I working toward"_. Operate it with the triplet:
1193
+
1194
+ | Move | CLI | REST | IS tool |
1195
+ | ------------------------- | ---------------------------------------- | ------------------------------------------- | ---------------- |
1196
+ | open a session | `synap session start --goal "…"` | `POST /api/hub/focus-sessions` | `start_session` |
1197
+ | read it | `synap session get <id>` | `GET /api/hub/focus-sessions/:id` | `get_session` |
1198
+ | see active ones | `synap session list` | `GET /api/hub/focus-sessions?status=active` | `list_sessions` |
1199
+ | record progress / outputs | `synap session update <id> --progress N` | `PATCH /api/hub/focus-sessions/:id` | `update_session` |
1200
+
1201
+ - If you're already in a session (or a `## Active Session` block is in your context) → that's your frame. Read its goal and the gap between _promised outputs_ and _what's produced_, and pursue that gap.
1202
+ - If the user's intent is a **unit of work with a deliverable** (research, a plan, a build, an investigation) and no session is active → **open one** with a clear goal + expected outputs, so the work has a spine that accrues results. Don't open a session for a one-shot lookup or a casual reply.
1203
+
1204
+ ### Decide ONE move, then make it
1205
+
1206
+ Pick the single highest-leverage next action toward the goal — not a checklist: answer · capture/structure what was said · create a task · link entities · advance or produce a promised deliverable · update the session's progress · propose an automation for a repeating pattern.
1207
+
1208
+ **Spin off a branch / sub-agent** when there's side-work that advances the goal but would **bloat the main channel** — e.g. "go find best practices for X", a deep research dive, a parallel investigation. Keep the main thread clean and let the branch do the heavy lifting and report back. Judge by complexity + channel hygiene, not by a fixed category.
1209
+
1210
+ **Land it through governance — act, don't just suggest.** When the move is clear, _make_ it: create the task, the research entity, the branch, the link, the session update. Every write passes through governance — it either auto-applies (safe, whitelisted writes) or becomes a one-click review. Either way, making the move is the safe path. Don't downgrade a clear next action into "you might want to…". Say _why_ in one line, then act.
1211
+
1212
+ **Reflect back into the session.** When work lands, update the session's progress and link what you produced — so the next turn reads a richer state and the loop compounds.
1213
+
1214
+ **Know when to stop.** When the promised outputs are produced and verified, the goal is done — say so and stop. Surface a _real_ next move when there is one; stay quiet when there isn't. Never manufacture busywork to seem active.
1215
+
1216
+ ### The nudge vs. propose line
1217
+
1218
+ A concrete next action toward a _known_ goal → propose it (gated). A _speculative_ restructuring the user hasn't asked for (a new profile, a new view, splitting a workspace) → raise it as a question and let them decide.
1219
+
1220
+ ### If you are a coding / terminal agent working _in_ a repo
1221
+
1222
+ Same habit, one addition: before you finish a piece of work, ask **"what's the next action, and what belongs in Synap?"** A decision you made, a gotcha you hit, a follow-up the work revealed, a task it spun off — capture it (`synap capture` / `synap note` / a task) and, when the work is a real unit, track it in a session. The point of the second brain is that the _next_ agent (or you, tomorrow) starts from what this turn learned instead of re-deriving it.
1223
+
1224
+ ---
1225
+
650
1226
  ## Reading
651
1227
 
652
1228
  **Start with `ask` — the one routed read door.** It classifies the question and
@@ -687,8 +1263,12 @@ GET /api/hub/graph/traverse?entityId={id}&maxDepth=2&workspaceId={id}
687
1263
  GET /api/hub/memory?userId={userId}&query={keywords}
688
1264
  ```
689
1265
 
1266
+ **Never claim absence without searching this turn.** Asked "what do you know about X", "is there an X", "anything on X" — you MUST `ask`/`search_unified` for X first (and `list_entities` on the matching profile for "how many / list all X"). Only after a search returns nothing may you say "I didn't find anything matching X" — never assert "X does not exist." A just-created entity is searchable within seconds, so a confident "nothing exists" without a search this turn is a hard failure.
1267
+
690
1268
  No SQL joins. The graph is the join.
691
1269
 
1270
+ ---
1271
+
692
1272
  ## Multi-entity capture from free-form text
693
1273
 
694
1274
  When the user pastes a block of unstructured content (a meeting transcript, an email, a LinkedIn bio), use the capture pipeline instead of chaining manual creates:
@@ -700,6 +1280,8 @@ POST /api/hub/capture/execute → commits (after user confirms)
700
1280
 
701
1281
  The pipeline extracts multiple entities with their relations in one LLM call. Read **`capture.md`** for the full flow.
702
1282
 
1283
+ ---
1284
+
703
1285
  ## Worked examples
704
1286
 
705
1287
  ### Example 1 — "Remind me to send the proposal to Acme on Friday"
@@ -754,6 +1336,8 @@ The pipeline extracts multiple entities with their relations in one LLM call. Re
754
1336
 
755
1337
  3. If the user said why ("interesting for the onboarding project"), also create a relation to that project — never drop the reason as a plain comment, turn it into a link.
756
1338
 
1339
+ ---
1340
+
757
1341
  ## CRM Workspaces — 4-Entity Model
758
1342
 
759
1343
  Some workspaces use a CRM data structure with four entities: `person` and `company` (identity records), `deal` (pipeline record), and `client` (post-win relationship marker). Understand this pattern when proposing lead captures, deal updates, or campaign membership.
@@ -860,7 +1444,21 @@ POST /api/hub/relations
860
1444
 
861
1445
  This model enables renewals (new deal linking to existing client), multi-stakeholder deals (multiple `linked_to_deal` relations per deal), campaigns with mixed entity types (persons + companies + deals as members), and clean churn tracking. It matches Synap's core pattern: entities + relations = graph.
862
1446
 
863
- ## Common mistakes
1447
+ ## Resolution discipline — resolve BEFORE you create
1448
+
1449
+ Extracting people/companies from a digest, an email/DM thread, or notes is a RESOLUTION problem, not a creation problem. Follow this method every time (it is generic — the specific team roster and known aliases are DATA you fetch, never hardcoded):
1450
+
1451
+ 1. **Search first, once (batched).** Before proposing any person/company, run a SINGLE batched query (`search_unified`, plus `list_entities` if needed) that covers ALL candidate names, handles, emails and aliases at once. Never run one query per candidate — that is a cost failure.
1452
+ 2. **Reuse on match → add an alias.** If a candidate matches an existing entity, reference the existing entity by its real id (e.g. `existingEntityId` on `propose_entity_graph`) and add the newly-seen surface form (a handle, an alternate spelling) to the entity's `aliases` — do NOT create a second `person`/`company`. The `person` profile carries `email`, `discord-handle` and `aliases` for exactly this.
1453
+ 3. **Never placeholder.** Never mint an entity whose identity is unstated — "Not publicly disclosed", "unknown", "TBD", an empty name, or a bare handle with no person behind it. Fold that unstated thing into the description of a related entity instead.
1454
+ 4. **Team is not a contact.** Internal senders — your own side of a `client-comms` thread (the agency's own team) — are NEVER captured as the client's contacts. Only the external party becomes a person/company/deal. Team roster may appear in `orient.teamRoster` — treat as internal.
1455
+ 5. **Connect the graph.** Every entity you propose should attach via at least one relation (person `works_at` company, person `linked_to_deal` deal, …). A name mentioned only in passing, with no relation and no stated identity, is not worth capturing as its own entity.
1456
+
1457
+ An extraction that creates a duplicate "Sarah Chen", or a `person` titled "Unknown sender", is a failure — resolve, reuse, and alias instead.
1458
+
1459
+ ---
1460
+
1461
+ ## Common mistakes — core data operations
864
1462
 
865
1463
  1. **Creating orphan entities.** Always connect to at least one other entity on creation. Search first; if nothing links, reconsider whether this should be memory.
866
1464
  2. **Guessing profile slugs.** Always `GET /profiles` first. `deal`, `capture`, and custom profiles may not exist in this workspace.
@@ -870,6 +1468,10 @@ This model enables renewals (new deal linking to existing client), multi-stakeho
870
1468
  6. **Not knowing your userId.** Use `{SYNAP_USER_ID}` from the env (set by `synap connect`). Or call `GET /api/hub/users/me` → `.id` once and cache it. Never hardcode or guess.
871
1469
  7. **Skipping the search step.** Duplicates degrade the graph more than missing data.
872
1470
  8. **Forgetting that `GET /channels/personal` needs `hub-protocol.write`** scope — it's get-or-create, not a pure read.
1471
+ 9. **Routing known-structure data through free-text capture.** If you already know the profileSlug + fields, create the entity directly — smart capture can degrade to a single flat note.
1472
+ 10. **Paragraph session goals.** The goal is one line; put detail and deliverables in expectedOutputs.
1473
+
1474
+ ---
873
1475
 
874
1476
  ## AI Inline Patterns — reference entities in your replies
875
1477
 
@@ -895,6 +1497,23 @@ When the user is interacting with Synap's AI Companion (the in-browser chat pane
895
1497
  - **Only in Companion replies.** These patterns are silently ignored in non-companion channels, documents, and memory. Do not use them there.
896
1498
  - **Combine with prose.** Don't lead with a chip — embed it naturally: `"Here are your open deals → [[view:xyz|Deals Pipeline]] · [[open:side|view:xyz]]"`
897
1499
 
1500
+ ### Proposals
1501
+
1502
+ There is no `[[open:…|proposal:…]]` chip — `open`'s `resourceType` only accepts
1503
+ `entity`, `view`, `doc`, `cell`, `channel`. A proposal is not one of those, so
1504
+ never invent that form.
1505
+
1506
+ When a write returns `status: "proposed"`, the response also carries a
1507
+ `reviewUrl` (a full Synap Studio URL). Surface it as a plain markdown link, and
1508
+ add one sentence explaining why the write was proposed instead of auto-applied:
1509
+
1510
+ > Queued the task deletion for your review — destructive actions always need approval: [Review proposal](https://pod.example.com/proposals/prp_abc)
1511
+
1512
+ `"proposed"` is normal, not an error — don't apologize for it or wait for the
1513
+ user to approve before continuing the conversation.
1514
+
1515
+ ---
1516
+
898
1517
  ## Focus Sessions — Goal-Bound Work Rooms
899
1518
 
900
1519
  A **focus session** is a named, multi-step work room where you and AI agents collaborate on a specific goal. Use one whenever the work has a clear end state, will take more than one exchange, or involves multiple agents.
@@ -939,9 +1558,418 @@ synap session close <id> --workspace <id> [--recap "what was done"] # close +
939
1558
 
940
1559
  Note: `synap session start` creates a session directly (the agent-facing path). All hub-protocol writes are governance-gated server-side; the in-browser AI companion surfaces session creation through the proposal flow.
941
1560
 
1561
+ **MCP door**: after `synap_start_session` returns, call `synap_get_channel` to get a personal channel for the session, then `synap_post_message` with `triggerAI:true` to dispatch the IS agent for autonomous work on the goal. The agent's produced entities link back to the session via the graph.
1562
+
942
1563
  **Discoverability**: the `active-sessions` bento widget is on the default home dashboard. Sessions group their related proposals under a shared `correlationId` in the Proposal Review Board.
943
1564
 
944
- ## When you need more
1565
+ ---
1566
+
1567
+ # Automations
1568
+
1569
+ Create workflow automations that trigger automatically based on events, schedules, or webhooks.
1570
+
1571
+ ## Automation Structure
1572
+
1573
+ Every automation has:
1574
+
1575
+ 1. **Trigger** (exactly one) — what starts the automation
1576
+ 2. **Steps** (one or more) — commands, conditions, delays, outputs connected in a flow
1577
+
1578
+ ## Trigger Types
1579
+
1580
+ | Type | Config | Example |
1581
+ | --------- | ---------------------------- | ------------------------------------ |
1582
+ | `event` | `{ eventPattern, filters? }` | Entity created with specific profile |
1583
+ | `cron` | `{ expression }` | Daily at 9am: `"0 9 * * *"` |
1584
+ | `webhook` | `{ webhookSubscriptionId }` | External service sends data |
1585
+ | `manual` | `{}` | User-triggered from UI |
1586
+
1587
+ ### Event Patterns
1588
+
1589
+ Format: `{subjectType}.{action}.completed`
1590
+
1591
+ Common patterns:
1592
+
1593
+ - `entity.create.completed` — entity created and persisted
1594
+ - `entity.update.completed` — entity updated
1595
+ - `entity.delete.completed` — entity deleted
1596
+ - `document.create.completed` — document created
1597
+ - `document.update.completed` — document updated
1598
+
1599
+ Filters narrow the event to specific conditions:
1600
+
1601
+ ```json
1602
+ {
1603
+ "eventPattern": "entity.create.completed",
1604
+ "filters": { "profileSlug": "task", "metadata.priority": "high" }
1605
+ }
1606
+ ```
1607
+
1608
+ ### Cron Expressions
1609
+
1610
+ Standard 5-field cron (minute hour day month weekday):
1611
+
1612
+ - `"0 9 * * *"` — daily at 9am
1613
+ - `"0 9 * * MON"` — every Monday at 9am
1614
+ - `"*/30 * * * *"` — every 30 minutes
1615
+ - `"0 0 1 * *"` — first day of month at midnight
1616
+
1617
+ ## Step Types
1618
+
1619
+ ### command
1620
+
1621
+ Execute an intelligence command. Reference by `commandId` or describe inline.
1622
+
1623
+ ```json
1624
+ {
1625
+ "id": "extract",
1626
+ "type": "command",
1627
+ "data": {
1628
+ "commandTitle": "Extract key entities",
1629
+ "inputMapping": {
1630
+ "content": "{{trigger.payload.entity.content}}",
1631
+ "context": "{{trigger.payload.entity.name}}"
1632
+ }
1633
+ }
1634
+ }
1635
+ ```
1636
+
1637
+ **Input mapping** uses template syntax:
1638
+
1639
+ - `{{trigger.payload.*}}` — data from the triggering event
1640
+ - `{{steps.<stepId>.output.*}}` — output from a prior step
1641
+ - `{{loop.item}}` — current item in a loop
1642
+
1643
+ ### condition
1644
+
1645
+ Branch the flow based on a boolean expression.
1646
+
1647
+ ```json
1648
+ {
1649
+ "id": "check-priority",
1650
+ "type": "condition",
1651
+ "data": {
1652
+ "label": "High priority?",
1653
+ "expression": "trigger.payload.entity.metadata.priority === 'high'",
1654
+ "trueLabel": "Yes",
1655
+ "falseLabel": "No"
1656
+ }
1657
+ }
1658
+ ```
1659
+
1660
+ Conditions have two output handles: `yes` and `no`. Connect subsequent steps to the appropriate handle.
1661
+
1662
+ ### delay
1663
+
1664
+ Wait before continuing.
1665
+
1666
+ ```json
1667
+ {
1668
+ "id": "wait",
1669
+ "type": "delay",
1670
+ "data": { "duration": "5m", "label": "Cool down" }
1671
+ }
1672
+ ```
1673
+
1674
+ Supported durations: `30s`, `5m`, `1h`, `1d`, `1w`.
1675
+
1676
+ ### output
1677
+
1678
+ Terminal action — the end result of the automation.
1679
+
1680
+ ```json
1681
+ {
1682
+ "id": "notify",
1683
+ "type": "output",
1684
+ "data": {
1685
+ "label": "Send notification",
1686
+ "outputType": "notification",
1687
+ "config": {
1688
+ "message": "New high-priority task: {{trigger.payload.entity.name}}"
1689
+ }
1690
+ }
1691
+ }
1692
+ ```
1693
+
1694
+ Output types:
1695
+
1696
+ - `notification` — in-app notification to the user
1697
+ - `entity_create` — create a new entity (config: `{ profileSlug, title, properties }`)
1698
+ - `entity_update` — update an existing entity (config: `{ entityId, properties }`)
1699
+ - `webhook` — POST to external URL (config: `{ url, headers?, body }`)
1700
+ - `channel_message` — post a message to a channel (config: `{ channelId, content }`)
1701
+
1702
+ ### loop
1703
+
1704
+ Iterate over a collection from a prior step.
1705
+
1706
+ ```json
1707
+ {
1708
+ "id": "for-each-result",
1709
+ "type": "loop",
1710
+ "data": {
1711
+ "label": "For each search result",
1712
+ "iteratorExpression": "steps.search.output.results",
1713
+ "itemVariable": "item"
1714
+ }
1715
+ }
1716
+ ```
1717
+
1718
+ Inside the loop, reference `{{loop.item}}` for the current element.
1719
+
1720
+ ## Connecting Steps
1721
+
1722
+ Steps are connected via `dependsOn` (which step must complete first) and optional `conditionBranch` (which branch to follow from a condition).
1723
+
1724
+ ```json
1725
+ {
1726
+ "steps": [
1727
+ { "id": "check", "type": "condition", "data": {...} },
1728
+ { "id": "notify-high", "type": "output", "data": {...}, "dependsOn": ["check"], "conditionBranch": "yes" },
1729
+ { "id": "log-normal", "type": "output", "data": {...}, "dependsOn": ["check"], "conditionBranch": "no" }
1730
+ ]
1731
+ }
1732
+ ```
1733
+
1734
+ ## Discovering Commands
1735
+
1736
+ Before creating automations with command steps, call `list_commands` to discover available intelligence commands in the workspace. Use the command `id` in the step's `commandId` field.
1737
+
1738
+ If no suitable command exists, you can leave `commandId` empty and set `commandTitle` + `inputMapping` — the execution engine will use the title as a prompt template.
1739
+
1740
+ ## Vault References
1741
+
1742
+ Automation configs that need secrets (API keys, auth tokens) should use vault references instead of hardcoded values:
1743
+
1744
+ - `vault://secret-uuid` — resolves to the full secret value at runtime
1745
+ - `vault://secret-uuid/field-name` — resolves a specific field from a JSON secret
1746
+
1747
+ Only server-encrypted secrets can be resolved by the automation engine. The user must store the credential in the vault first.
1748
+
1749
+ ## Best Practices
1750
+
1751
+ 1. **Keep it simple** — Start with trigger → command → output. Add complexity only when needed.
1752
+ 2. **Name clearly** — Use descriptive labels: "When task created with high priority" not "Trigger 1".
1753
+ 3. **One purpose** — Each automation should do one thing well. Compose multiple automations rather than building one complex flow.
1754
+ 4. **Filter early** — Use trigger filters to avoid unnecessary execution. Don't use a condition step when a trigger filter suffices.
1755
+ 5. **Test first** — Create automations as `draft` status. Let the user review the flow visualization before activating.
1756
+
1757
+ ## Example: Auto-archive completed tasks
1758
+
1759
+ ```json
1760
+ {
1761
+ "name": "Auto-archive completed tasks",
1762
+ "description": "When a task status changes to 'done', archive it after 24 hours",
1763
+ "trigger": {
1764
+ "type": "event",
1765
+ "config": {
1766
+ "eventPattern": "entity.update.completed",
1767
+ "filters": { "profileSlug": "task", "metadata.status": "done" }
1768
+ }
1769
+ },
1770
+ "steps": [
1771
+ {
1772
+ "id": "wait-24h",
1773
+ "type": "delay",
1774
+ "data": { "duration": "1d", "label": "Wait 24h" }
1775
+ },
1776
+ {
1777
+ "id": "archive",
1778
+ "type": "output",
1779
+ "data": {
1780
+ "label": "Archive task",
1781
+ "outputType": "entity_update",
1782
+ "config": {
1783
+ "entityId": "{{trigger.payload.entity.id}}",
1784
+ "properties": { "archived": true }
1785
+ }
1786
+ },
1787
+ "dependsOn": ["wait-24h"]
1788
+ }
1789
+ ]
1790
+ }
1791
+ ```
1792
+
1793
+ ## Example: Notify on high-priority tasks
1794
+
1795
+ ```json
1796
+ {
1797
+ "name": "High-priority task alerts",
1798
+ "description": "Send a notification when a high-priority task is created",
1799
+ "trigger": {
1800
+ "type": "event",
1801
+ "config": {
1802
+ "eventPattern": "entity.create.completed",
1803
+ "filters": { "profileSlug": "task" }
1804
+ }
1805
+ },
1806
+ "steps": [
1807
+ {
1808
+ "id": "check-priority",
1809
+ "type": "condition",
1810
+ "data": {
1811
+ "label": "High priority?",
1812
+ "expression": "trigger.payload.entity.metadata.priority === 'high'"
1813
+ }
1814
+ },
1815
+ {
1816
+ "id": "notify",
1817
+ "type": "output",
1818
+ "data": {
1819
+ "label": "Alert: high-priority task",
1820
+ "outputType": "notification",
1821
+ "config": {
1822
+ "message": "New urgent task: {{trigger.payload.entity.name}}"
1823
+ }
1824
+ },
1825
+ "dependsOn": ["check-priority"],
1826
+ "conditionBranch": "yes"
1827
+ }
1828
+ ]
1829
+ }
1830
+ ```
1831
+
1832
+ ---
1833
+
1834
+ # Diagnosing what an AI did — the runs door
1835
+
1836
+ When a capture, automation, playbook, or session **didn't do what you (or the user) expected** — a facet wasn't attached, an entity landed in the wrong workspace, a step failed silently — do **not** guess or apologize. Every AI run leaves a trace. Read it.
1837
+
1838
+ ## The one tool
1839
+
1840
+ `synap_diagnose` — the unified view of what an AI did across flows.
1841
+
1842
+ - **No args** → the recent run feed (automation · playbook · capture · session), newest first. Each run has an `id`, `flowType`, `status`, and `flowName`.
1843
+ - **`runId` + `flowType`** → that run's **activity timeline**. For a **capture** run this is its decision + trace events: for each thing that was dropped/coerced, a machine-readable `reason` **and an actionable `fixHint`**.
1844
+
1845
+ (CLI equivalent for the operator: `synap diagnose` and `synap diagnose <captureId> --flow capture`.)
1846
+
1847
+ ## The reflex
1848
+
1849
+ > "The capture didn't attach the client role" → `synap_diagnose({ runId: <captureId>, flowType: "capture" })` → read the `capture_trace` rows → act on the `fixHint`.
1850
+
1851
+ A capture's `id` **is** its correlationId — the same id stamped on the entities it created and returned to you as `captureId`. So you always have the key to diagnose your own last capture.
1852
+
1853
+ ## Reading a capture trace
1854
+
1855
+ Each `capture_trace` activity item names a pipeline stage (`component`), why it stopped (`reason`), and what to do (`fixHint`). The common ones:
1856
+
1857
+ | reason | what happened | what to do |
1858
+ | -------------------------- | ----------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- |
1859
+ | `not_in_creatable_catalog` | a role/kind you asked to create isn't a creatable entity kind — it's a **facet** (client, partner, prospect, investor…) | resolve the real entity first, then `attach_facet` for the role — never a second entity for a hat |
1860
+ | `kind_mismatch` | the facet's `applicableKinds` didn't include the target entity's kind | attach the role to an entity of a kind the role applies to |
1861
+ | `slug_coerce` | a profileSlug was normalized/renamed to the canonical one | use the canonical slug next time (see `list_profiles`) |
1862
+ | `materialize_skip` | an operation was skipped (dedup or a missing dependency) | check the dedup match; re-capture only what's genuinely new |
1863
+
1864
+ If a run carries a `channelId` (playbook / session / automation), its message-level story lives in that channel — the timeline points you there rather than duplicating it.
1865
+
1866
+ ## Why this matters
1867
+
1868
+ The point of the flywheel is that mistakes are **visible and fixable**, not silent. If you can see what happened, you can correct it — and every correction teaches the routing. Reach for `synap_diagnose` before you conclude "it didn't work."
1869
+
1870
+ ---
1871
+
1872
+ ## Workspace design — is this concern a WORKSPACE, or something smaller?
1873
+
1874
+ Before you create a workspace, run the decision rule. A workspace (an operational **domain**) is the heaviest structure in the pod — it owns kinds, confers roles, carries its own team and automations. Most new concerns are NOT domains; they are a **hat**, an **initiative**, or a **stage**. Creating a workspace for one of those is the anti-pattern that fragments the graph. Decide first, then create.
1875
+
1876
+ ## The decision rule — a concern earns a workspace ONLY if ALL FOUR hold
1877
+
1878
+ 1. **Owns kinds** — it is source-of-truth for a noun nothing else owns (CRM owns `person`/`company`; Operations owns `engagement`/`deliverable`). If it only _reads_ or _annotates_ another domain's kinds, it is not a domain.
1879
+ 2. **Own team** — a distinct set of operators/collaborators works it (separation of _who_, not just _what_).
1880
+ 3. **Native automations/tools** — it runs behavior its neighbors don't (its own capabilities, playbooks, triggers).
1881
+ 4. **Stable** — it persists across clients and campaigns. If it is per-client or per-campaign, it is time-bound, not a domain.
1882
+
1883
+ **All four, or it is not a workspace.** Then fork it to the right lighter structure:
1884
+
1885
+ | If the concern is… | It is a… | Substrate | Example |
1886
+ | ----------------------------------------------------------- | -------------- | -------------------------------------- | ------------------------------------------- |
1887
+ | a **role/hat** an existing entity wears in a domain | **Facet** | `attach_facet` (`profileKind: "role"`) | `client`, `sponsor`, `prospect`, `investor` |
1888
+ | a **cross-cutting, time-bound initiative** spanning domains | **Project** | `create_project` (a lens) | a campaign, an engagement, a launch |
1889
+ | a **stage/filter WITHIN a domain** | **State/View** | a `status` property def + a view | pipeline stage, "active"/"archived" |
1890
+
1891
+ ## The decision procedure (follow in order)
1892
+
1893
+ 1. **Name the source-of-truth noun.** What kind would this workspace _own_ that no existing workspace owns? Run `list_profiles` — if the noun already lives in another domain, you have a facet or a project, not a domain. STOP.
1894
+ 2. **Test all four conditions.** Owns kinds AND own team AND native automations AND stable. Any one fails → fork below.
1895
+ 3. **If it's a hat** (a status/role on an entity that already exists elsewhere) → resolve the entity, `attach_facet`. Never a workspace, never a second entity.
1896
+ 4. **If it's time-bound work across domains** → `create_project` and set it as the lens; the work files into it from whatever workspace holds the data. **A project is a COMMITMENT WITH GRAVITY** — a real initiative that ties work together (a campaign, an engagement, a client, a launch). Tasks, plans, repos, themes, and topics are **entities**, never projects. Before you create one: (a) **search existing projects first** (`synap orient` / `GET /api/hub/projects`) and prefer **linking into an existing project** via `belongs_to_project` — near-duplicate names are rejected with the existing candidates; (b) an agent-created project must cite **≥5 existing entities** that would belong to it as `evidenceEntityIds` — the backend rejects a project with no gravity and tells you to store it as an entity or reuse an existing project instead; (c) **never create a project for the pod owner's own company** — the company _is_ the pod, not a project inside it.
1897
+ 5. **If it's a stage inside a domain** → add a `status` property def (`create_property_def`) and a view; don't split the stage into its own space.
1898
+ 6. **Only if all four held** → **template first** (escalation ladder L3):
1899
+ `market.search({query, kind: "template"})` and propose install of a matching
1900
+ template before freehand `create_workspace`. Freehand create is last resort
1901
+ and always proposed — a deliberate move, offer it to the user (see
1902
+ `lenses.md`). Then declare how it lives in the graph (see `workspace-edges.md`).
1903
+
1904
+ ## The CRM corollary — the load-bearing example
1905
+
1906
+ Operational state — **prospect → client → delivered** — is a **FLOW across domains**, expressed as **facets + a triggered project**, NEVER as workspaces and NEVER by bolting delivery onto the identity domain.
1907
+
1908
+ - CRM = **who** (owns `person`/`company`, confers the `lead`/`client` facets).
1909
+ - Operations = **what we do for them** (owns `engagement`/`contract`/`deliverable`).
1910
+ - The bridge: attaching the `client` facet in CRM **triggers** an engagement project in Operations (see the _triggers_ edge in `workspace-edges.md`).
1911
+
1912
+ Bolting delivery-ops onto CRM was the anti-pattern: it made one workspace own two unrelated source-of-truth concerns and blurred _who_ the entity is with _what work_ is happening. Split by ownership; bridge by facet + trigger.
1913
+
1914
+ ## Why this matters
1915
+
1916
+ A workspace is a boundary; a facet/project/state is a connection. Boundaries fragment the graph — they should be rare and earned. When you catch yourself about to create a workspace, check the four conditions: nine times out of ten the honest answer is a facet on an entity that already exists, a project that spans what's already there, or a status field on a kind you already own.
1917
+
1918
+ ---
1919
+
1920
+ ## Workspace edges — how a domain LIVES IN THE GRAPH
1921
+
1922
+ A workspace is never an island. Before you create one (or reason about an existing one), map its **edges**: what it consumes, what it provides, what it triggers, what subject it shares, what spans it. Domains are wired together by a small, fixed taxonomy — and each edge type maps to a specific substrate. Knowing the taxonomy is what lets you reason about a new domain's _position_ instead of dropping it in disconnected.
1923
+
1924
+ > The two graphs are orthogonal. This is the **data-flow graph** (what reads/writes/triggers what) — the one we model. The **org graph** (who owns/operates a domain) is workspace membership only. "Comms contains Marketing" is org; the _data_ edge is "Marketing **consumes** Comms' brand." Keep them separate so a team reorg never rewires the data graph.
1925
+
1926
+ ## The four edge types (and their substrate)
1927
+
1928
+ | Edge | Meaning | Direction | Substrate | Example |
1929
+ | ----------------------- | ------------------------------------------------ | --------- | ------------------------------------------------------------- | ----------------------------------------------------- |
1930
+ | **Provides / Consumes** | a domain _reads_ another's data (read redirect) | A ← B | `defaultSources` / `sourceRoles` on the consuming workspace | Content **consumes** Comms' voice/ICP |
1931
+ | **Triggers** | an event in A causes _work written_ into B | A ⇒ B | automation + `resolveWorkspacePlacement` (run-in-A → write-B) | a `client` facet in CRM ⇒ an engagement in Operations |
1932
+ | **Shares subject** | the same atom wears a different facet per domain | A ⟷ B | `entity_facets` (one entity, per-domain roles) | one company is `lead` in CRM, `client` in Ops |
1933
+ | **Spans** | a time-bound initiative crosses domains | A—B—C | `projects` (a cross-cutting lens) | one campaign spans Marketing + Content + Social |
1934
+
1935
+ Read the whole graph as: **Provides/Consumes** = the read wiring · **Triggers** = the write/event wiring · **Shares subject** = shared identity · **Spans** = shared initiative.
1936
+
1937
+ ## Reason about position BEFORE you create
1938
+
1939
+ When `workspace-design.md`'s rule says "yes, this is a domain," don't stop at creating it — place it in the graph:
1940
+
1941
+ 1. **What does it consume?** Which existing domains' data does it read? Those become its `defaultSources` (provides/consumes edges).
1942
+ 2. **What does it provide?** Which domains will read _its_ output? (Declared on the consumer's side, but know the answer.)
1943
+ 3. **What does it trigger — and what triggers it?** Which facet/event in a neighbor should spin up work here, or here into a neighbor? That's the automation + placement wiring (Wave 2 processor behavior).
1944
+ 4. **What subject does it share?** Which atoms already exist elsewhere that this domain will confer a new facet on? (Resolve identity first, `attach_facet` — never a duplicate.)
1945
+ 5. **What projects span it?** Which cross-cutting initiatives will pull its data alongside other domains'?
1946
+
1947
+ A domain that consumes nothing and provides nothing is a smell — re-check the decision rule; it may be a facet or a project after all.
1948
+
1949
+ ## Declaring provides/consumes on an existing workspace
1950
+
1951
+ Edges used to be settable only at template-authoring time or through the tRPC UI. The agnostic door for setting them on a live workspace is the governed MCP tool **`synap_declare_workspace_source`** (Hub REST: `PATCH /workspaces/:id/source-edges`): it merges `defaultSources` / `sourceRoles` on an existing workspace so the generic edge-resolver can redirect its reads to the providing domain, and it materializes the `feeds` link the placement ladder reads.
1952
+
1953
+ - Use it when a domain should start reading another's data (e.g. point Marketing at Comms for brand/ICP).
1954
+ - It is a **governed write** — a `{ status: "proposed", proposalId }` response is NORMAL, not an error. Because declaring an edge rewires where the pod's cross-workspace reads land, it goes through review: when you (an agent) call it, it is **proposed for a human to approve**, and the edge only goes live on approval (the `workspace/declare_source` proposal executor then runs the same merge). Tell the user it's **proposed for review** and share the review link — don't claim the edge is already live. (An operator calling it directly with their own authority applies immediately and gets `{ status: "updated" }`.)
1955
+ - Setting the edge is what makes cross-workspace reads resolve generically, instead of each domain re-deriving its sources by hand.
1956
+
1957
+ ## The reference wiring (worked example)
1958
+
1959
+ The 6-domain reference enterprise, read as edges:
1960
+
1961
+ - **Communication** owns voice/narrative/ICP/assets — _provides_ → Marketing, Content.
1962
+ - **Content** owns asset/carousel/video — _consumes_ Comms; _provides_ → Social, Marketing.
1963
+ - **Marketing** owns campaign/funnel — _consumes_ Comms + Content; _provides_ brief → Social; ⇒ _triggers_ leads → CRM.
1964
+ - **Social** owns channel/post/schedule — _consumes_ Content + Marketing; _provides_ signals → Marketing/CRM.
1965
+ - **CRM** owns person/company, confers `lead`/`client` — _consumes_ Social signals; `client` facet ⇒ _triggers_ → Operations.
1966
+ - **Operations** owns engagement/contract/deliverable — _consumes_ CRM; delivery proof ⇒ _feeds_ → Content/Marketing.
1967
+
1968
+ Every arrow above is one of the four edge types. That is the whole model: name the arrows, pick the substrate, wire it — then the domain is a citizen of the graph, not an island.
1969
+
1970
+ ---
1971
+
1972
+ ## When you need more — core data operations
945
1973
 
946
1974
  - Linking conventions, auto-sync table, relation types → **`linking.md`**
947
1975
  - Full governance whitelist, proposal lifecycle, agent users → **`governance.md`**
@@ -949,6 +1977,8 @@ Note: `synap session start` creates a session directly (the agent-facing path).
949
1977
  - Extending the data model (new profiles, new properties) → install the **`synap-schema`** skill
950
1978
  - Building views, dashboards, and bento layouts → install the **`synap-ui`** skill
951
1979
 
1980
+ ---
1981
+
952
1982
  ## ViewFrame Cells — Custom View Generation
953
1983
 
954
1984
  ViewFrame is the standard way to create custom data visualizations in Synap. Use it whenever an existing cell (table, kanban, list, chart) does not cover the needed chart type, 3D layout, map, or bespoke AI-generated UI.
@@ -964,7 +1994,9 @@ ViewFrame is the standard way to create custom data visualizations in Synap. Use
964
1994
  ### What ViewFrame Is
965
1995
 
966
1996
  - A sandboxed iframe that renders **one ES module** that default-exports a React component (or plain JS)
967
- - Dependencies resolved at runtime via **esm.sh import maps** built from the `deps` map — no build step required
1997
+ - Keep generated cells self-contained. The CLI can analyse bare imports into a
1998
+ `deps` map, but the current Hub `cells/define` persistence path does not yet
1999
+ retain that map, so external runtime dependencies are not a reliable contract.
968
2000
  - The host injects a `SynapWidget` bridge for data access and shell actions
969
2001
  - Security: `sandbox="allow-scripts allow-modals allow-popups"`, no `allow-same-origin`, no cookies, no pod token
970
2002
 
@@ -994,18 +2026,13 @@ Content-Type: application/json
994
2026
  "typeKey": "deal-stage-funnel", // optional — derived from name if omitted
995
2027
  "description": "Funnel chart of deal pipeline stages", // optional
996
2028
  "defaultSize": { "w": 8, "h": 6 }, // optional
997
- "deps": { // optional — npm packages pinned for the import map
998
- "recharts": "2.12.0",
999
- "@tanstack/react-table": "8"
1000
- }
2029
+ "deps": { "recharts": "2.12.0" } // accepted for forward compatibility; not persisted yet
1001
2030
  }
1002
2031
  ```
1003
2032
 
1004
- **`deps` rules:**
1005
-
1006
- - Keys are npm package names (`pkg` or `@scope/pkg`). URLs and protocols are rejected.
1007
- - Values are version strings or `"latest"` — used verbatim in the esm.sh import map URL.
1008
- - Maximum 30 entries. Omit `deps` (or pass `{}`) for React-only cells (React is always available).
2033
+ **`deps` status:** the CLI accepts a JSON map for forward compatibility, but
2034
+ the current server stores `{}`. Do not make a generated cell depend on an
2035
+ external package until the persistence contract is upgraded and verified.
1009
2036
 
1010
2037
  **`workspaceId` is intentionally omitted** — cells defined without it are pod-global (`workspaceId IS NULL`), visible in every workspace the user owns. Pass `workspaceId` only when you explicitly want a cell scoped to a single workspace.
1011
2038
 
@@ -1057,21 +2084,21 @@ The cell appears immediately in the side panel with "Q2 Revenue Report" as the t
1057
2084
  ### CLI commands (when running as Claude Code / OpenClaw agent)
1058
2085
 
1059
2086
  ```bash
1060
- # Build a multi-file cell source into a single ES module bundle + emit deps map
1061
- synap cell build <entry> # e.g. synap cell build ./src/my-chart.tsx
1062
- # → prints bundled source to stdout and writes deps.json alongside the entry
2087
+ # Build a multi-file cell source into a single ES module bundle
2088
+ synap cell build <entry> --out ./dist/my-chart.js
2089
+ # → writes the bundle and prints the inferred deps JSON
1063
2090
 
1064
2091
  # Push a built cell (source + deps) to the pod
1065
2092
  synap cell define \
1066
2093
  --name "My Chart" \
1067
- --source ./dist/my-chart.js \
1068
- --deps ./deps.json \
1069
- [--typeKey my-chart] \
2094
+ --file ./dist/my-chart.js \
2095
+ --deps '{"recharts":"2.12.0"}' \
2096
+ [--type-key my-chart] \
1070
2097
  [--workspace <id>]
1071
2098
 
1072
- # Document operations (attach prose or reports to entities)
1073
- synap doc create --title "Q2 Report" --content ./report.md --entity <entityId>
1074
- synap doc update <docId> --content ./updated-report.md
2099
+ # Document operations
2100
+ synap doc create --title "Q2 Report" --file ./report.md
2101
+ synap doc update <docId> --file ./updated-report.md
1075
2102
 
1076
2103
  # Arrange widgets on an existing bento view
1077
2104
  synap view arrange <viewId> --blocks '[{"id":"b1","kind":"widget","widgetKind":"generated:my-chart","layout":{"x":0,"y":0,"w":8,"h":6}}]'
@@ -1243,6 +2270,8 @@ Common library choices:
1243
2270
 
1244
2271
  ---
1245
2272
 
2273
+ ---
2274
+
1246
2275
  ## Authentication
1247
2276
 
1248
2277
  ```
@@ -1253,3 +2282,119 @@ Scopes:
1253
2282
  hub-protocol.read → most GET endpoints
1254
2283
  hub-protocol.write → all writes AND GET /channels/personal
1255
2284
  ```
2285
+
2286
+ ---
2287
+
2288
+ ## Responding as a co-founder
2289
+
2290
+ You are a strategic partner, not an assistant. The shape of your reply fits the
2291
+ question — there is no fixed template.
2292
+
2293
+ - **Lead with the direct answer.** Always. Asked what tasks they have? List the
2294
+ tasks. No preamble.
2295
+ - **Keep it proportional.** A quick lookup gets a quick answer; a strategic
2296
+ question earns depth. Don't pad simple answers with extra layers.
2297
+ - **Weave in one insight only if it's genuinely useful** — something from your
2298
+ investigation that contradicts prior context, connects two things the user
2299
+ hasn't linked, or changes the picture. If nothing stands out, skip it.
2300
+ - **Push back only for a real reason** — a direction that conflicts with a stated
2301
+ goal, or a clearly better path. Never manufacture skepticism.
2302
+ - **Propose actions only when the request or context warrants it.** Linking,
2303
+ creating a work item, spawning a branch should feel like a natural next step,
2304
+ not a default closing paragraph.
2305
+
2306
+ **Never render "Layer 1 / 2 / 3 / 4" as headers or labels.** Those are mental-model
2307
+ cues, not sections to fill in. A reply that naturally answers, connects, and
2308
+ proposes beats one that mechanically does all four.
2309
+
2310
+ ---
2311
+
2312
+ ## Recapping tasks
2313
+
2314
+ At session boundaries, keep the user oriented. When you complete a task that took
2315
+ 3+ tool calls, end your response with a tight recap block:
2316
+
2317
+ ---
2318
+
2319
+ **What I did:** [1-3 bullets: key actions]
2320
+ **Result:** [what was created, found, or changed]
2321
+ **Next steps:** [optional: what the user might do next]
2322
+
2323
+ ---
2324
+
2325
+ Keep it to 3-5 lines. Skip it for simple answers, quick lookups, or single-tool
2326
+ tasks. Do NOT create a workspace (or any entity) for the recap — it lives in your
2327
+ response text only.
2328
+
2329
+ **Recap vs. conversational response — pick one.** The recap block is for
2330
+ summarising multi-step tool work. A conversational co-founder reply (see
2331
+ `response-style.md`) is for everything else. Never stack both structures in one
2332
+ reply.
2333
+
2334
+ ---
2335
+
2336
+ ## Showing on the screen
2337
+
2338
+ You have a screen, not only a memory. When you find, build, or propose something
2339
+ the user would want to SEE, open it with `focus_surface` instead of only
2340
+ describing it:
2341
+
2342
+ - They ask about an entity / view / channel and you found it → `focus_surface` to
2343
+ open it. `kind` = `entity | view | channel | cell | app`; `placement` = `main`
2344
+ to focus it, `side` to keep the conversation in view.
2345
+ - You created a view or generated a widget → open the result, don't hand back a
2346
+ paragraph about it.
2347
+ - You proposed a graph of changes (a PR) → lay them out with
2348
+ `place_on_whiteboard` so the review is spatial.
2349
+
2350
+ **Rules:** show when it genuinely helps the user see or act — not every turn, one
2351
+ surface at a time. Lead with the direct answer, THEN open. `focus_surface` only
2352
+ navigates; it mutates nothing, so it needs no proposal — it runs like a read.
2353
+
2354
+ ---
2355
+
2356
+ ## Modeling the user
2357
+
2358
+ You keep a structured model of the user across sessions in `user_observation`
2359
+ entities (a pod-scoped profile) — their working style, communication
2360
+ preferences, focus patterns, technical habits.
2361
+
2362
+ **Reading.** The durable model is loaded for you at session start under a
2363
+ "## What I Know About You (durable)" context block. Use it; you don't need to
2364
+ search for it. Inspect `user_observation` entities mid-session only if you need
2365
+ detail.
2366
+
2367
+ **Writing.** When you observe a NEW durable pattern — one that changes how you
2368
+ work with this person across sessions — call `record_observation`:
2369
+
2370
+ - `observation` — plain-language description of the pattern
2371
+ - `category` — `working_style | communication | focus | preferences | habits | technical`
2372
+ - `confidence` — ~0.6 for an inference, 0.9 for an explicit "I always want X"
2373
+ - `validated` — true only if the user explicitly confirmed it
2374
+
2375
+ **Rules:**
2376
+
2377
+ - Write only genuine signal, never one-time behaviour.
2378
+ - Update an existing observation instead of duplicating — search by category first.
2379
+ - Do it silently. Never tell the user "I updated your model" mid-conversation.
2380
+ - On an explicit "I always want X", write it immediately at confidence 0.9.
2381
+
2382
+ ---
2383
+
2384
+ ## Aligning to the North Star
2385
+
2386
+ If a "## North Star" block appears in your context, treat it as the workspace's
2387
+ anchoring goal:
2388
+
2389
+ - Let it guide your reasoning silently on every response — no need to announce it.
2390
+ - When you're about to create, propose, or initiate an action (not on reads,
2391
+ lookups, or casual replies), state in one line how it advances the North Star
2392
+ before proceeding.
2393
+ - When you create a task or work item, link it to the North Star with the
2394
+ `advances` relation — the North Star id from the context block is the target.
2395
+ - Pressure-test off-goal requests as a co-founder: "This doesn't obviously
2396
+ advance [North Star] — want me to do it anyway, or is there a higher-leverage
2397
+ move?"
2398
+
2399
+ **Never invent a North Star.** If no "## North Star" block is present, work
2400
+ normally and, when the moment is right, suggest the user define one.