@rubytech/create-maxy-code 0.1.278 → 0.1.280

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 (244) hide show
  1. package/package.json +1 -1
  2. package/payload/platform/plugins/admin/PLUGIN.md +1 -0
  3. package/payload/platform/plugins/admin/hooks/__tests__/prompt-optimiser-directive.test.sh +42 -0
  4. package/payload/platform/plugins/admin/hooks/prompt-optimiser-directive.sh +60 -15
  5. package/payload/platform/plugins/admin/mcp/dist/__tests__/e-sign-discoverable.test.d.ts +2 -0
  6. package/payload/platform/plugins/admin/mcp/dist/__tests__/e-sign-discoverable.test.d.ts.map +1 -0
  7. package/payload/platform/plugins/admin/mcp/dist/__tests__/e-sign-discoverable.test.js +54 -0
  8. package/payload/platform/plugins/admin/mcp/dist/__tests__/e-sign-discoverable.test.js.map +1 -0
  9. package/payload/platform/plugins/admin/mcp/dist/content-producer-authoring-skills.d.ts +2 -0
  10. package/payload/platform/plugins/admin/mcp/dist/content-producer-authoring-skills.d.ts.map +1 -0
  11. package/payload/platform/plugins/admin/mcp/dist/content-producer-authoring-skills.js +19 -0
  12. package/payload/platform/plugins/admin/mcp/dist/content-producer-authoring-skills.js.map +1 -0
  13. package/payload/platform/plugins/admin/mcp/dist/skill-resolution.d.ts.map +1 -1
  14. package/payload/platform/plugins/admin/mcp/dist/skill-resolution.js +2 -5
  15. package/payload/platform/plugins/admin/mcp/dist/skill-resolution.js.map +1 -1
  16. package/payload/platform/plugins/admin/skills/a4-print-documents/SKILL.md +2 -0
  17. package/payload/platform/plugins/admin/skills/access-manager/SKILL.md +2 -0
  18. package/payload/platform/plugins/admin/skills/admin-user-management/SKILL.md +2 -0
  19. package/payload/platform/plugins/admin/skills/business-profile/SKILL.md +2 -0
  20. package/payload/platform/plugins/admin/skills/capabilities-here/SKILL.md +2 -0
  21. package/payload/platform/plugins/admin/skills/datetime/SKILL.md +2 -0
  22. package/payload/platform/plugins/admin/skills/deck-pages/SKILL.md +2 -0
  23. package/payload/platform/plugins/admin/skills/file-presentation/SKILL.md +2 -0
  24. package/payload/platform/plugins/admin/skills/investigate/SKILL.md +2 -0
  25. package/payload/platform/plugins/admin/skills/plainly/SKILL.md +2 -0
  26. package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +2 -0
  27. package/payload/platform/plugins/admin/skills/plugin-management/SKILL.md +2 -0
  28. package/payload/platform/plugins/admin/skills/professional-document/SKILL.md +2 -0
  29. package/payload/platform/plugins/admin/skills/public-agent-manager/SKILL.md +2 -0
  30. package/payload/platform/plugins/admin/skills/qr-code/SKILL.md +2 -0
  31. package/payload/platform/plugins/admin/skills/session-management/SKILL.md +2 -0
  32. package/payload/platform/plugins/admin/skills/skill-builder/SKILL.md +2 -0
  33. package/payload/platform/plugins/admin/skills/specialist-management/SKILL.md +2 -0
  34. package/payload/platform/plugins/admin/skills/stream-log-review/SKILL.md +2 -0
  35. package/payload/platform/plugins/admin/skills/superpowers-sprint/SKILL.md +2 -0
  36. package/payload/platform/plugins/admin/skills/task/SKILL.md +2 -0
  37. package/payload/platform/plugins/admin/skills/update-knowledge/SKILL.md +2 -0
  38. package/payload/platform/plugins/admin/skills/upgrade/SKILL.md +2 -0
  39. package/payload/platform/plugins/aeo/skills/structured-answer/SKILL.md +2 -0
  40. package/payload/platform/plugins/business-assistant/PLUGIN.md +5 -0
  41. package/payload/platform/plugins/business-assistant/references/crm.md +6 -0
  42. package/payload/platform/plugins/business-assistant/references/site-lead-intake.md +52 -0
  43. package/payload/platform/plugins/business-assistant/skills/e-sign/SKILL.md +309 -0
  44. package/payload/platform/plugins/cloudflare/PLUGIN.md +1 -0
  45. package/payload/platform/plugins/cloudflare/references/api.md +17 -3
  46. package/payload/platform/plugins/cloudflare/references/dashboard-guide.md +10 -0
  47. package/payload/platform/plugins/cloudflare/references/web-analytics.md +64 -0
  48. package/payload/platform/plugins/cloudflare/skills/cloudflare/SKILL.md +3 -0
  49. package/payload/platform/plugins/deep-research/skills/academic-verify/SKILL.md +2 -0
  50. package/payload/platform/plugins/deep-research/skills/book-mirror/SKILL.md +2 -0
  51. package/payload/platform/plugins/deep-research/skills/data-research/SKILL.md +2 -0
  52. package/payload/platform/plugins/deep-research/skills/deep-research/SKILL.md +2 -0
  53. package/payload/platform/plugins/deep-research/skills/strategic-reading/SKILL.md +2 -0
  54. package/payload/platform/plugins/email/PLUGIN.md +2 -0
  55. package/payload/platform/plugins/email/mcp/dist/lib/imap.d.ts +42 -11
  56. package/payload/platform/plugins/email/mcp/dist/lib/imap.d.ts.map +1 -1
  57. package/payload/platform/plugins/email/mcp/dist/lib/imap.js +123 -57
  58. package/payload/platform/plugins/email/mcp/dist/lib/imap.js.map +1 -1
  59. package/payload/platform/plugins/email/mcp/dist/tools/email-otp-extract.d.ts.map +1 -1
  60. package/payload/platform/plugins/email/mcp/dist/tools/email-otp-extract.js +4 -2
  61. package/payload/platform/plugins/email/mcp/dist/tools/email-otp-extract.js.map +1 -1
  62. package/payload/platform/plugins/email/mcp/dist/tools/email-read.d.ts.map +1 -1
  63. package/payload/platform/plugins/email/mcp/dist/tools/email-read.js +18 -8
  64. package/payload/platform/plugins/email/mcp/dist/tools/email-read.js.map +1 -1
  65. package/payload/platform/plugins/email/mcp/dist/tools/email-search.d.ts.map +1 -1
  66. package/payload/platform/plugins/email/mcp/dist/tools/email-search.js +18 -8
  67. package/payload/platform/plugins/email/mcp/dist/tools/email-search.js.map +1 -1
  68. package/payload/platform/plugins/email/skills/email-composition/SKILL.md +2 -0
  69. package/payload/platform/plugins/email/skills/email-ingest/SKILL.md +2 -0
  70. package/payload/platform/plugins/graph-viewer/skills/render-graph/SKILL.md +2 -0
  71. package/payload/platform/plugins/linkedin-extension/skills/linkedin-extension/SKILL.md +2 -0
  72. package/payload/platform/plugins/memory/skills/archive-crawler/SKILL.md +2 -0
  73. package/payload/platform/plugins/memory/skills/challenge/SKILL.md +2 -0
  74. package/payload/platform/plugins/memory/skills/concept-synthesis/SKILL.md +2 -0
  75. package/payload/platform/plugins/memory/skills/connect/SKILL.md +2 -0
  76. package/payload/platform/plugins/memory/skills/conversational-memory/SKILL.md +2 -0
  77. package/payload/platform/plugins/memory/skills/emerge/SKILL.md +2 -0
  78. package/payload/platform/plugins/outlook/skills/outlook/SKILL.md +2 -0
  79. package/payload/platform/plugins/prompt-optimiser/skills/prompt-optimiser/SKILL.md +3 -1
  80. package/payload/platform/plugins/prompt-optimiser/skills/prompt-optimiser/__tests__/restatement-ladder.test.sh +21 -0
  81. package/payload/platform/plugins/scheduling/skills/briefing/SKILL.md +2 -0
  82. package/payload/platform/plugins/scheduling/skills/daily-prep/SKILL.md +2 -0
  83. package/payload/platform/plugins/slides/skills/deck-system/SKILL.md +2 -0
  84. package/payload/platform/plugins/whatsapp/skills/connect-whatsapp/SKILL.md +2 -0
  85. package/payload/platform/plugins/whatsapp/skills/manage-whatsapp-config/SKILL.md +2 -0
  86. package/payload/platform/plugins/work/skills/execute-task/SKILL.md +2 -0
  87. package/payload/platform/plugins/workflows/skills/workflow-manager/SKILL.md +2 -0
  88. package/payload/platform/scripts/lib/__tests__/admin-skills-bootstrap.test.sh +64 -0
  89. package/payload/platform/scripts/lib/admin-skills-bootstrap.sh +109 -0
  90. package/payload/platform/scripts/setup-account.sh +8 -0
  91. package/payload/platform/services/claude-session-manager/dist/fs-watcher.d.ts.map +1 -1
  92. package/payload/platform/services/claude-session-manager/dist/fs-watcher.js +43 -11
  93. package/payload/platform/services/claude-session-manager/dist/fs-watcher.js.map +1 -1
  94. package/payload/platform/services/claude-session-manager/dist/http-server.d.ts.map +1 -1
  95. package/payload/platform/services/claude-session-manager/dist/http-server.js +74 -1
  96. package/payload/platform/services/claude-session-manager/dist/http-server.js.map +1 -1
  97. package/payload/platform/services/claude-session-manager/dist/index.js +48 -2
  98. package/payload/platform/services/claude-session-manager/dist/index.js.map +1 -1
  99. package/payload/platform/services/claude-session-manager/dist/liveness-divergence.d.ts +19 -0
  100. package/payload/platform/services/claude-session-manager/dist/liveness-divergence.d.ts.map +1 -0
  101. package/payload/platform/services/claude-session-manager/dist/liveness-divergence.js +38 -0
  102. package/payload/platform/services/claude-session-manager/dist/liveness-divergence.js.map +1 -0
  103. package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts +8 -0
  104. package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts.map +1 -1
  105. package/payload/platform/services/claude-session-manager/dist/pty-spawner.js +12 -0
  106. package/payload/platform/services/claude-session-manager/dist/pty-spawner.js.map +1 -1
  107. package/payload/platform/services/claude-session-manager/dist/scope-record.d.ts +11 -0
  108. package/payload/platform/services/claude-session-manager/dist/scope-record.d.ts.map +1 -0
  109. package/payload/platform/services/claude-session-manager/dist/scope-record.js +81 -0
  110. package/payload/platform/services/claude-session-manager/dist/scope-record.js.map +1 -0
  111. package/payload/platform/services/whatsapp-channel/dist/notification.d.ts +10 -0
  112. package/payload/platform/services/whatsapp-channel/dist/notification.d.ts.map +1 -1
  113. package/payload/platform/services/whatsapp-channel/dist/notification.js +22 -1
  114. package/payload/platform/services/whatsapp-channel/dist/notification.js.map +1 -1
  115. package/payload/platform/templates/agents/admin/IDENTITY.md +2 -6
  116. package/payload/server/public/assets/AdminShell-C6_5h1cC.js +1 -0
  117. package/payload/server/public/assets/{Checkbox-BY2lndUH.js → Checkbox-2reCESkb.js} +1 -1
  118. package/payload/server/public/assets/{admin-C364q_-g.js → admin-BpnsTEa3.js} +1 -1
  119. package/payload/server/public/assets/{arc-BW1WhwmV.js → arc-BgelqC_3.js} +1 -1
  120. package/payload/server/public/assets/architecture-YZFGNWBL-YM-TtgPY.js +1 -0
  121. package/payload/server/public/assets/{architectureDiagram-Q4EWVU46-CLhiy6Rc.js → architectureDiagram-Q4EWVU46-BabnAZJm.js} +1 -1
  122. package/payload/server/public/assets/{blockDiagram-DXYQGD6D-ue7Vkc-Q.js → blockDiagram-DXYQGD6D-BJzZqNsy.js} +1 -1
  123. package/payload/server/public/assets/{browser-D66JrDpx.js → browser-fgBRZ5gK.js} +1 -1
  124. package/payload/server/public/assets/{c4Diagram-AHTNJAMY-dVjuUU_p.js → c4Diagram-AHTNJAMY-By-xuh9F.js} +1 -1
  125. package/payload/server/public/assets/channel-BRPH0atd.js +1 -0
  126. package/payload/server/public/assets/{chunk-2KRD3SAO-CKn-bEkI.js → chunk-2KRD3SAO-Gj2dSpBl.js} +1 -1
  127. package/payload/server/public/assets/{chunk-336JU56O-DLBJaAyL.js → chunk-336JU56O-2PXVPCUA.js} +2 -2
  128. package/payload/server/public/assets/chunk-426QAEUC-QhauxjvK.js +1 -0
  129. package/payload/server/public/assets/{chunk-4BX2VUAB-B2mSmC97.js → chunk-4BX2VUAB-9XIBlnC2.js} +1 -1
  130. package/payload/server/public/assets/{chunk-4TB4RGXK-DPHoq8nE.js → chunk-4TB4RGXK-Cr3mXPnI.js} +1 -1
  131. package/payload/server/public/assets/{chunk-55IACEB6-JLT9m7Hd.js → chunk-55IACEB6-Ccg46J3A.js} +1 -1
  132. package/payload/server/public/assets/{chunk-5FUZZQ4R-DjxdZ8WO.js → chunk-5FUZZQ4R-Cz9MvPKc.js} +1 -1
  133. package/payload/server/public/assets/{chunk-5PVQY5BW-BujJUZqf.js → chunk-5PVQY5BW-BleOdlgO.js} +1 -1
  134. package/payload/server/public/assets/{chunk-67CJDMHE-CCdP3Pdx.js → chunk-67CJDMHE-CyCYCQWy.js} +1 -1
  135. package/payload/server/public/assets/{chunk-7N4EOEYR-bXgVzdM9.js → chunk-7N4EOEYR-KpT3uHzH.js} +1 -1
  136. package/payload/server/public/assets/{chunk-AA7GKIK3-CYeDvY8B.js → chunk-AA7GKIK3-BfOzbuxq.js} +1 -1
  137. package/payload/server/public/assets/{chunk-BSJP7CBP-U0Fi-Ers.js → chunk-BSJP7CBP-Bq0M3wlv.js} +1 -1
  138. package/payload/server/public/assets/{chunk-CIAEETIT-C8QrV6Gg.js → chunk-CIAEETIT-CR-2dNaL.js} +1 -1
  139. package/payload/server/public/assets/{chunk-EDXVE4YY-DF2EBXie.js → chunk-EDXVE4YY-egUGMTOS.js} +1 -1
  140. package/payload/server/public/assets/{chunk-ENJZ2VHE-CoTooXpM.js → chunk-ENJZ2VHE-CQohBEFw.js} +1 -1
  141. package/payload/server/public/assets/{chunk-FMBD7UC4-DxAQS3UB.js → chunk-FMBD7UC4-DIxBUbLP.js} +1 -1
  142. package/payload/server/public/assets/{chunk-FOC6F5B3-CyIK3zeY.js → chunk-FOC6F5B3-C2694Zoq.js} +1 -1
  143. package/payload/server/public/assets/{chunk-ICPOFSXX-D8F-S9Bk.js → chunk-ICPOFSXX-DpXZKHyn.js} +2 -2
  144. package/payload/server/public/assets/{chunk-K5T4RW27-7GcDzjyJ.js → chunk-K5T4RW27-CUicG0Rp.js} +1 -1
  145. package/payload/server/public/assets/{chunk-KGLVRYIC-B7pAr9hf.js → chunk-KGLVRYIC-B5mPJhOr.js} +1 -1
  146. package/payload/server/public/assets/{chunk-LIHQZDEY-DEoU2jrz.js → chunk-LIHQZDEY-q81QhuMs.js} +1 -1
  147. package/payload/server/public/assets/{chunk-ORNJ4GCN-BwMFuN24.js → chunk-ORNJ4GCN-DgurdFZs.js} +1 -1
  148. package/payload/server/public/assets/{chunk-OYMX7WX6-C15Ps-tC.js → chunk-OYMX7WX6-CxqMRu2d.js} +1 -1
  149. package/payload/server/public/assets/chunk-QZHKN3VN-BDAQvtxz.js +1 -0
  150. package/payload/server/public/assets/{chunk-U2HBQHQK-BBPMxdAI.js → chunk-U2HBQHQK-BSLctJC9.js} +1 -1
  151. package/payload/server/public/assets/{chunk-X2U36JSP-xwRszlfe.js → chunk-X2U36JSP--0AUeeFl.js} +1 -1
  152. package/payload/server/public/assets/{chunk-XPW4576I-BA0a8Ygs.js → chunk-XPW4576I-D-hf9Bcn.js} +1 -1
  153. package/payload/server/public/assets/{chunk-YZCP3GAM-C4liAf7D.js → chunk-YZCP3GAM-PvOhSwI9.js} +1 -1
  154. package/payload/server/public/assets/{chunk-ZZ45TVLE-Cy3If_Rl.js → chunk-ZZ45TVLE-1zPUlNWz.js} +1 -1
  155. package/payload/server/public/assets/classDiagram-6PBFFD2Q-BJv89PB6.js +1 -0
  156. package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-DNUszH5M.js +1 -0
  157. package/payload/server/public/assets/clone-CREg9jLM.js +1 -0
  158. package/payload/server/public/assets/{cose-bilkent-S5V4N54A-Df_JmB8E.js → cose-bilkent-S5V4N54A-Cvo-gkBd.js} +1 -1
  159. package/payload/server/public/assets/{dagre-DKCdgCLD.js → dagre-CMHsNfjP.js} +1 -1
  160. package/payload/server/public/assets/{dagre-KV5264BT-DHZ3rA0O.js → dagre-KV5264BT-CM1DSx4E.js} +1 -1
  161. package/payload/server/public/assets/{data-uiOcPh8T.js → data-Cuynp6Y6.js} +1 -1
  162. package/payload/server/public/assets/{diagram-5BDNPKRD-XAci8Krv.js → diagram-5BDNPKRD-VDHiHwEI.js} +1 -1
  163. package/payload/server/public/assets/{diagram-G4DWMVQ6-DI6g5YJH.js → diagram-G4DWMVQ6-OKdufoeW.js} +1 -1
  164. package/payload/server/public/assets/{diagram-MMDJMWI5-D94oS42o.js → diagram-MMDJMWI5-Yg6QdCA1.js} +1 -1
  165. package/payload/server/public/assets/{diagram-TYMM5635-D_j5_oj_.js → diagram-TYMM5635-CpEMZQDu.js} +1 -1
  166. package/payload/server/public/assets/{dist-BmvZ8OaX.js → dist-811BIK0R.js} +1 -1
  167. package/payload/server/public/assets/{erDiagram-SMLLAGMA-DPx8cHxF.js → erDiagram-SMLLAGMA-Dw5gAkFM.js} +1 -1
  168. package/payload/server/public/assets/{flatten-Bo6YRmWl.js → flatten-BsWEYbBB.js} +1 -1
  169. package/payload/server/public/assets/{flowDiagram-DWJPFMVM-Cdfxz2_5.js → flowDiagram-DWJPFMVM-Cy_R1XHz.js} +1 -1
  170. package/payload/server/public/assets/{ganttDiagram-T4ZO3ILL-oregwFNl.js → ganttDiagram-T4ZO3ILL-BYARP_eH.js} +1 -1
  171. package/payload/server/public/assets/gitGraph-7Q5UKJZL-CtOPqykB.js +1 -0
  172. package/payload/server/public/assets/{gitGraphDiagram-UUTBAWPF-BZI26T0o.js → gitGraphDiagram-UUTBAWPF-BGOe0unY.js} +1 -1
  173. package/payload/server/public/assets/{graph-BSHyF267.js → graph-Ddg24bjy.js} +2 -2
  174. package/payload/server/public/assets/{graph-labels-D5ecmK-Z.js → graph-labels-eOrWYy0R.js} +1 -1
  175. package/payload/server/public/assets/{graphlib-BuiXJGQr.js → graphlib-DaefxCga.js} +1 -1
  176. package/payload/server/public/assets/info-OMHHGYJF-Jba5enA8.js +1 -0
  177. package/payload/server/public/assets/infoDiagram-42DDH7IO-Df9BlkPA.js +2 -0
  178. package/payload/server/public/assets/{isEmpty-D6Kr-M1M.js → isEmpty-BWl67LAZ.js} +1 -1
  179. package/payload/server/public/assets/{ishikawaDiagram-UXIWVN3A-DnBy6cze.js → ishikawaDiagram-UXIWVN3A-YrGuzpiI.js} +1 -1
  180. package/payload/server/public/assets/{journeyDiagram-VCZTEJTY-DeYkJsvd.js → journeyDiagram-VCZTEJTY-EIykOcqg.js} +1 -1
  181. package/payload/server/public/assets/{kanban-definition-6JOO6SKY-BHuZmXiA.js → kanban-definition-6JOO6SKY-BR0TC6Je.js} +1 -1
  182. package/payload/server/public/assets/{line-BAjS0lyG.js → line-DPGcT5IM.js} +1 -1
  183. package/payload/server/public/assets/{linear-COm19-p7.js → linear-CPN03uSh.js} +1 -1
  184. package/payload/server/public/assets/{mermaid-parser.core-Cg06vNXY.js → mermaid-parser.core-D7Iu4c9M.js} +2 -2
  185. package/payload/server/public/assets/{mermaid.core-DkUCZA6N.js → mermaid.core-krXRpXn9.js} +3 -3
  186. package/payload/server/public/assets/{mindmap-definition-QFDTVHPH-Gbaa7NRj.js → mindmap-definition-QFDTVHPH-HwiCtPzN.js} +1 -1
  187. package/payload/server/public/assets/{ordinal-BDi6f4xk.js → ordinal-krseTxxN.js} +1 -1
  188. package/payload/server/public/assets/packet-4T2RLAQJ-D_eWzeAP.js +1 -0
  189. package/payload/server/public/assets/pie-ZZUOXDRM-BDiy1Ob2.js +1 -0
  190. package/payload/server/public/assets/{pieDiagram-DEJITSTG-v7yUgLoq.js → pieDiagram-DEJITSTG-xodxyWLe.js} +1 -1
  191. package/payload/server/public/assets/{public-CTkj6UYK.js → public-xVaPcg6i.js} +3 -3
  192. package/payload/server/public/assets/{quadrantDiagram-34T5L4WZ-BpVlNbru.js → quadrantDiagram-34T5L4WZ-DuYYY4ZZ.js} +1 -1
  193. package/payload/server/public/assets/radar-PYXPWWZC-C5mlfmtg.js +1 -0
  194. package/payload/server/public/assets/{reduce-CGi9ik8i.js → reduce-tk-xY6Fv.js} +1 -1
  195. package/payload/server/public/assets/{requirementDiagram-MS252O5E-QrSZzaxF.js → requirementDiagram-MS252O5E-D0ta3kru.js} +1 -1
  196. package/payload/server/public/assets/{sankeyDiagram-XADWPNL6-IVgJCKl1.js → sankeyDiagram-XADWPNL6-KdHdlzvT.js} +1 -1
  197. package/payload/server/public/assets/{sequenceDiagram-FGHM5R23-DsN4J_nq.js → sequenceDiagram-FGHM5R23-DoSFUTQZ.js} +1 -1
  198. package/payload/server/public/assets/{src-DLEIsjER.js → src-Cpl1JzME.js} +1 -1
  199. package/payload/server/public/assets/{stateDiagram-FHFEXIEX-DN0-8ZrI.js → stateDiagram-FHFEXIEX-BLW0KiSA.js} +1 -1
  200. package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-C5zlCV4X.js +1 -0
  201. package/payload/server/public/assets/{timeline-definition-GMOUNBTQ-BLYu380r.js → timeline-definition-GMOUNBTQ-8QAzhYz1.js} +1 -1
  202. package/payload/server/public/assets/treeView-SZITEDCU-CM-9m-cS.js +1 -0
  203. package/payload/server/public/assets/treemap-W4RFUUIX-DDHPB-Xh.js +1 -0
  204. package/payload/server/public/assets/{useSelectionMode-98jrB9kD.css → useSelectionMode-BnRcJeg2.css} +1 -1
  205. package/payload/server/public/assets/{useSelectionMode-80iYuGPa.js → useSelectionMode-DlJsX-_X.js} +1 -1
  206. package/payload/server/public/assets/{vennDiagram-DHZGUBPP-Tfn8qVAB.js → vennDiagram-DHZGUBPP-BKvfScOi.js} +1 -1
  207. package/payload/server/public/assets/wardley-RL74JXVD-CUb3Br4q.js +1 -0
  208. package/payload/server/public/assets/{wardleyDiagram-NUSXRM2D-CeQNnKpT.js → wardleyDiagram-NUSXRM2D-DaMfDpJ3.js} +1 -1
  209. package/payload/server/public/assets/whatsapp-BE79pNh8.js +1 -0
  210. package/payload/server/public/assets/{xychartDiagram-5P7HB3ND-H-IatOB_.js → xychartDiagram-5P7HB3ND-BIkNQlLa.js} +1 -1
  211. package/payload/server/public/browser.html +5 -5
  212. package/payload/server/public/data.html +6 -6
  213. package/payload/server/public/graph.html +7 -7
  214. package/payload/server/public/index.html +6 -6
  215. package/payload/server/public/public.html +5 -5
  216. package/payload/server/public/whatsapp.html +18 -0
  217. package/payload/server/server.js +5772 -5673
  218. package/payload/server/public/assets/AdminShell-BcHJy_Y2.js +0 -1
  219. package/payload/server/public/assets/architecture-YZFGNWBL-xHbVjatf.js +0 -1
  220. package/payload/server/public/assets/channel-1FBBBcz1.js +0 -1
  221. package/payload/server/public/assets/chunk-426QAEUC-CfdaOTNb.js +0 -1
  222. package/payload/server/public/assets/chunk-QZHKN3VN-CHE_iZO7.js +0 -1
  223. package/payload/server/public/assets/classDiagram-6PBFFD2Q--mLJq--t.js +0 -1
  224. package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-C8TJtMFc.js +0 -1
  225. package/payload/server/public/assets/clone-UUs4PDQ1.js +0 -1
  226. package/payload/server/public/assets/gitGraph-7Q5UKJZL-CmHdoS27.js +0 -1
  227. package/payload/server/public/assets/info-OMHHGYJF-DTA9msO-.js +0 -1
  228. package/payload/server/public/assets/infoDiagram-42DDH7IO-C9cKcMg3.js +0 -2
  229. package/payload/server/public/assets/packet-4T2RLAQJ-6GjqIKgu.js +0 -1
  230. package/payload/server/public/assets/pie-ZZUOXDRM-T72DJ5JR.js +0 -1
  231. package/payload/server/public/assets/radar-PYXPWWZC-x-6xW1dE.js +0 -1
  232. package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-DGDGYxGd.js +0 -1
  233. package/payload/server/public/assets/treeView-SZITEDCU-5-6wEryJ.js +0 -1
  234. package/payload/server/public/assets/treemap-W4RFUUIX-DF48Cc86.js +0 -1
  235. package/payload/server/public/assets/wardley-RL74JXVD-duKIa6Hq.js +0 -1
  236. /package/payload/server/public/assets/{_baseFor-Cam2PbSt.js → _baseFor-BHtDrjIo.js} +0 -0
  237. /package/payload/server/public/assets/{array-DYRGGQae.js → array-DetWRiSa.js} +0 -0
  238. /package/payload/server/public/assets/{chunk-CnGqDkHZ.js → chunk-CWOGyPgy.js} +0 -0
  239. /package/payload/server/public/assets/{cytoscape.esm-nWsJMTNI.js → cytoscape.esm-C9yNhe1u.js} +0 -0
  240. /package/payload/server/public/assets/{defaultLocale-Du1XY3Dp.js → defaultLocale-_WRwicXn.js} +0 -0
  241. /package/payload/server/public/assets/{init-B5BXBRcm.js → init-sTEcj9YX.js} +0 -0
  242. /package/payload/server/public/assets/{katex-HOUACuRw.js → katex-s61Rgv6l.js} +0 -0
  243. /package/payload/server/public/assets/{path-CNO468J-.js → path-B0Ik7Tu9.js} +0 -0
  244. /package/payload/server/public/assets/{rough.esm-DRO6hWPh.js → rough.esm-DKRO8IF-.js} +0 -0
@@ -5,6 +5,8 @@ description: Use when authoring or auditing a page whose primary purpose is to a
5
5
 
6
6
  # Structured-answer page template
7
7
 
8
+ Invoked from `specialists:content-producer`.
9
+
8
10
  Answer engines (Claude, ChatGPT, Perplexity, Google AI Overviews, Bing Copilot) lift a single short span when they cite a page. That span is almost always the first paragraph after the H1, when it is short and direct. Every other element on the page either supports the lift or gets ignored.
9
11
 
10
12
  ## The shape
@@ -28,8 +28,12 @@ You are an AI-powered business assistant — like a trusted secretary who knows
28
28
 
29
29
  Every incoming customer message: identify (new vs existing) → triage (urgent/routine/quick) → gather info → respond or escalate → update memory.
30
30
 
31
+ Enquiries also arrive through the business's **published-site contact form**, which lands rows in a Cloudflare **D1** database rather than as messages — a primary inbound channel, not a side channel. When the owner asks about new sign-ups, contacts, or enquiries, read D1 **first** (see `references/site-lead-intake.md`), before memory or sessions.
32
+
31
33
  Track status through the pipeline: enquiry → quoted → booked → completed → invoiced → paid.
32
34
 
35
+ When a built document — a quote, contract, proposal, or set of terms — needs a recordable signature without DocuSign, load the `e-sign` skill (`skill-load skillName="e-sign"`).
36
+
33
37
  ## Session & Task Continuity
34
38
 
35
39
  Every conversation is a session. At the start of each session:
@@ -56,6 +60,7 @@ Load the relevant reference when the task requires it:
56
60
  - **Scheduling & briefings** → `references/scheduling.md` — booking protocol, geographic clustering, travel time estimation, morning briefings, holiday mode, GPS integration
57
61
  - **Invoices & payment** → `references/invoicing.md` — HTML invoice creation, PDF generation via `browser-navigate` + `browser-pdf-save`, sending, payment chase protocol
58
62
  - **Customer records** → `references/crm.md` — contact record management, `contact_lookup`/`contact_update` usage, pipeline tracking, when to create/update records
63
+ - **Site form submissions** → `references/site-lead-intake.md` — the published-site contact form is a primary inbound channel landing in Cloudflare D1; "any new sign-ups/contacts/enquiries?" reads D1 first, then dedupes each unswept row into the graph and routes it to the recorded front-line owner
59
64
  - **Document filing** → `references/document-management.md` — dual-scope filing system, naming conventions, folder structure for customer-facing vs internal documents
60
65
  - **Task & session management** → `references/task-management.md` — task lifecycle, session naming, cross-session progress tracking
61
66
  - **Owner profiling** → `references/profiling.md` — observation triggers, privacy boundaries, when and how to write preferences
@@ -36,6 +36,12 @@ Create a contact record on the **first meaningful interaction** with a new custo
36
36
  - Spam or wrong numbers
37
37
  - One-off enquiries that go nowhere (unless the business owner asks you to)
38
38
 
39
+ ## Inbound Enquiries Also Arrive via the Site Form → D1
40
+
41
+ Not every new contact comes in by phone or WhatsApp. The business's **published-site contact form** is a primary inbound-enquiry channel, and its submissions land as rows in a Cloudflare **D1** database, not as messages — nothing surfaces them automatically. When the owner asks about new sign-ups, contacts, or enquiries, read D1 first, then turn each new row into a contact record here. See `references/site-lead-intake.md` for the read-first-from-D1, dedupe, create-or-update, sweep-after-write capability.
42
+
43
+ A new site lead is routed to the install's **recorded front-line owner** — and that ownership is **operator data** (recorded on the graph, or asked of the operator if not yet recorded), never a guessed name and never baked into a reference. Routing a lead to whoever seems plausible is the mistake; route to the recorded owner.
44
+
39
45
  ## When to Update a Record
40
46
 
41
47
  Update the contact record after every event that changes the customer's status or adds useful information:
@@ -0,0 +1,52 @@
1
+ # Managing site form submissions (the published-site → D1 inbound channel)
2
+
3
+ The business's published website has a contact / enquiry form, and that form is a **primary inbound-enquiry channel** — equal to phone and WhatsApp, not a side channel. Submissions do not arrive as messages; they land as rows in a **Cloudflare D1** database (the leads DB created in `plugins/cloudflare/references/d1-data-capture.md`). Nothing surfaces them automatically. This reference is the capability: when the owner asks about new enquiries, read D1 first, then turn each new row into a graph contact routed to the right person.
4
+
5
+ The failure this prevents is concrete: asked "any new sign-ups?", an agent that searches memory or lists public-agent sessions will report "nothing new" while real leads sit unswept in D1. Memory and public-agent sessions are not where site enquiries live. **D1 is.**
6
+
7
+ ---
8
+
9
+ ## "Any new sign-ups / contacts / enquiries?" → read D1 first
10
+
11
+ When the owner asks whether anything new has come in — sign-ups, contacts, enquiries, leads — the **first action is a D1 read of the leads DB**, before `memory_search`, before `contact_lookup` sweeps, before any public-agent `session-list`. The unswept set is the answer:
12
+
13
+ ```bash
14
+ CLOUDFLARE_API_TOKEN="${MINTED_PAGES_D1}" wrangler d1 execute <db-name> --remote --json --command \
15
+ "SELECT id, name, email, message, created_at FROM leads WHERE swept = 0 ORDER BY id;"
16
+ ```
17
+
18
+ This composes `plugins/cloudflare/references/d1-data-capture.md` § 6 (read/sweep) and § 0 (minting the Pages-+-D1 token); the credential and token discipline there is binding. Before this `wrangler` call: run the load-credentials pre-flight from `plugins/cloudflare/references/api.md` (source the secrets file, assert `CLOUDFLARE_API_TOKEN` non-empty), mint the narrow Pages-+-D1 token, and confirm `${MINTED_PAGES_D1}` is non-empty — so the incident's `CLOUDFLARE_API_TOKEN not set` failure cannot reach the leads read. The DB name and the site's project are operator/install data, recorded where the site was set up — not guessed.
19
+
20
+ **Outcome contract for the answer:** the transcript shows a D1 round-trip (`wrangler d1 execute … SELECT … WHERE swept = 0`) as the response to "any new sign-ups?", not a memory search. A `memory_search` or public-agent `session-list` returned as the answer is the defect, not the answer.
21
+
22
+ ---
23
+
24
+ ## Each unswept row → dedupe, create-or-update, then route
25
+
26
+ For every row in the unswept set, in order:
27
+
28
+ 1. **Dedupe against the graph.** Look the person up by the identifiers the row actually carries. The canonical leads table (`plugins/cloudflare/references/d1-data-capture.md` § 3) holds `name, email, message` — so `contact_lookup` on the row's **email**, and on a **phone** only when the form captured one and the SELECT fetched it. Match the columns the form collects; do not look up a field the row does not carry. A match means update; no match means create.
29
+ 2. **Create or update the contact.** No match → `contact_create` with the name and identifiers from the row, `status: enquiry`, `source` naming the site form. Match → `contact_update` to add anything new (email/phone/message context) without clobbering existing fields. Write the enquiry detail into the contact's memory profile as in `references/crm.md`.
30
+ 3. **Route to the install's recorded front-line owner** (below).
31
+ 4. **Sweep the row only after the graph write is confirmed.** Flip `swept = 1` for the ingested rows once the contact create/update has returned successfully — never before. An unconfirmed write followed by a sweep loses the lead silently.
32
+
33
+ ```bash
34
+ CLOUDFLARE_API_TOKEN="${MINTED_PAGES_D1}" wrangler d1 execute <db-name> --remote --command \
35
+ "UPDATE leads SET swept = 1 WHERE swept = 0;"
36
+ ```
37
+
38
+ A row is the durable record until it is swept; the sweep is the commit, and it follows the graph write, not the other way round.
39
+
40
+ ---
41
+
42
+ ## Front-line-owner routing is operator data, never a baked name
43
+
44
+ A new site lead is assigned to the install's **recorded front-line owner** — the person enquiries route to for first contact and follow-up. That ownership is **operator data**: it lives on the graph (a recorded property or relationship on the business/account) or, if not yet recorded, it is **asked of the operator**. It is never a name baked into this reference, and never guessed from context. Assigning a lead to whoever seems plausible — a back-office contact, a name seen earlier in the session — is the defect this rule prevents; an owner the operator then has to correct means the routing was guessed, not read.
45
+
46
+ If the front-line owner is not recorded on the install, ask the operator who new enquiries should go to and record it, then route. (Populating that data is an operator action; this reference states only the routing rule and where the data lives.)
47
+
48
+ ---
49
+
50
+ ## Why D1, not memory
51
+
52
+ Memory profiles and public-agent sessions describe people the business already talks to. A site enquiry is a **new** signal arriving through the website, captured by the form's Pages Function into D1 with a `swept` cursor precisely so "what's new" is a deterministic query rather than a guess. The whole point of the `swept = 0` set is that it is the authoritative, un-missable list of enquiries not yet brought into the business's records. Querying anything else first is how a real lead gets reported as "nothing new".
@@ -0,0 +1,309 @@
1
+ ---
2
+ name: e-sign
3
+ description: "Put a legally-recordable signature on any hosted document — a quote, contract, proposal, or set of terms — without DocuSign. A Cloudflare Pages signing form captures the acceptance to D1; the agent then sweeps the row, renders the signed content to PDF, and emails both parties. Triggers: 'sign this proposal', 'get this contract signed', 'put a signature on this', 'e-signature', 'send for signature', 'accept a proposal online', 'signature on a document'."
4
+ ---
5
+
6
+ # E-signatures — signing any hosted document (form → D1 → PDF + email)
7
+
8
+ **Invoked by the business-assistant (or admin) directly** when a built document — a quote, contract, proposal, or set of terms — needs a recordable signature.
9
+
10
+ How to put a legally-recordable signature on **any** document the business hosts — a quote, a contract, a proposal, a set of terms — without DocuSign. The document is deployed to **Cloudflare Pages** with a signing form as its final section; the signer submits; a Pages Function writes the acceptance to **Cloudflare D1**; the agent later sweeps the row, renders the signed content to PDF, and emails both parties a confirmation.
11
+
12
+ This skill owns **only the signature**: the form, the capture, the PDF, and the dispatch. The document's body — what is actually being signed — is whatever flow built it (a quotation plugin, a hand-written contract, anything). This begins at *"a built document needs a signature."*
13
+
14
+ It composes two patterns you must read alongside it:
15
+
16
+ - **`cloudflare/references/d1-data-capture.md`** — the form → Pages Function → D1 → sweep mechanics, and the token-scope breakage (§ 0). Everything about minting the Pages-+-D1 token, `wrangler.toml`, and `wrangler d1 execute --remote` lives there; this skill does not repeat it.
17
+ - **`business-assistant/references/invoicing.md`** — PDF generation via `browser-navigate` + `browser-pdf-save` (its step 4). The signed-content PDF uses that exact pattern.
18
+
19
+ Auth for every `wrangler`/API call is a minted narrow token per `cloudflare/references/api.md`. Deploy mechanics are `cloudflare/references/hosting-sites.md`.
20
+
21
+ ---
22
+
23
+ ## 0. Pre-flight — block on either prerequisite
24
+
25
+ Do **not** deploy until both pass. Each failure has one exact remediation; stop and give it.
26
+
27
+ 1. **Email must be configured.** Call `email-status`. If it does not report a configured inbox, stop:
28
+ > "Email isn't set up yet. Run `email-setup` first, then ask me again."
29
+
30
+ Dispatch (§ 6) sends through `email-send`; without a configured inbox the acceptance is captured but never delivered.
31
+
32
+ 2. **The Cloudflare master token must exist.** With `SECRETS_DIR` per `api.md` (`~/<brand>-code/data/accounts/<accountId>/secrets`):
33
+
34
+ ```bash
35
+ ( test -s "${SECRETS_DIR}/cloudflare.env" && grep -q '^CLOUDFLARE_API_TOKEN=' "${SECRETS_DIR}/cloudflare.env" ) \
36
+ && echo "ok" || echo "missing"
37
+ ```
38
+
39
+ On `missing`, stop:
40
+ > "There's no Cloudflare master token. Provision it once per `cloudflare/references/api.md` § Provisioning the master token, then ask me again."
41
+
42
+ ---
43
+
44
+ ## 1. The signable document
45
+
46
+ A self-contained HTML document — all styles inline, no external resources — deployed to **Cloudflare Pages**, not the local platform server. Pages is required because only a Pages Function can receive the acceptance POST; a page served off the platform device has nowhere to write the row.
47
+
48
+ - **Slug carries a random hex suffix** so the URL is unguessable: `quote-lakeside-7f3a9c.html` (or a project whose name carries the suffix). Mint the suffix once: `openssl rand -hex 3`.
49
+ - **Page carries `<meta name="robots" content="noindex,nofollow">`** in `<head>` — a signable document is private, never indexed.
50
+ - **Assign a `DOC_REF`** — a short stable identifier for this one document, e.g. `QUOTE-LAKESIDE`. It keys the acceptance row and seeds the token. It is per-document and **never reused for changed terms** (see immutability, below).
51
+
52
+ **Immutability — signed content must not change.** Once an acceptance is recorded against a `DOC_REF`, the deployed document **must not be edited**. The PDF that is dispatched is rendered from the live page, so any later edit silently changes what the records claim was signed. Re-issuing changed terms is a **new** document with a **new** `DOC_REF` and a fresh deploy — never an edit to the signed one. (Failure-mode detail: § 8.3.)
53
+
54
+ ---
55
+
56
+ ## 2. The signing form (final section of the document)
57
+
58
+ The form is the document's last section. It carries three fixed controls plus whatever the operator's document needs:
59
+
60
+ - **`name`** — required.
61
+ - **`email`** — required (this is where the signer's confirmation is sent).
62
+ - **An agreement checkbox** — required; the signer cannot submit without ticking it.
63
+ - **Operator-defined fields** — any extra inputs the specific document needs (company, project address, a quoted figure to confirm). These are **discovered from the document**, not fixed here; the client JS below sweeps every named field beyond the fixed three into `captured_json`.
64
+
65
+ On submit, client JS mints an **acceptance token** and POSTs JSON to the Pages Function. On `{"ok":true}` the form is replaced by a confirmation panel showing the token; on any failure the signer gets a pre-filled `mailto:` fallback so the acceptance is never lost.
66
+
67
+ **Acceptance token** — `<DOC_REF>-<5-char base36 timestamp>-<6-char djb2 hash>`. The hash is djb2 over `name + email + DOC_REF`: it binds the signer's identity to this document, so a UNIQUE collision on the token means *the same signer re-submitting the same document* — a genuine duplicate, not a clash between two signers.
68
+
69
+ The confirmation and fallback panels are built with `textContent` and DOM nodes, never `innerHTML` — the signer's own name and email flow back into the page, and `innerHTML` would make that an injection surface.
70
+
71
+ ```html
72
+ <section id="sign">
73
+ <h2>Accept &amp; sign</h2>
74
+ <form id="esign">
75
+ <input name="name" required placeholder="Full name">
76
+ <input name="email" type="email" required placeholder="Email">
77
+ <!-- operator-defined fields go here, e.g.:
78
+ <input name="company" placeholder="Company">
79
+ <input name="project_address" placeholder="Project address"> -->
80
+ <label><input type="checkbox" name="agree" required>
81
+ I have read and agree to the terms above.</label>
82
+ <button type="submit">Sign</button>
83
+ </form>
84
+ <div id="confirm" hidden></div>
85
+ </section>
86
+
87
+ <script>
88
+ const DOC_REF = "QUOTE-LAKESIDE"; // unique per document; never reused for changed terms
89
+ const ACCEPT_URL = "/api/accept";
90
+ const OWNER_MAILTO = "OWNER@EXAMPLE.COM"; // fallback recipient = the business's configured address
91
+
92
+ // 6-char base36 djb2 — deterministic over signer identity + document.
93
+ function djb2(s){let h=5381;for(let i=0;i<s.length;i++)h=((h<<5)+h+s.charCodeAt(i))>>>0;return h.toString(36).slice(-6).padStart(6,"0");}
94
+
95
+ document.getElementById("esign").addEventListener("submit", async (e) => {
96
+ e.preventDefault();
97
+ const f = e.target;
98
+ const name = f.name.value.trim();
99
+ const email = f.email.value.trim();
100
+ const ts = Date.now().toString(36).slice(-5);
101
+ const token = `${DOC_REF}-${ts}-${djb2(name + email + DOC_REF)}`;
102
+
103
+ // Everything beyond the fixed three fields is operator-defined; carry it verbatim.
104
+ const extra = {};
105
+ for (const el of f.elements) {
106
+ if (!el.name || ["name", "email", "agree"].includes(el.name)) continue;
107
+ extra[el.name] = el.type === "checkbox" ? el.checked : el.value;
108
+ }
109
+ const payload = { name, email, token, doc_ref: DOC_REF, ...extra };
110
+
111
+ const c = document.getElementById("confirm");
112
+ c.textContent = "";
113
+ try {
114
+ const r = await fetch(ACCEPT_URL, {
115
+ method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(payload),
116
+ });
117
+ const j = await r.json();
118
+ if (!j.ok) throw new Error(j.error || "rejected");
119
+ f.hidden = true; c.hidden = false;
120
+ const p = document.createElement("p");
121
+ const ref = document.createElement("strong");
122
+ ref.textContent = j.token;
123
+ p.append("Signed. Your acceptance reference is ", ref, ". A copy has been sent for processing.");
124
+ c.append(p);
125
+ } catch (err) {
126
+ // Function unreachable or insert failed — never drop the acceptance; offer the pre-filled mailto.
127
+ const subj = encodeURIComponent(`Acceptance: ${DOC_REF} (${token})`);
128
+ const body = encodeURIComponent(`I accept ${DOC_REF}.\nName: ${name}\nEmail: ${email}\nReference: ${token}`);
129
+ c.hidden = false;
130
+ const p = document.createElement("p");
131
+ const a = document.createElement("a");
132
+ a.href = `mailto:${OWNER_MAILTO}?subject=${subj}&body=${body}`;
133
+ a.textContent = "Send it by email instead";
134
+ p.append("We couldn't record your signature automatically. ", a, ".");
135
+ c.append(p);
136
+ }
137
+ });
138
+ </script>
139
+ ```
140
+
141
+ `OWNER_MAILTO` is the **business's own configured address** — read it from configured business identity, never invent one. It is the fallback path only; the primary path is the Function.
142
+
143
+ **PDF tip:** add `@media print { #sign { display: none } }` to the document's styles so the dispatched PDF carries the agreed content, not an empty form.
144
+
145
+ ---
146
+
147
+ ## 3. D1 table — one per brand, shared across that brand's documents
148
+
149
+ Follow `d1-data-capture.md` for creating the database, the `wrangler.toml` binding, and the both-Pages-Edit-**and**-D1-Edit token. The schema here is generic — it imposes no business fields; operator-defined fields live in `captured_json`:
150
+
151
+ ```bash
152
+ CLOUDFLARE_API_TOKEN="${MINTED_PAGES_D1}" wrangler d1 execute <db-name> --remote --command \
153
+ "CREATE TABLE IF NOT EXISTS acceptances (
154
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
155
+ doc_ref TEXT NOT NULL,
156
+ name TEXT NOT NULL,
157
+ email TEXT NOT NULL,
158
+ token TEXT NOT NULL UNIQUE,
159
+ captured_json TEXT,
160
+ accepted_at TEXT NOT NULL DEFAULT (datetime('now')),
161
+ swept INTEGER NOT NULL DEFAULT 0
162
+ );"
163
+ ```
164
+
165
+ One `acceptances` table serves every document the brand signs; rows are separated by `doc_ref`. `token UNIQUE` is what makes a re-submit idempotent. `swept` is the dispatch cursor (§ 6).
166
+
167
+ ---
168
+
169
+ ## 4. The Pages Function — `functions/api/accept.ts`
170
+
171
+ Served at `POST /api/accept` by file path. It validates the four required fields, inserts, and handles the UNIQUE collision gracefully — an already-recorded token still returns `ok:true` so the signer never sees an error for signing twice. No secret lives in this file; the `DB` binding is injected at runtime.
172
+
173
+ ```ts
174
+ interface Env { DB: D1Database }
175
+
176
+ const cors = {
177
+ "Access-Control-Allow-Origin": "*",
178
+ "Access-Control-Allow-Methods": "POST, OPTIONS",
179
+ "Access-Control-Allow-Headers": "Content-Type",
180
+ };
181
+ const json = (body: unknown, status = 200) =>
182
+ new Response(JSON.stringify(body), { status, headers: { "Content-Type": "application/json", ...cors } });
183
+
184
+ export const onRequestOptions: PagesFunction = async () => new Response(null, { status: 204, headers: cors });
185
+
186
+ export const onRequestPost: PagesFunction<Env> = async ({ request, env }) => {
187
+ let p: any;
188
+ try { p = await request.json(); } catch { return json({ ok: false, error: "bad json" }, 400); }
189
+
190
+ const name = String(p.name ?? "").trim();
191
+ const email = String(p.email ?? "").trim();
192
+ const token = String(p.token ?? "").trim();
193
+ const docRef = String(p.doc_ref ?? "").trim();
194
+ if (!name || !email || !token || !docRef)
195
+ return json({ ok: false, error: "name, email, token, doc_ref required" }, 400);
196
+
197
+ // Everything beyond the four required fields is operator-defined; stash verbatim.
198
+ const { name: _n, email: _e, token: _t, doc_ref: _d, ...extra } = p;
199
+ const capturedJson = Object.keys(extra).length ? JSON.stringify(extra) : null;
200
+
201
+ try {
202
+ await env.DB
203
+ .prepare("INSERT INTO acceptances (doc_ref, name, email, token, captured_json) VALUES (?, ?, ?, ?, ?)")
204
+ .bind(docRef, name, email, token, capturedJson)
205
+ .run();
206
+ } catch (err) {
207
+ // UNIQUE(token) collision = same signer, same document, already recorded. Idempotent: still ok.
208
+ // D1 surfaces the constraint as the stable text "UNIQUE constraint failed"; match the phrase,
209
+ // not the bare word, so an unrelated error never gets swallowed as a duplicate.
210
+ if (String(err).includes("UNIQUE constraint failed")) return json({ ok: true, token, duplicate: true });
211
+ return json({ ok: false, error: "insert failed" }, 500);
212
+ }
213
+ return json({ ok: true, token });
214
+ };
215
+ ```
216
+
217
+ **The single most common breakage — token scope.** A Pages-only token deploys the document and renders the form, but **every POST silently 500s at the insert** because the Function can't write D1. The minted token must carry **both Pages Edit and D1 Edit** (`d1-data-capture.md` § 0). When acceptances stop arriving, this is the first thing to check.
218
+
219
+ ---
220
+
221
+ ## 5. Deploy
222
+
223
+ Deploy the document + Function via `hosting-sites.md`, with the both-Pages-Edit-and-D1-Edit token. The `[[d1_databases]]` binding in `wrangler.toml` is what wires the deployed Function to the `acceptances` database.
224
+
225
+ ---
226
+
227
+ ## 6. Sweep + dispatch
228
+
229
+ When the operator asks for new acceptances (there is no standing cron — see § 8.2 for the reconciliation a scheduled task would run). Two tokens are minted here, both per `api.md` § Minting a narrow token, resolving permission-group ids exactly as `d1-data-capture.md` § 0 does: `${MINTED_D1_READ}` carries only **D1 Read** (for the SELECTs), `${MINTED_D1_EDIT}` carries **D1 Edit** (for the per-row mark-swept). Minting read-scoped where only a read is needed keeps the edit token off every command that does not write.
230
+
231
+ 1. **Read the unswept rows** with `${MINTED_D1_READ}`:
232
+
233
+ ```bash
234
+ CLOUDFLARE_API_TOKEN="${MINTED_D1_READ}" wrangler d1 execute <db-name> --remote --json --command \
235
+ "SELECT id, doc_ref, name, email, token, captured_json, accepted_at
236
+ FROM acceptances WHERE swept = 0 ORDER BY id;"
237
+ ```
238
+
239
+ 2. **For each unswept row, in order — dispatch, then mark that one row swept:**
240
+ - **Render the signed content to PDF.** `browser-navigate` to the deployed document URL, then `browser-pdf-save` to an absolute path under the account directory — the `invoicing.md` step-4 pattern. The PDF is of the live page, which (immutability, § 1) is exactly what was signed.
241
+ - **Send twice via `email-send`**, each carrying `doc_ref`, `name`, `token`, `accepted_at`, and the PDF as an attachment:
242
+ - once to the **business owner's configured address** (read from configured business identity — never hard-code a recipient in this workflow);
243
+ - once to the **signer's captured `email`**.
244
+ - **Mark this row — and only this row — swept, once both sends return a verified result**, keyed by its `id`:
245
+
246
+ ```bash
247
+ CLOUDFLARE_API_TOKEN="${MINTED_D1_EDIT}" wrangler d1 execute <db-name> --remote --command \
248
+ "UPDATE acceptances SET swept = 1 WHERE id = <row-id>;"
249
+ ```
250
+
251
+ Never flip `swept` on assumption. If either `email-send` for a row fails, **skip its UPDATE** and move to the next row; that row stays `swept = 0` so the next sweep (or the reconciliation in § 8.2) re-surfaces it. A bulk `WHERE swept = 0` here is wrong — it would mark rows whose dispatch failed earlier in the same batch; the per-row `WHERE id = <row-id>` is what makes the `swept` flag track verified dispatch exactly.
252
+
253
+ ---
254
+
255
+ ## 7. Outcome contract
256
+
257
+ The acceptance flow is proven end-to-end on the **live** Pages deployment — the D1 row, not the POST status, is the contract:
258
+
259
+ ```bash
260
+ # 1. POST a test acceptance to the live Function.
261
+ curl -sS -X POST -H "Content-Type: application/json" \
262
+ -d '{"name":"verify","email":"test@example.com","token":"QUOTE-LAKESIDE-abcde-zzzzzz","doc_ref":"QUOTE-LAKESIDE"}' \
263
+ "https://<project>.pages.dev/api/accept"
264
+ # expect: {"ok":true,"token":"QUOTE-LAKESIDE-abcde-zzzzzz"}
265
+
266
+ # 2. The row exists in the unswept set. (A 200 with no row here is a FAILURE — see § 8.1.)
267
+ CLOUDFLARE_API_TOKEN="${MINTED_D1_READ}" wrangler d1 execute <db-name> --remote --command \
268
+ "SELECT token FROM acceptances WHERE swept = 0;"
269
+
270
+ # 3. POST the SAME token again → still {"ok":true}; the SELECT shows exactly one row (UNIQUE handled).
271
+
272
+ # 4. Run the sweep (§ 6): both the owner address and test@example.com hold the confirmation email,
273
+ # each with the signed-content PDF attached; re-running the SELECT WHERE swept = 0 no longer returns the row.
274
+ ```
275
+
276
+ Surface every step as verb + target ("inserting a test acceptance", "sweeping unswept rows from `<db-name>`"). **Token values are never printed** to chat or stdout outside the signer-facing confirmation panel.
277
+
278
+ ---
279
+
280
+ ## 8. Failure modes and the signal for each
281
+
282
+ 1. **POST silently 500s at the insert (token missing D1 Edit).** Signal fires *without reproducing end to end*: the test POST returns non-`ok`, and the confirming `SELECT WHERE swept = 0` shows no new row. Fix: re-mint the token confirming **both** Pages Edit and D1 Edit (`d1-data-capture.md` § 0). This is the first thing to check when acceptances stop arriving.
283
+
284
+ 2. **Acceptance recorded but never dispatched (no-event failure).** The sweep never ran, or `email-send` failed after the row existed — this emits no log, because no action was taken. Detection is a **standing reconciliation, not a hung log line**:
285
+
286
+ ```bash
287
+ CLOUDFLARE_API_TOKEN="${MINTED_D1_READ}" wrangler d1 execute <db-name> --remote --command \
288
+ "SELECT count(*), min(accepted_at) FROM acceptances WHERE swept = 0;"
289
+ ```
290
+
291
+ Any row whose `accepted_at` is older than the sweep cadence is an undispatched acceptance. Run this on a schedule (or each time acceptances are reviewed) and treat a non-empty aged result as the failure signal. Because § 6 flips `swept` only after both sends verify, a failed dispatch leaves the row here for the next pass.
292
+
293
+ 3. **Document modified after an acceptance (signed-content drift).** Signal: the dispatched PDF no longer matches what was signed. Detection without reproduction — record the deployed document's content hash at first acceptance; a later mismatch is the failure. The rule (§ 1) is absolute: changed terms require a **new** `doc_ref` and a fresh deploy, never an edit to a signed document.
294
+
295
+ 4. **Duplicate-acceptance row.** Signal: a `token` appears more than once — the UNIQUE handling regressed.
296
+
297
+ ```bash
298
+ CLOUDFLARE_API_TOKEN="${MINTED_D1_READ}" wrangler d1 execute <db-name> --remote --command \
299
+ "SELECT token, count(*) c FROM acceptances GROUP BY token HAVING c > 1;"
300
+ ```
301
+
302
+ Healthy signature: re-POSTing a token leaves its count at one.
303
+
304
+ **Ground-truth lifeline** for one document's whole acceptance lifecycle:
305
+
306
+ ```bash
307
+ CLOUDFLARE_API_TOKEN="${MINTED_D1_READ}" wrangler d1 execute <db-name> --remote --json --command \
308
+ "SELECT id, doc_ref, token, accepted_at, swept FROM acceptances WHERE doc_ref = 'QUOTE-LAKESIDE' ORDER BY id;"
309
+ ```
@@ -41,6 +41,7 @@ The plugin registers no agent-facing MCP tools. Every Cloudflare operation is th
41
41
  | [api.md](references/api.md) | Cloudflare API library — canonical docs URL + curated endpoint map (DNS, zones, tunnels, token create/verify), the advanced master-token creation walkthrough, the account-scoped secrets-file storage convention, and the agent's mint-narrow-token discipline. |
42
42
  | [d1-data-capture.md](references/d1-data-capture.md) | Form → Pages Function → D1 store → read/sweep. The Pages-Edit **and** D1-Edit token-scope requirement, `wrangler d1 create`/`execute --remote`, the `swept` column. |
43
43
  | [hosting-sites.md](references/hosting-sites.md) | Deploy a static or Next.js site to Cloudflare Pages via `wrangler pages deploy`. |
44
+ | [web-analytics.md](references/web-analytics.md) | Why a registered site reports 0 visitors — the RUM API is blocked (no permission group), registered ≠ collecting, edge auto-injection is impossible for a Pages-only custom domain, and the beacon must be injected into the site HTML, rebuilt, and redeployed. Outcome: beacon present in live HTML. |
44
45
  | [dashboard-guide.md](references/dashboard-guide.md) | Click-paths for operations the operator prefers to do by hand — sign in, switch accounts, add a site, edit an apex CNAME, verify nameservers, delete a tunnel, manage CNAME records, author an Access policy. |
45
46
  | [reset-guide.md](references/reset-guide.md) | Decision tree for reset vs. patch, the exact `pkill` incantation for token-mode connectors, and the dashboard cleanup paths for stray records and rogue entries. |
46
47
 
@@ -57,6 +57,18 @@ Load the master for a mint call:
57
57
  set -a; . "${SECRETS_DIR}/cloudflare.env"; set +a
58
58
  ```
59
59
 
60
+ ### Load-credentials pre-flight (assert before any `wrangler` / `curl`)
61
+
62
+ Sourcing the file is not proof the token loaded — a missing file, a typo in the path, or an empty value all leave `CLOUDFLARE_API_TOKEN` unset, and the **next** `wrangler` / `curl` then fails with `CLOUDFLARE_API_TOKEN not set` *after* you have already issued the command. Make the load a **hard precondition**: source, then assert the token is non-empty, and stop if it is not. This block precedes every `wrangler d1`, `wrangler pages`, and Cloudflare-API `curl` in this reference and the ones that compose it:
63
+
64
+ ```bash
65
+ set -a; . "${SECRETS_DIR}/cloudflare.env"; set +a
66
+ : "${CLOUDFLARE_API_TOKEN:?credentials not loaded — source the secrets file before any wrangler/curl}"
67
+ : "${CLOUDFLARE_ACCOUNT_ID:?account id not loaded — source the secrets file before any wrangler/curl}"
68
+ ```
69
+
70
+ The `:?` form aborts the shell with the given message when the variable is empty or unset, so a credential-unloaded call can never reach the API. The token value is never printed — only the abort message on failure.
71
+
60
72
  ---
61
73
 
62
74
  ## Provisioning the master token (ADVANCED — operator-guided, never automated)
@@ -75,12 +87,14 @@ This is the one manual, operator-driven step. The agent relays the click-path; i
75
87
  5. Click **Continue to summary**, then **Create Token**.
76
88
  6. Copy the token **once** — Cloudflare shows it a single time. Paste it into the secrets file (above). The agent stores it without echoing it back.
77
89
 
78
- Verify the master works (token value never printed — only the verification result):
90
+ Verify the master works (token value never printed — only the verification result). The master provisioned above is an **account-scoped** token (`cfat_…`, scoped to one account under **Account Resources**), so it verifies at the **account** endpoint, not the user endpoint. Verifying a `cfat_…` token at `/user/tokens/verify` returns `Invalid API Token` even when the token is valid:
79
91
 
80
92
  ```bash
81
93
  set -a; . "${SECRETS_DIR}/cloudflare.env"; set +a
94
+ : "${CLOUDFLARE_API_TOKEN:?credentials not loaded — source the secrets file first}"
95
+ : "${CLOUDFLARE_ACCOUNT_ID:?account id not loaded — source the secrets file first}"
82
96
  curl -sS -H "Authorization: Bearer ${CLOUDFLARE_API_TOKEN}" \
83
- "https://api.cloudflare.com/client/v4/user/tokens/verify" | jq '.result.status'
97
+ "https://api.cloudflare.com/client/v4/accounts/${CLOUDFLARE_ACCOUNT_ID}/tokens/verify" | jq '.result.status'
84
98
  # expect: "active"
85
99
  ```
86
100
 
@@ -128,7 +142,7 @@ Look up `<pages-edit-permission-group-id>` (and the ids for DNS Edit, D1 Edit, e
128
142
  Request/response shapes live at the canonical docs URL; this is the index of what to reach for. `${ACC}` = `CLOUDFLARE_ACCOUNT_ID`, `${ZONE}` = the zone id, `${TOKEN}` = a **minted narrow** token.
129
143
 
130
144
  ### Token create / verify
131
- - `GET /user/tokens/verify` — confirm a token is active.
145
+ - `GET /accounts/${ACC}/tokens/verify` — confirm an **account-scoped** (`cfat_…`) token is active. This is the endpoint for the master and every minted narrow token here, all of which are account-scoped. **`/user/tokens/verify` is user-scoped only** and returns `Invalid API Token` for a `cfat_…` token even when it is valid — do not use it to verify an account token.
132
146
  - `POST /accounts/${ACC}/tokens` — mint a narrow token (see above).
133
147
  - `GET /accounts/${ACC}/tokens/permission_groups` — resolve permission-group ids for scoping.
134
148
 
@@ -4,6 +4,8 @@ The dashboard is the only surface for operations the CLI cannot perform: signing
4
4
 
5
5
  Anchor on the dashboard's text labels, not pixel positions. Cloudflare reshuffles its UI every few months; when a label has moved or been renamed, the operator will find the new location faster than any stale screenshot would help.
6
6
 
7
+ **Never fabricate a navigation path.** Only relay a click-path that appears verbatim in this file or another verified runbook. When the operator needs a dashboard step that is **not** in a verified path here — or when a relayed path turns out to be wrong ("No such menu") — do **not** invent menu names or guess where Cloudflare put it. Ask the operator what they see on their screen, or ask them to paste the snippet / value you need, and route from there. A guessed nav path that sends the operator hunting for a menu that does not exist is the failure this rule prevents; an honest "I don't have a verified path for that — what do you see under X?" is correct.
8
+
7
9
  ---
8
10
 
9
11
  ## Sign in and confirm which account
@@ -168,6 +170,14 @@ No server restart is needed. Verify with `curl -s https://<domain>/ | head` —
168
170
 
169
171
  ---
170
172
 
173
+ ## Web Analytics — registration is here, but collection is a beacon in the HTML
174
+
175
+ Cloudflare Web Analytics is configured in the dashboard, but registering a site there does **not** start collection — the beacon must be present in the served HTML. The full diagnosis and fix (why the RUM API is blocked, why registered ≠ collecting, why auto-injection is impossible for a Pages-only custom domain, and the inject-rebuild-redeploy path) lives in `references/web-analytics.md`. Read it before answering any "0 visitors / no analytics" question.
176
+
177
+ Because Cloudflare moves this area and the exact menu path varies by account, do not guess the click-path to the Web Analytics page. Ask the operator what they see, or have them paste the site's beacon snippet, then follow `references/web-analytics.md`.
178
+
179
+ ---
180
+
171
181
  ## Where tunnels live in the dashboard (as of 2026-04)
172
182
 
173
183
  Tunnels are under **Zero Trust** → **Networks** → **Tunnels**. This is not in the top-level sidebar — it is under the Zero Trust sub-dashboard, which itself appears in the main sidebar. Cloudflare has moved this location in the past. If **Networks** → **Tunnels** is not visible under Zero Trust, the label has likely been renamed; look for "Tunnels" under Networks or Access.
@@ -0,0 +1,64 @@
1
+ # Cloudflare Web Analytics — beacon-injected, not API-configured
2
+
3
+ Cloudflare Web Analytics is the privacy-first RUM (real-user-monitoring) product that counts visits, page views, and referrers for a site without cookies. On this install it is configured **in the dashboard** and collects data **only when the JS beacon is present in the served HTML**. This reference exists because a 0-visitor reading is almost always a missing beacon, not a registration problem — and because the API path most agents reach for first is structurally blocked here.
4
+
5
+ Pair this with `hosting-sites.md` (how the site is built and deployed) and `dashboard-guide.md` (the click-paths the operator runs when a dashboard step is genuinely required).
6
+
7
+ ---
8
+
9
+ ## The RUM site API is blocked without a RUM permission group
10
+
11
+ The `/accounts/{account_id}/rum/site_info` family of endpoints (list/create/edit RUM sites) requires an **Account · Web Analytics** permission group on the token. That permission group is **not among the master token's rows** provisioned in `api.md` § Provisioning (which owns the authoritative list). A minted token therefore cannot carry it, and every RUM API call returns auth error **10000 ("Authentication error")** regardless of which token is tried.
12
+
13
+ Do not chase 10000 by re-minting. Web Analytics site registration is a **dashboard-only** configuration step on this install: the operator creates/inspects the site under the dashboard, and the agent never drives it via the API. Treat the RUM API as unavailable, not as a token bug to fix.
14
+
15
+ ---
16
+
17
+ ## Registered ≠ collecting — 0 visitors means the beacon is missing
18
+
19
+ A site can show as **registered/enabled** in Web Analytics and still report **0 visitors**. Registration only allocates a site tag in the dashboard; it does not place anything on the page. Collection begins only when the beacon `<script>` is actually loaded by the browser, which requires the snippet to be present in the **served HTML**.
20
+
21
+ So when the operator asks "why are there no analytics / 0 visitors" on a site that is registered:
22
+
23
+ - The diagnosis is **not** "registration failed" and **not** "the dashboard is wrong".
24
+ - The diagnosis is "the beacon JS is not in the HTML". Confirm it directly against the live site:
25
+
26
+ ```bash
27
+ curl -s https://<host>/ | grep cloudflareinsights || echo "BEACON ABSENT"
28
+ ```
29
+
30
+ If `cloudflareinsights` does not appear, the beacon is missing and no amount of dashboard reconfiguration will start collection.
31
+
32
+ ---
33
+
34
+ ## Auto-injection needs a proxied DNS zone — Pages-only custom domains do not have one
35
+
36
+ Cloudflare can **auto-inject** the beacon at the edge, but only for hostnames served through a **Cloudflare-proxied DNS zone** (orange-cloud), where Cloudflare controls the HTML response on the way out. A **Pages-only custom domain** — e.g. a `*.pages.dev` project mapped to a custom domain that is **not** a CF-managed zone (such as `realagent.network`, which is Pages-only with no zone) — has no proxied zone in the path, so edge auto-injection is **structurally impossible**. There is no flag that turns it on; the prerequisite (a zone) does not exist.
37
+
38
+ Do not claim auto-injection "would work if the domain were a zone" as a fix — for a Pages-only custom domain the domain is not a zone, so the only path is manual injection into the site's own HTML.
39
+
40
+ ---
41
+
42
+ ## The working path: inject the beacon into the site HTML, rebuild, redeploy
43
+
44
+ For a Pages-only custom domain the beacon must be placed in the source HTML the site renders, then built and deployed:
45
+
46
+ 1. Get the site's beacon snippet from the dashboard (the `<script defer src="https://static.cloudflareinsights.com/beacon.min.js" data-cf-beacon='{"token":"<site-tag>"}'></script>` block, with the site's own token).
47
+ 2. Add it to the rendered HTML — for a Next.js app, the document `<head>` in `app/layout.tsx` (or the framework's equivalent root layout) so every route carries it.
48
+ 3. Rebuild the site and `wrangler pages deploy` per `hosting-sites.md`.
49
+ 4. Confirm the beacon is live (outcome contract below).
50
+
51
+ This is exactly the fix applied to `realagent.network` in the session that sourced this reference: the snippet was added to `app/layout.tsx`, rebuilt, redeployed, and confirmed in the served HTML.
52
+
53
+ ---
54
+
55
+ ## Outcome contract
56
+
57
+ Web Analytics is collecting when the beacon appears in the **live served HTML**, not when the dashboard says "enabled":
58
+
59
+ ```bash
60
+ curl -s https://<host>/ | grep cloudflareinsights
61
+ # a match (the beacon.min.js script line) is the proof
62
+ ```
63
+
64
+ A registered site in the dashboard is **not** the contract; a present beacon in the response is. Visitor counts then appear in the dashboard after real traffic — first data is delayed, so the grep, not an immediate visitor number, is what closes the task.
@@ -5,6 +5,8 @@ description: Cloudflare operations for the install — tunnel setup/diagnosis/re
5
5
 
6
6
  # Cloudflare operations
7
7
 
8
+ Invoked from `specialists:personal-assistant`.
9
+
8
10
  This is the entry point for every Cloudflare task on the install. Pick the operation class, read the matching reference, run the command via Bash, relay the literal output, and prove the outcome with a live signal. There are no Cloudflare MCP tools; the plugin registers none. The agent never browser-automates the dashboard — the operator clicks where a click is genuinely required.
9
11
 
10
12
  | You want to… | Read | Outcome contract |
@@ -15,6 +17,7 @@ This is the entry point for every Cloudflare task on the install. Pick the opera
15
17
  | Call the Cloudflare API (DNS, zones, tunnels, Access, token mint) | `references/api.md` | the API call returns success and the change is observable |
16
18
  | Deploy a static / Next.js site to Cloudflare Pages | `references/hosting-sites.md` | the deployed URL serves the new build |
17
19
  | Capture form/waitlist submissions into a database | `references/d1-data-capture.md` | a test POST appears in `SELECT … WHERE swept = 0` |
20
+ | Diagnose / enable Web Analytics on a site (0 visitors, no data) | `references/web-analytics.md` | beacon present in live HTML (`curl -s https://<host>/ \| grep cloudflareinsights`) |
18
21
 
19
22
  ## Two auth paths, by operation class
20
23
 
@@ -5,6 +5,8 @@ description: "Traces an academic or scientific claim through the publication cha
5
5
 
6
6
  # Academic verify
7
7
 
8
+ Invoked from `specialists:research-assistant`.
9
+
8
10
  This skill is for claims that arrive as "studies show", "research suggests", or a named paper title. It traces the claim to the actual paper, the actual journal, and the actual conclusion of the source. It never says "verified" without a primary source URL.
9
11
 
10
12
  ## When to run
@@ -5,6 +5,8 @@ description: "Reads a book the owner provides (PDF, ePub, web link, or text), wa
5
5
 
6
6
  # Book mirror
7
7
 
8
+ Invoked from `specialists:research-assistant`.
9
+
8
10
  This skill turns a book into a usable document by walking the chapters one at a time and asking, for each: "what does this say, and how does it apply to this specific owner". The output is a personalised pack the owner can read once and act on. It is grounded in the graph; the right-hand column is always specific to what the graph already knows about the owner.
9
11
 
10
12
  ## When to run
@@ -5,6 +5,8 @@ description: "Recipe-driven structured-data extraction. Takes a named recipe and
5
5
 
6
6
  # Data research
7
7
 
8
+ Invoked from `specialists:research-assistant`.
9
+
8
10
  This skill turns unstructured-looking sources (emails, reports, forms, web pages) into structured graph entities. The transformation is always recipe-driven: a named YAML recipe declares what fields to extract, what validations to apply, and what graph nodes to write. The skill runs a named recipe; it never invents a schema on the fly.
9
11
 
10
12
  **Default parent.** Every recipe declares its hierarchy parent via `graph_target.parent` — either `Project` (the run-time recipe argument names which Project elementId to attach to) or `LocalBusiness` (the account root, for cross-project recipes like investor-updates). The skill refuses to run a recipe whose `parent` field is missing or whose named Project does not exist. Per-row writes attach to that parent via the canonical containment edge; the recipe's `relationships:` block describes cross-hierarchy edges (e.g. `ROUND_FOR → Organization`) which are written separately and do not substitute for the parent.
@@ -20,6 +20,8 @@ config:
20
20
 
21
21
  # Deep Research Skill
22
22
 
23
+ Invoked from `specialists:research-assistant`.
24
+
23
25
  You are a research agent. Answer questions using live web sources, synthesise findings across multiple sources, and present results with full attribution. You do not guess. You search, read, extract, and cite.
24
26
 
25
27
  ## Workflow
@@ -5,6 +5,8 @@ description: "Reads a source (article, book, report, document) through a specifi
5
5
 
6
6
  # Strategic reading
7
7
 
8
+ Invoked from `specialists:research-assistant`.
9
+
8
10
  This skill reads a document with a specific question in mind. The owner names the lens (a project, a problem, a goal). The skill summarises what the source actually says, then translates each section into how it applies to the named lens, and closes with a playbook of three to seven concrete steps. Both halves are grounded: the left half in the source text, the right half in the graph.
9
11
 
10
12
  ## When to run