@rubytech/create-maxy-code 0.1.469 → 0.1.471

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 (431) hide show
  1. package/dist/index.js +40 -0
  2. package/package.json +1 -1
  3. package/payload/platform/config/brand.json +2 -1
  4. package/payload/platform/docs/superpowers/plans/2026-07-18-graph-native-ledger.md +807 -0
  5. package/payload/platform/docs/superpowers/specs/2026-07-18-graph-native-ledger-design.md +241 -0
  6. package/payload/platform/lib/ledger-core/dist/ageing.d.ts +14 -0
  7. package/payload/platform/lib/ledger-core/dist/ageing.d.ts.map +1 -0
  8. package/payload/platform/lib/ledger-core/dist/ageing.js +38 -0
  9. package/payload/platform/lib/ledger-core/dist/ageing.js.map +1 -0
  10. package/payload/platform/lib/ledger-core/dist/balance.d.ts +25 -0
  11. package/payload/platform/lib/ledger-core/dist/balance.d.ts.map +1 -0
  12. package/payload/platform/lib/ledger-core/dist/balance.js +38 -0
  13. package/payload/platform/lib/ledger-core/dist/balance.js.map +1 -0
  14. package/payload/platform/lib/ledger-core/dist/index.d.ts +5 -0
  15. package/payload/platform/lib/ledger-core/dist/index.d.ts.map +1 -0
  16. package/payload/platform/lib/ledger-core/dist/index.js +5 -0
  17. package/payload/platform/lib/ledger-core/dist/index.js.map +1 -0
  18. package/payload/platform/lib/ledger-core/dist/money.d.ts +12 -0
  19. package/payload/platform/lib/ledger-core/dist/money.d.ts.map +1 -0
  20. package/payload/platform/lib/ledger-core/dist/money.js +16 -0
  21. package/payload/platform/lib/ledger-core/dist/money.js.map +1 -0
  22. package/payload/platform/lib/ledger-core/dist/reconcile.d.ts +51 -0
  23. package/payload/platform/lib/ledger-core/dist/reconcile.d.ts.map +1 -0
  24. package/payload/platform/lib/ledger-core/dist/reconcile.js +77 -0
  25. package/payload/platform/lib/ledger-core/dist/reconcile.js.map +1 -0
  26. package/payload/platform/lib/ledger-core/package.json +16 -0
  27. package/payload/platform/lib/ledger-core/src/__tests__/ageing.test.ts +42 -0
  28. package/payload/platform/lib/ledger-core/src/__tests__/balance.test.ts +48 -0
  29. package/payload/platform/lib/ledger-core/src/__tests__/reconcile.test.ts +87 -0
  30. package/payload/platform/lib/ledger-core/src/ageing.ts +51 -0
  31. package/payload/platform/lib/ledger-core/src/balance.ts +52 -0
  32. package/payload/platform/lib/ledger-core/src/index.ts +4 -0
  33. package/payload/platform/lib/ledger-core/src/money.ts +17 -0
  34. package/payload/platform/lib/ledger-core/src/reconcile.ts +109 -0
  35. package/payload/platform/lib/ledger-core/tsconfig.json +6 -0
  36. package/payload/platform/neo4j/schema.cypher +16 -0
  37. package/payload/platform/package.json +2 -2
  38. package/payload/platform/plugins/.claude-plugin/marketplace.json +10 -0
  39. package/payload/platform/plugins/admin/hooks/lib/maxy-mcp-plugins.txt +2 -0
  40. package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +171 -2
  41. package/payload/platform/plugins/cloudflare/bin/__tests__/portal-brand-css.test.sh +139 -0
  42. package/payload/platform/plugins/cloudflare/bin/portal-brand-css.mjs +128 -0
  43. package/payload/platform/plugins/cloudflare/mcp/__tests__/portal-enrol.test.ts +1 -1
  44. package/payload/platform/plugins/cloudflare/mcp/__tests__/skill-contract.test.ts +25 -0
  45. package/payload/platform/plugins/cloudflare/mcp/__tests__/template-config.test.ts +55 -3
  46. package/payload/platform/plugins/cloudflare/skills/data-portal/SKILL.md +38 -3
  47. package/payload/platform/plugins/cloudflare/skills/data-portal/template/{public/portal.css → portal.css} +13 -2
  48. package/payload/platform/plugins/cloudflare/skills/data-portal/template/wrangler.toml +1 -1
  49. package/payload/platform/plugins/docs/references/admin-ui.md +48 -0
  50. package/payload/platform/plugins/docs/references/google-guide.md +68 -0
  51. package/payload/platform/plugins/docs/references/internals.md +1 -1
  52. package/payload/platform/plugins/docs/references/ledger.md +43 -0
  53. package/payload/platform/plugins/email/PLUGIN.md +4 -0
  54. package/payload/platform/plugins/email/mcp/dist/__tests__/display-truncation.test.d.ts +2 -0
  55. package/payload/platform/plugins/email/mcp/dist/__tests__/display-truncation.test.d.ts.map +1 -0
  56. package/payload/platform/plugins/email/mcp/dist/__tests__/display-truncation.test.js +196 -0
  57. package/payload/platform/plugins/email/mcp/dist/__tests__/display-truncation.test.js.map +1 -0
  58. package/payload/platform/plugins/email/mcp/dist/__tests__/source-fetch-efficiency.test.js +10 -8
  59. package/payload/platform/plugins/email/mcp/dist/__tests__/source-fetch-efficiency.test.js.map +1 -1
  60. package/payload/platform/plugins/email/mcp/dist/__tests__/truncation-tool-output.test.d.ts +2 -0
  61. package/payload/platform/plugins/email/mcp/dist/__tests__/truncation-tool-output.test.d.ts.map +1 -0
  62. package/payload/platform/plugins/email/mcp/dist/__tests__/truncation-tool-output.test.js +110 -0
  63. package/payload/platform/plugins/email/mcp/dist/__tests__/truncation-tool-output.test.js.map +1 -0
  64. package/payload/platform/plugins/email/mcp/dist/lib/imap.d.ts +22 -0
  65. package/payload/platform/plugins/email/mcp/dist/lib/imap.d.ts.map +1 -1
  66. package/payload/platform/plugins/email/mcp/dist/lib/imap.js +90 -36
  67. package/payload/platform/plugins/email/mcp/dist/lib/imap.js.map +1 -1
  68. package/payload/platform/plugins/email/mcp/dist/tools/email-read.d.ts.map +1 -1
  69. package/payload/platform/plugins/email/mcp/dist/tools/email-read.js +6 -2
  70. package/payload/platform/plugins/email/mcp/dist/tools/email-read.js.map +1 -1
  71. package/payload/platform/plugins/email/mcp/dist/tools/email-search.d.ts.map +1 -1
  72. package/payload/platform/plugins/email/mcp/dist/tools/email-search.js +6 -2
  73. package/payload/platform/plugins/email/mcp/dist/tools/email-search.js.map +1 -1
  74. package/payload/platform/plugins/email/skills/email-ingest/SKILL.md +4 -0
  75. package/payload/platform/plugins/google/.claude-plugin/plugin.json +21 -0
  76. package/payload/platform/plugins/google/PLUGIN.md +104 -0
  77. package/payload/platform/plugins/google/lib/mcp-spawn-tee/index.js +193 -0
  78. package/payload/platform/plugins/google/lib/mcp-spawn-tee/package.json +3 -0
  79. package/payload/platform/plugins/google/mcp/dist/__tests__/account-register.test.d.ts +2 -0
  80. package/payload/platform/plugins/google/mcp/dist/__tests__/account-register.test.d.ts.map +1 -0
  81. package/payload/platform/plugins/google/mcp/dist/__tests__/account-register.test.js +60 -0
  82. package/payload/platform/plugins/google/mcp/dist/__tests__/account-register.test.js.map +1 -0
  83. package/payload/platform/plugins/google/mcp/dist/__tests__/calendar-tools.test.d.ts +2 -0
  84. package/payload/platform/plugins/google/mcp/dist/__tests__/calendar-tools.test.d.ts.map +1 -0
  85. package/payload/platform/plugins/google/mcp/dist/__tests__/calendar-tools.test.js +48 -0
  86. package/payload/platform/plugins/google/mcp/dist/__tests__/calendar-tools.test.js.map +1 -0
  87. package/payload/platform/plugins/google/mcp/dist/__tests__/google-client.test.d.ts +2 -0
  88. package/payload/platform/plugins/google/mcp/dist/__tests__/google-client.test.d.ts.map +1 -0
  89. package/payload/platform/plugins/google/mcp/dist/__tests__/google-client.test.js +25 -0
  90. package/payload/platform/plugins/google/mcp/dist/__tests__/google-client.test.js.map +1 -0
  91. package/payload/platform/plugins/google/mcp/dist/__tests__/index-registration.test.d.ts +2 -0
  92. package/payload/platform/plugins/google/mcp/dist/__tests__/index-registration.test.d.ts.map +1 -0
  93. package/payload/platform/plugins/google/mcp/dist/__tests__/index-registration.test.js +40 -0
  94. package/payload/platform/plugins/google/mcp/dist/__tests__/index-registration.test.js.map +1 -0
  95. package/payload/platform/plugins/google/mcp/dist/__tests__/pending-store.test.d.ts +2 -0
  96. package/payload/platform/plugins/google/mcp/dist/__tests__/pending-store.test.d.ts.map +1 -0
  97. package/payload/platform/plugins/google/mcp/dist/__tests__/pending-store.test.js +35 -0
  98. package/payload/platform/plugins/google/mcp/dist/__tests__/pending-store.test.js.map +1 -0
  99. package/payload/platform/plugins/google/mcp/dist/__tests__/token-store.test.d.ts +2 -0
  100. package/payload/platform/plugins/google/mcp/dist/__tests__/token-store.test.d.ts.map +1 -0
  101. package/payload/platform/plugins/google/mcp/dist/__tests__/token-store.test.js +85 -0
  102. package/payload/platform/plugins/google/mcp/dist/__tests__/token-store.test.js.map +1 -0
  103. package/payload/platform/plugins/google/mcp/dist/auth/pending-store.d.ts +25 -0
  104. package/payload/platform/plugins/google/mcp/dist/auth/pending-store.d.ts.map +1 -0
  105. package/payload/platform/plugins/google/mcp/dist/auth/pending-store.js +87 -0
  106. package/payload/platform/plugins/google/mcp/dist/auth/pending-store.js.map +1 -0
  107. package/payload/platform/plugins/google/mcp/dist/auth/token-store.d.ts +46 -0
  108. package/payload/platform/plugins/google/mcp/dist/auth/token-store.d.ts.map +1 -0
  109. package/payload/platform/plugins/google/mcp/dist/auth/token-store.js +138 -0
  110. package/payload/platform/plugins/google/mcp/dist/auth/token-store.js.map +1 -0
  111. package/payload/platform/plugins/google/mcp/dist/index.d.ts +25 -0
  112. package/payload/platform/plugins/google/mcp/dist/index.d.ts.map +1 -0
  113. package/payload/platform/plugins/google/mcp/dist/index.js +300 -0
  114. package/payload/platform/plugins/google/mcp/dist/index.js.map +1 -0
  115. package/payload/platform/plugins/google/mcp/dist/lib/google-client.d.ts +28 -0
  116. package/payload/platform/plugins/google/mcp/dist/lib/google-client.d.ts.map +1 -0
  117. package/payload/platform/plugins/google/mcp/dist/lib/google-client.js +102 -0
  118. package/payload/platform/plugins/google/mcp/dist/lib/google-client.js.map +1 -0
  119. package/payload/platform/plugins/google/mcp/dist/lib/log.d.ts +3 -0
  120. package/payload/platform/plugins/google/mcp/dist/lib/log.d.ts.map +1 -0
  121. package/payload/platform/plugins/google/mcp/dist/lib/log.js +16 -0
  122. package/payload/platform/plugins/google/mcp/dist/lib/log.js.map +1 -0
  123. package/payload/platform/plugins/google/mcp/dist/tools/account-info.d.ts +12 -0
  124. package/payload/platform/plugins/google/mcp/dist/tools/account-info.d.ts.map +1 -0
  125. package/payload/platform/plugins/google/mcp/dist/tools/account-info.js +23 -0
  126. package/payload/platform/plugins/google/mcp/dist/tools/account-info.js.map +1 -0
  127. package/payload/platform/plugins/google/mcp/dist/tools/account-list.d.ts +12 -0
  128. package/payload/platform/plugins/google/mcp/dist/tools/account-list.d.ts.map +1 -0
  129. package/payload/platform/plugins/google/mcp/dist/tools/account-list.js +9 -0
  130. package/payload/platform/plugins/google/mcp/dist/tools/account-list.js.map +1 -0
  131. package/payload/platform/plugins/google/mcp/dist/tools/account-register.d.ts +42 -0
  132. package/payload/platform/plugins/google/mcp/dist/tools/account-register.d.ts.map +1 -0
  133. package/payload/platform/plugins/google/mcp/dist/tools/account-register.js +102 -0
  134. package/payload/platform/plugins/google/mcp/dist/tools/account-register.js.map +1 -0
  135. package/payload/platform/plugins/google/mcp/dist/tools/calendar-cancel.d.ts +10 -0
  136. package/payload/platform/plugins/google/mcp/dist/tools/calendar-cancel.d.ts.map +1 -0
  137. package/payload/platform/plugins/google/mcp/dist/tools/calendar-cancel.js +9 -0
  138. package/payload/platform/plugins/google/mcp/dist/tools/calendar-cancel.js.map +1 -0
  139. package/payload/platform/plugins/google/mcp/dist/tools/calendar-create.d.ts +20 -0
  140. package/payload/platform/plugins/google/mcp/dist/tools/calendar-create.d.ts.map +1 -0
  141. package/payload/platform/plugins/google/mcp/dist/tools/calendar-create.js +25 -0
  142. package/payload/platform/plugins/google/mcp/dist/tools/calendar-create.js.map +1 -0
  143. package/payload/platform/plugins/google/mcp/dist/tools/calendar-event.d.ts +6 -0
  144. package/payload/platform/plugins/google/mcp/dist/tools/calendar-event.d.ts.map +1 -0
  145. package/payload/platform/plugins/google/mcp/dist/tools/calendar-event.js +7 -0
  146. package/payload/platform/plugins/google/mcp/dist/tools/calendar-event.js.map +1 -0
  147. package/payload/platform/plugins/google/mcp/dist/tools/calendar-freebusy.d.ts +14 -0
  148. package/payload/platform/plugins/google/mcp/dist/tools/calendar-freebusy.d.ts.map +1 -0
  149. package/payload/platform/plugins/google/mcp/dist/tools/calendar-freebusy.js +16 -0
  150. package/payload/platform/plugins/google/mcp/dist/tools/calendar-freebusy.js.map +1 -0
  151. package/payload/platform/plugins/google/mcp/dist/tools/calendar-list.d.ts +44 -0
  152. package/payload/platform/plugins/google/mcp/dist/tools/calendar-list.d.ts.map +1 -0
  153. package/payload/platform/plugins/google/mcp/dist/tools/calendar-list.js +36 -0
  154. package/payload/platform/plugins/google/mcp/dist/tools/calendar-list.js.map +1 -0
  155. package/payload/platform/plugins/google/mcp/dist/tools/calendar-respond.d.ts +16 -0
  156. package/payload/platform/plugins/google/mcp/dist/tools/calendar-respond.d.ts.map +1 -0
  157. package/payload/platform/plugins/google/mcp/dist/tools/calendar-respond.js +29 -0
  158. package/payload/platform/plugins/google/mcp/dist/tools/calendar-respond.js.map +1 -0
  159. package/payload/platform/plugins/google/mcp/dist/tools/calendar-update.d.ts +16 -0
  160. package/payload/platform/plugins/google/mcp/dist/tools/calendar-update.d.ts.map +1 -0
  161. package/payload/platform/plugins/google/mcp/dist/tools/calendar-update.js +32 -0
  162. package/payload/platform/plugins/google/mcp/dist/tools/calendar-update.js.map +1 -0
  163. package/payload/platform/plugins/google/mcp/package.json +20 -0
  164. package/payload/platform/plugins/google/references/auth.md +35 -0
  165. package/payload/platform/plugins/ledger/.claude-plugin/plugin.json +21 -0
  166. package/payload/platform/plugins/ledger/PLUGIN.md +82 -0
  167. package/payload/platform/plugins/ledger/lib/mcp-spawn-tee/index.js +193 -0
  168. package/payload/platform/plugins/ledger/lib/mcp-spawn-tee/package.json +3 -0
  169. package/payload/platform/plugins/ledger/mcp/dist/index.d.ts +2 -0
  170. package/payload/platform/plugins/ledger/mcp/dist/index.d.ts.map +1 -0
  171. package/payload/platform/plugins/ledger/mcp/dist/index.js +168 -0
  172. package/payload/platform/plugins/ledger/mcp/dist/index.js.map +1 -0
  173. package/payload/platform/plugins/ledger/mcp/dist/lib/ids.d.ts +4 -0
  174. package/payload/platform/plugins/ledger/mcp/dist/lib/ids.d.ts.map +1 -0
  175. package/payload/platform/plugins/ledger/mcp/dist/lib/ids.js +0 -0
  176. package/payload/platform/plugins/ledger/mcp/dist/lib/ids.js.map +1 -0
  177. package/payload/platform/plugins/ledger/mcp/dist/lib/neo4j.d.ts +5 -0
  178. package/payload/platform/plugins/ledger/mcp/dist/lib/neo4j.d.ts.map +1 -0
  179. package/payload/platform/plugins/ledger/mcp/dist/lib/neo4j.js +40 -0
  180. package/payload/platform/plugins/ledger/mcp/dist/lib/neo4j.js.map +1 -0
  181. package/payload/platform/plugins/ledger/mcp/dist/lib/read.d.ts +29 -0
  182. package/payload/platform/plugins/ledger/mcp/dist/lib/read.d.ts.map +1 -0
  183. package/payload/platform/plugins/ledger/mcp/dist/lib/read.js +138 -0
  184. package/payload/platform/plugins/ledger/mcp/dist/lib/read.js.map +1 -0
  185. package/payload/platform/plugins/ledger/mcp/dist/lib/types.d.ts +47 -0
  186. package/payload/platform/plugins/ledger/mcp/dist/lib/types.d.ts.map +1 -0
  187. package/payload/platform/plugins/ledger/mcp/dist/lib/types.js +14 -0
  188. package/payload/platform/plugins/ledger/mcp/dist/lib/types.js.map +1 -0
  189. package/payload/platform/plugins/ledger/mcp/dist/tools/balance.d.ts +18 -0
  190. package/payload/platform/plugins/ledger/mcp/dist/tools/balance.d.ts.map +1 -0
  191. package/payload/platform/plugins/ledger/mcp/dist/tools/balance.js +23 -0
  192. package/payload/platform/plugins/ledger/mcp/dist/tools/balance.js.map +1 -0
  193. package/payload/platform/plugins/ledger/mcp/dist/tools/cash-record.d.ts +26 -0
  194. package/payload/platform/plugins/ledger/mcp/dist/tools/cash-record.d.ts.map +1 -0
  195. package/payload/platform/plugins/ledger/mcp/dist/tools/cash-record.js +33 -0
  196. package/payload/platform/plugins/ledger/mcp/dist/tools/cash-record.js.map +1 -0
  197. package/payload/platform/plugins/ledger/mcp/dist/tools/invoice-record.d.ts +27 -0
  198. package/payload/platform/plugins/ledger/mcp/dist/tools/invoice-record.d.ts.map +1 -0
  199. package/payload/platform/plugins/ledger/mcp/dist/tools/invoice-record.js +40 -0
  200. package/payload/platform/plugins/ledger/mcp/dist/tools/invoice-record.js.map +1 -0
  201. package/payload/platform/plugins/ledger/mcp/dist/tools/payment-record.d.ts +46 -0
  202. package/payload/platform/plugins/ledger/mcp/dist/tools/payment-record.d.ts.map +1 -0
  203. package/payload/platform/plugins/ledger/mcp/dist/tools/payment-record.js +68 -0
  204. package/payload/platform/plugins/ledger/mcp/dist/tools/payment-record.js.map +1 -0
  205. package/payload/platform/plugins/ledger/mcp/dist/tools/reconcile.d.ts +20 -0
  206. package/payload/platform/plugins/ledger/mcp/dist/tools/reconcile.d.ts.map +1 -0
  207. package/payload/platform/plugins/ledger/mcp/dist/tools/reconcile.js +30 -0
  208. package/payload/platform/plugins/ledger/mcp/dist/tools/reconcile.js.map +1 -0
  209. package/payload/platform/plugins/ledger/mcp/dist/tools/statement.d.ts +26 -0
  210. package/payload/platform/plugins/ledger/mcp/dist/tools/statement.d.ts.map +1 -0
  211. package/payload/platform/plugins/ledger/mcp/dist/tools/statement.js +35 -0
  212. package/payload/platform/plugins/ledger/mcp/dist/tools/statement.js.map +1 -0
  213. package/payload/platform/plugins/ledger/mcp/package.json +21 -0
  214. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/ledger-vertical-coverage.test.d.ts +2 -0
  215. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/ledger-vertical-coverage.test.d.ts.map +1 -0
  216. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/ledger-vertical-coverage.test.js +81 -0
  217. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/ledger-vertical-coverage.test.js.map +1 -0
  218. package/payload/platform/plugins/memory/mcp/dist/lib/schema-loader.d.ts +9 -7
  219. package/payload/platform/plugins/memory/mcp/dist/lib/schema-loader.d.ts.map +1 -1
  220. package/payload/platform/plugins/memory/mcp/dist/lib/schema-loader.js +10 -8
  221. package/payload/platform/plugins/memory/mcp/dist/lib/schema-loader.js.map +1 -1
  222. package/payload/platform/plugins/memory/references/schema-base.md +22 -0
  223. package/payload/platform/plugins/memory/references/schema-construction.md +0 -18
  224. package/payload/platform/plugins/scheduling/PLUGIN.md +20 -0
  225. package/payload/platform/plugins/scheduling/mcp/dist/lib/meeting-write.d.ts.map +1 -1
  226. package/payload/platform/plugins/scheduling/mcp/dist/lib/meeting-write.js +11 -2
  227. package/payload/platform/plugins/scheduling/mcp/dist/lib/meeting-write.js.map +1 -1
  228. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-event.js +1 -1
  229. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-event.js.map +1 -1
  230. package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-create.js +3 -3
  231. package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-create.js.map +1 -1
  232. package/payload/platform/services/claude-session-manager/dist/activity-rows.d.ts +138 -0
  233. package/payload/platform/services/claude-session-manager/dist/activity-rows.d.ts.map +1 -0
  234. package/payload/platform/services/claude-session-manager/dist/activity-rows.js +268 -0
  235. package/payload/platform/services/claude-session-manager/dist/activity-rows.js.map +1 -0
  236. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.d.ts.map +1 -1
  237. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js +20 -0
  238. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js.map +1 -1
  239. package/payload/platform/services/claude-session-manager/dist/http-server.d.ts +6 -0
  240. package/payload/platform/services/claude-session-manager/dist/http-server.d.ts.map +1 -1
  241. package/payload/platform/services/claude-session-manager/dist/http-server.js +42 -0
  242. package/payload/platform/services/claude-session-manager/dist/http-server.js.map +1 -1
  243. package/payload/platform/services/claude-session-manager/dist/index.js +10 -1
  244. package/payload/platform/services/claude-session-manager/dist/index.js.map +1 -1
  245. package/payload/platform/services/claude-session-manager/dist/pty-census.d.ts +80 -6
  246. package/payload/platform/services/claude-session-manager/dist/pty-census.d.ts.map +1 -1
  247. package/payload/platform/services/claude-session-manager/dist/pty-census.js +96 -20
  248. package/payload/platform/services/claude-session-manager/dist/pty-census.js.map +1 -1
  249. package/payload/platform/services/claude-session-manager/dist/slice-memory-policy.d.ts.map +1 -1
  250. package/payload/platform/services/claude-session-manager/dist/slice-memory-policy.js +5 -1
  251. package/payload/platform/services/claude-session-manager/dist/slice-memory-policy.js.map +1 -1
  252. package/payload/platform/templates/specialists/agents/database-operator.md +1 -1
  253. package/payload/platform/templates/specialists/agents/personal-assistant.md +1 -1
  254. package/payload/platform/templates/specialists/agents/project-manager.md +1 -1
  255. package/payload/server/{chunk-POBXIHOF.js → chunk-BSTIQX52.js} +59 -1
  256. package/payload/server/maxy-edge.js +1 -1
  257. package/payload/server/public/activity.html +17 -0
  258. package/payload/server/public/assets/AdminLoginScreens-Rf94VD9w.js +1 -0
  259. package/payload/server/public/assets/AdminShell-CyGKg7MG.js +2 -0
  260. package/payload/server/public/assets/Checkbox-B0jmI2y2.js +1 -0
  261. package/payload/server/public/assets/HeaderMenu-BnTYGNaB.js +9 -0
  262. package/payload/server/public/assets/{useSubAccountSwitcher-pu0jjLCz.css → HeaderMenu-DtIEBHOU.css} +1 -1
  263. package/payload/server/public/assets/activity-BOBTWFr-.js +1 -0
  264. package/payload/server/public/assets/admin-HDLzwbLl.js +1 -0
  265. package/payload/server/public/assets/{arc-qKfXIt8v.js → arc-CFMJyMm-.js} +1 -1
  266. package/payload/server/public/assets/architecture-YZFGNWBL-Cypt5qHL.js +1 -0
  267. package/payload/server/public/assets/{architectureDiagram-Q4EWVU46-DkVrGn7G.js → architectureDiagram-Q4EWVU46-CmOpXLsH.js} +1 -1
  268. package/payload/server/public/assets/{blockDiagram-DXYQGD6D-DqavswGH.js → blockDiagram-DXYQGD6D-CmS8f9YF.js} +1 -1
  269. package/payload/server/public/assets/browser-D4_XLSSm.js +1 -0
  270. package/payload/server/public/assets/{c4Diagram-AHTNJAMY-B7fyKiXe.js → c4Diagram-AHTNJAMY-BXIjMl6A.js} +1 -1
  271. package/payload/server/public/assets/calendar-s5V_gbI7.js +1 -0
  272. package/payload/server/public/assets/channel-DhrFAm7m.js +1 -0
  273. package/payload/server/public/assets/chat-40WyKzGb.js +1 -0
  274. package/payload/server/public/assets/chevron-left-C35qGP5f.js +1 -0
  275. package/payload/server/public/assets/{chunk-2KRD3SAO-DsgMtGkv.js → chunk-2KRD3SAO-D4fktAwW.js} +1 -1
  276. package/payload/server/public/assets/{chunk-336JU56O-CkdISfNt.js → chunk-336JU56O-C2MKnRTj.js} +2 -2
  277. package/payload/server/public/assets/chunk-426QAEUC-BhU7-TyS.js +1 -0
  278. package/payload/server/public/assets/{chunk-4BX2VUAB-CYpiF2bl.js → chunk-4BX2VUAB-C13Wz3zS.js} +1 -1
  279. package/payload/server/public/assets/{chunk-4TB4RGXK-CWyc_F7v.js → chunk-4TB4RGXK-CalYahTR.js} +1 -1
  280. package/payload/server/public/assets/{chunk-55IACEB6-BbzKMw2Z.js → chunk-55IACEB6-TmOKdy8a.js} +1 -1
  281. package/payload/server/public/assets/{chunk-5FUZZQ4R-u19Nai9e.js → chunk-5FUZZQ4R-C-i0BVag.js} +1 -1
  282. package/payload/server/public/assets/{chunk-5PVQY5BW-Ba8by6Uy.js → chunk-5PVQY5BW-D6bJMLnf.js} +1 -1
  283. package/payload/server/public/assets/{chunk-67CJDMHE-BwUuxtyb.js → chunk-67CJDMHE-Cif9Uu5C.js} +1 -1
  284. package/payload/server/public/assets/{chunk-7N4EOEYR-BRH9iM9L.js → chunk-7N4EOEYR-WjTHCXnd.js} +1 -1
  285. package/payload/server/public/assets/{chunk-AA7GKIK3-BGoztgLL.js → chunk-AA7GKIK3-CwCfdQmb.js} +1 -1
  286. package/payload/server/public/assets/{chunk-BSJP7CBP-C5GDAZTl.js → chunk-BSJP7CBP-9A67yXwT.js} +1 -1
  287. package/payload/server/public/assets/{chunk-CIAEETIT-ChY32VPS.js → chunk-CIAEETIT-DPMiWSBi.js} +1 -1
  288. package/payload/server/public/assets/{chunk-EDXVE4YY-CPSD3p6p.js → chunk-EDXVE4YY-B9PBdwiT.js} +1 -1
  289. package/payload/server/public/assets/{chunk-ENJZ2VHE-D1oL-iUr.js → chunk-ENJZ2VHE-CM5OqcQ5.js} +1 -1
  290. package/payload/server/public/assets/{chunk-FMBD7UC4-CPvYEr54.js → chunk-FMBD7UC4-Bmt_ZRlV.js} +1 -1
  291. package/payload/server/public/assets/{chunk-FOC6F5B3-9Yyj7Vbz.js → chunk-FOC6F5B3-DfGJGxpX.js} +1 -1
  292. package/payload/server/public/assets/{chunk-ICPOFSXX-BaYgh8FR.js → chunk-ICPOFSXX-D6M89DA0.js} +2 -2
  293. package/payload/server/public/assets/{chunk-K5T4RW27-5ucpeRvm.js → chunk-K5T4RW27-DMpoaqw2.js} +1 -1
  294. package/payload/server/public/assets/{chunk-KGLVRYIC-DW-FHk7T.js → chunk-KGLVRYIC-DYUFgri2.js} +1 -1
  295. package/payload/server/public/assets/{chunk-LIHQZDEY-CGGVfAyG.js → chunk-LIHQZDEY-x3LmeKmx.js} +1 -1
  296. package/payload/server/public/assets/{chunk-ORNJ4GCN-Bv8EUa5d.js → chunk-ORNJ4GCN-BeuNAWkW.js} +1 -1
  297. package/payload/server/public/assets/{chunk-OYMX7WX6-C1mXJnyw.js → chunk-OYMX7WX6-Bv6gisQ6.js} +1 -1
  298. package/payload/server/public/assets/chunk-QZHKN3VN-7_kS3RBy.js +1 -0
  299. package/payload/server/public/assets/{chunk-U2HBQHQK-PwSQz6EP.js → chunk-U2HBQHQK-C2iT9Av2.js} +1 -1
  300. package/payload/server/public/assets/{chunk-X2U36JSP--NPHTBlU.js → chunk-X2U36JSP-Bd6bv87A.js} +1 -1
  301. package/payload/server/public/assets/{chunk-XPW4576I-Bbc7Da32.js → chunk-XPW4576I-ByFnN4JO.js} +1 -1
  302. package/payload/server/public/assets/{chunk-YZCP3GAM-D-9v8e--.js → chunk-YZCP3GAM-Bt8I6ZDb.js} +1 -1
  303. package/payload/server/public/assets/{chunk-ZZ45TVLE-CMPg670Q.js → chunk-ZZ45TVLE-DBMPgZ_A.js} +1 -1
  304. package/payload/server/public/assets/classDiagram-6PBFFD2Q-BrWqm88p.js +1 -0
  305. package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-D0wzG5Fd.js +1 -0
  306. package/payload/server/public/assets/clone-DQA-nNrl.js +1 -0
  307. package/payload/server/public/assets/{cose-bilkent-S5V4N54A-NVSb1Bhy.js → cose-bilkent-S5V4N54A-DgLEaApT.js} +1 -1
  308. package/payload/server/public/assets/{dagre-KV5264BT-V2Ai1DFl.js → dagre-KV5264BT-kWVPgmmL.js} +1 -1
  309. package/payload/server/public/assets/{dagre-QfLcO-1j.js → dagre-mSazqXgG.js} +1 -1
  310. package/payload/server/public/assets/data-Bq0vU3qJ.js +1 -0
  311. package/payload/server/public/assets/{diagram-5BDNPKRD-Caw21693.js → diagram-5BDNPKRD-BHe_omJD.js} +1 -1
  312. package/payload/server/public/assets/{diagram-G4DWMVQ6-CgwVkT2q.js → diagram-G4DWMVQ6-fUFsmM3x.js} +1 -1
  313. package/payload/server/public/assets/{diagram-MMDJMWI5-DFUrLZhs.js → diagram-MMDJMWI5-DmujPLlM.js} +1 -1
  314. package/payload/server/public/assets/{diagram-TYMM5635-CEEiEuZJ.js → diagram-TYMM5635-B7Aoas89.js} +1 -1
  315. package/payload/server/public/assets/{dist-Cvgxm7qv.js → dist-C9D_xz81.js} +1 -1
  316. package/payload/server/public/assets/{erDiagram-SMLLAGMA-CG10NrT3.js → erDiagram-SMLLAGMA-6hS1rGiO.js} +1 -1
  317. package/payload/server/public/assets/{flatten-DAO0sfCM.js → flatten-BXzaHgtL.js} +1 -1
  318. package/payload/server/public/assets/{flowDiagram-DWJPFMVM-BqkV9Okv.js → flowDiagram-DWJPFMVM-Bqhr_r5b.js} +1 -1
  319. package/payload/server/public/assets/{ganttDiagram-T4ZO3ILL-DRiCDQF6.js → ganttDiagram-T4ZO3ILL-B8kjyN4o.js} +1 -1
  320. package/payload/server/public/assets/gitGraph-7Q5UKJZL-C_-XjxLd.js +1 -0
  321. package/payload/server/public/assets/{gitGraphDiagram-UUTBAWPF-BZV2voyy.js → gitGraphDiagram-UUTBAWPF--TTRq4ir.js} +1 -1
  322. package/payload/server/public/assets/{graph-BZiudUGr.js → graph-C252ZHjp.js} +3 -3
  323. package/payload/server/public/assets/graph-labels-DzPbbhLc.js +1 -0
  324. package/payload/server/public/assets/{graphlib-DGPThX8M.js → graphlib-CWkjawc5.js} +1 -1
  325. package/payload/server/public/assets/info-OMHHGYJF-BH8yDCfL.js +1 -0
  326. package/payload/server/public/assets/infoDiagram-42DDH7IO-D8U4M0pK.js +2 -0
  327. package/payload/server/public/assets/{isEmpty-Cb_U81C6.js → isEmpty-uB1dEu_Z.js} +1 -1
  328. package/payload/server/public/assets/{ishikawaDiagram-UXIWVN3A-BKsAxSvp.js → ishikawaDiagram-UXIWVN3A-CR8B7VzM.js} +1 -1
  329. package/payload/server/public/assets/{journeyDiagram-VCZTEJTY-CmliuOd-.js → journeyDiagram-VCZTEJTY-45lWAvtq.js} +1 -1
  330. package/payload/server/public/assets/{kanban-definition-6JOO6SKY-DAlElMb-.js → kanban-definition-6JOO6SKY-DkrPXLVb.js} +1 -1
  331. package/payload/server/public/assets/{line-DKZqLKAC.js → line-Db0H446a.js} +1 -1
  332. package/payload/server/public/assets/{linear-CbSGdh1x.js → linear-X3O7lwK3.js} +1 -1
  333. package/payload/server/public/assets/maximize-2-CEbXFpQT.js +1 -0
  334. package/payload/server/public/assets/{mermaid-parser.core-DFvbao9C.js → mermaid-parser.core-CixBHOya.js} +2 -2
  335. package/payload/server/public/assets/{mermaid.core-C0vEBxDm.js → mermaid.core-BGQq98L7.js} +3 -3
  336. package/payload/server/public/assets/{mindmap-definition-QFDTVHPH-CShASUKd.js → mindmap-definition-QFDTVHPH-v9cJl1mj.js} +1 -1
  337. package/payload/server/public/assets/operator-zKk4HMdX.js +1 -0
  338. package/payload/server/public/assets/{ordinal-H0tVmBDP.js → ordinal-CZY9laxg.js} +1 -1
  339. package/payload/server/public/assets/packet-4T2RLAQJ-Bajcns4r.js +1 -0
  340. package/payload/server/public/assets/page-BrmN_vzV.js +1 -0
  341. package/payload/server/public/assets/page-Cts-ESBe.js +32 -0
  342. package/payload/server/public/assets/{pdf-render-jAQU0nXt.js → pdf-render-7HZBTZ0_.js} +1 -1
  343. package/payload/server/public/assets/pie-ZZUOXDRM-D7hjM5w1.js +1 -0
  344. package/payload/server/public/assets/{pieDiagram-DEJITSTG-CBZHpDAJ.js → pieDiagram-DEJITSTG-DQ0b6edX.js} +1 -1
  345. package/payload/server/public/assets/public-BItzHBc0.js +1 -0
  346. package/payload/server/public/assets/{quadrantDiagram-34T5L4WZ-n3bB3K91.js → quadrantDiagram-34T5L4WZ-B64mj7Ei.js} +1 -1
  347. package/payload/server/public/assets/radar-PYXPWWZC-B46d5x7a.js +1 -0
  348. package/payload/server/public/assets/{reduce-DAvwat-a.js → reduce-BGQzAenn.js} +1 -1
  349. package/payload/server/public/assets/{requirementDiagram-MS252O5E-DiDvQK3c.js → requirementDiagram-MS252O5E-CE2JIEWo.js} +1 -1
  350. package/payload/server/public/assets/rotate-ccw-B0EYGhGx.js +1 -0
  351. package/payload/server/public/assets/{sankeyDiagram-XADWPNL6-DWDQQZmd.js → sankeyDiagram-XADWPNL6-Cmt5cfCz.js} +1 -1
  352. package/payload/server/public/assets/{sequenceDiagram-FGHM5R23-DmAqRlBT.js → sequenceDiagram-FGHM5R23-B5vVn_sw.js} +1 -1
  353. package/payload/server/public/assets/{src-DV4LKv2E.js → src-BTKk8KAG.js} +1 -1
  354. package/payload/server/public/assets/{stateDiagram-FHFEXIEX-D4R-n6bZ.js → stateDiagram-FHFEXIEX-DiUEJ-RZ.js} +1 -1
  355. package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-DIm3XuAD.js +1 -0
  356. package/payload/server/public/assets/tasks-ClE8-be8.js +1 -0
  357. package/payload/server/public/assets/time-entry-format-BwjGLnXu.js +1 -0
  358. package/payload/server/public/assets/{timeline-definition-GMOUNBTQ-CjiHGP4l.js → timeline-definition-GMOUNBTQ-C9jprDVw.js} +1 -1
  359. package/payload/server/public/assets/treeView-SZITEDCU-3XQh5xQ1.js +1 -0
  360. package/payload/server/public/assets/treemap-W4RFUUIX-DwR-_l5z.js +1 -0
  361. package/payload/server/public/assets/triangle-alert-DejWYn1x.js +1 -0
  362. package/payload/server/public/assets/useCopyFeedback-CpSgOavK.js +1 -0
  363. package/payload/server/public/assets/useSelectionMode-ZQZQUgze.js +1 -0
  364. package/payload/server/public/assets/useSubAccountSwitcher-DuRLvCfO.js +1 -0
  365. package/payload/server/public/assets/useVoiceRecorder-BTYYeUJW.js +2 -0
  366. package/payload/server/public/assets/{vennDiagram-DHZGUBPP-CBdDQ7c-.js → vennDiagram-DHZGUBPP-DWqQJ9X0.js} +1 -1
  367. package/payload/server/public/assets/wardley-RL74JXVD-DZp9oCGv.js +1 -0
  368. package/payload/server/public/assets/{wardleyDiagram-NUSXRM2D-BlIQPKLb.js → wardleyDiagram-NUSXRM2D-Dg6c2Voh.js} +1 -1
  369. package/payload/server/public/assets/{xychartDiagram-5P7HB3ND-CslKgh9B.js → xychartDiagram-5P7HB3ND-EhN-zeDj.js} +1 -1
  370. package/payload/server/public/browser.html +7 -6
  371. package/payload/server/public/calendar.html +8 -7
  372. package/payload/server/public/chat.html +15 -14
  373. package/payload/server/public/data.html +14 -12
  374. package/payload/server/public/graph.html +13 -12
  375. package/payload/server/public/index.html +17 -15
  376. package/payload/server/public/operator.html +17 -16
  377. package/payload/server/public/public.html +15 -14
  378. package/payload/server/public/tasks.html +7 -6
  379. package/payload/server/server.js +1064 -536
  380. package/payload/server/public/assets/AdminLoginScreens-Bed8SDxY.js +0 -1
  381. package/payload/server/public/assets/AdminShell-BLh9O6dn.js +0 -2
  382. package/payload/server/public/assets/Checkbox-BC_KSbpS.js +0 -1
  383. package/payload/server/public/assets/admin-DfWgJVI3.js +0 -1
  384. package/payload/server/public/assets/architecture-YZFGNWBL-FPHT7s-C.js +0 -1
  385. package/payload/server/public/assets/browser-C0vkv-hH.js +0 -1
  386. package/payload/server/public/assets/calendar-_pfHw0Hp.js +0 -1
  387. package/payload/server/public/assets/channel-j70eMvcL.js +0 -1
  388. package/payload/server/public/assets/chat-DYT5H81o.js +0 -1
  389. package/payload/server/public/assets/chevron-left-BnpVBu51.js +0 -1
  390. package/payload/server/public/assets/chunk-426QAEUC-D-NcAlTW.js +0 -1
  391. package/payload/server/public/assets/chunk-QZHKN3VN-CAwbQBtf.js +0 -1
  392. package/payload/server/public/assets/classDiagram-6PBFFD2Q-Ct6Rn0vt.js +0 -1
  393. package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-B_2XCIFv.js +0 -1
  394. package/payload/server/public/assets/clone-DjPWA_NH.js +0 -1
  395. package/payload/server/public/assets/data-CR-fbPWr.js +0 -1
  396. package/payload/server/public/assets/gitGraph-7Q5UKJZL-B8Hx7f-D.js +0 -1
  397. package/payload/server/public/assets/graph-labels-BzmmJwCR.js +0 -1
  398. package/payload/server/public/assets/info-OMHHGYJF-BicCJTqU.js +0 -1
  399. package/payload/server/public/assets/infoDiagram-42DDH7IO-tf-8rOez.js +0 -2
  400. package/payload/server/public/assets/maximize-2-Bhdsv0U-.js +0 -1
  401. package/payload/server/public/assets/operator-gm2eP4r6.js +0 -1
  402. package/payload/server/public/assets/packet-4T2RLAQJ-Ckn_5hq0.js +0 -1
  403. package/payload/server/public/assets/page-8AB3jUa_.js +0 -1
  404. package/payload/server/public/assets/page-BoPorLg-.js +0 -32
  405. package/payload/server/public/assets/pie-ZZUOXDRM-DzlGQW3J.js +0 -1
  406. package/payload/server/public/assets/public-DqJk6qgW.js +0 -1
  407. package/payload/server/public/assets/radar-PYXPWWZC-BHo2YD8b.js +0 -1
  408. package/payload/server/public/assets/rotate-ccw-D_5cETGt.js +0 -1
  409. package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-BcFRbGRu.js +0 -1
  410. package/payload/server/public/assets/tasks-EyKLXINi.js +0 -1
  411. package/payload/server/public/assets/time-entry-format-DAwmmR3i.js +0 -1
  412. package/payload/server/public/assets/treeView-SZITEDCU-BtRr5vXp.js +0 -1
  413. package/payload/server/public/assets/treemap-W4RFUUIX-D8p0sbyo.js +0 -1
  414. package/payload/server/public/assets/useCopyFeedback-rOWd_lzl.js +0 -1
  415. package/payload/server/public/assets/useSelectionMode-DDmLmPph.js +0 -1
  416. package/payload/server/public/assets/useSubAccountSwitcher-CU-lOxTd.js +0 -9
  417. package/payload/server/public/assets/useVoiceRecorder-azG8zSUI.js +0 -2
  418. package/payload/server/public/assets/wardley-RL74JXVD-DgMn-9ZJ.js +0 -1
  419. /package/payload/platform/plugins/cloudflare/skills/data-portal/{template/schema.sql → schema.sql} +0 -0
  420. /package/payload/platform/plugins/cloudflare/skills/data-portal/template/{public/index.html → index.html} +0 -0
  421. /package/payload/platform/plugins/cloudflare/skills/data-portal/template/{public/portal.js → portal.js} +0 -0
  422. /package/payload/server/public/assets/{_baseFor-D3BqAOBr.js → _baseFor-CMWvz0uA.js} +0 -0
  423. /package/payload/server/public/assets/{array-iYP9fzJp.js → array-Cny_NT6T.js} +0 -0
  424. /package/payload/server/public/assets/{chunk-Ck5AOu5v.js → chunk-TPjIAO9U.js} +0 -0
  425. /package/payload/server/public/assets/{cytoscape.esm-BQSBzPsB.js → cytoscape.esm-De0CCmB2.js} +0 -0
  426. /package/payload/server/public/assets/{defaultLocale-BrdA1gDi.js → defaultLocale-BahRht8U.js} +0 -0
  427. /package/payload/server/public/assets/{init-Bx0S6pkp.js → init-DoIvjoJ_.js} +0 -0
  428. /package/payload/server/public/assets/{katex-BsiH_a6d.js → katex-Bfn1OZEl.js} +0 -0
  429. /package/payload/server/public/assets/{path-BLSgTy3w.js → path-CTezU8L5.js} +0 -0
  430. /package/payload/server/public/assets/{preload-helper-ClBMtM1g.js → preload-helper-CT1Z6Pdu.js} +0 -0
  431. /package/payload/server/public/assets/{rough.esm-BMOtDaaS.js → rough.esm-BJo4pvXu.js} +0 -0
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: platform-architecture
3
3
  description: Use when grounding any documented-surface claim about what Maxy ships — plugins, skills, specialists, install/deploy flows, internals. This is the install catalogue, not evidence of what is enabled on the current account. For install state on this account, call `capabilities-here`; for documented surface, cite the `Source:` URL inline.
4
- content-hash: sha256:c2a25fe4d72c9db00a75f700eb4642cb78ac01835892daf406e7bf0f438a9bb8
4
+ content-hash: sha256:46e07a18921b711849e53f63b8c29b72f3324f91bc682eb5be585ccd64c98971
5
5
  brand: maxy-code
6
6
  product-name: Maxy
7
7
  ---
@@ -606,6 +606,54 @@ Pausing keeps a workflow but stops it running. Editing replaces the steps or the
606
606
 
607
607
  A workflow can run on a schedule instead of on demand. Tell Maxy when you want it to run, for example "run the daily summary workflow every morning at eight," and it sets up the schedule. Scheduled runs happen with no one present, so a workflow that needs you to supply inputs is best run on demand rather than scheduled.
608
608
 
609
+ ---
610
+ # Cash Book + AR/AP Ledger
611
+ Source: https://docs.getmaxy.com/ledger.md
612
+
613
+ # Ledger
614
+
615
+ A cash book with receivables and payables. It records what you invoiced, what you were billed, what was paid, and money that moved without an invoice, then tells you what is still outstanding.
616
+
617
+ ## What it is not
618
+
619
+ It is not double-entry bookkeeping. There is no chart of accounts, no journals, no trial balance. It computes no VAT and handles a single currency. It does not read bank feeds and it does not talk to QuickBooks.
620
+
621
+ If you need statutory accounts, this is not that. It is the answer to "who owes me what, what do I owe, and where did the money go".
622
+
623
+ ## The seven things you can ask for
624
+
625
+ **Record an invoice you raised.** A reference, who owes you, the amount, and when it is due.
626
+
627
+ **Record a bill you received.** The same, in the other direction.
628
+
629
+ **Record a payment.** Against either an invoice or a bill. You give the reference, the amount, the date and how it was paid.
630
+
631
+ **Record a credit.** Against an invoice you raised. It reduces what is owed exactly as a payment does.
632
+
633
+ **Record a cash movement.** Money in or out that belongs to no invoice: bank charges, a cash sale, drawings, a refund.
634
+
635
+ **Ask what is outstanding on one document.** You get the total, what has been paid, what has been credited, and what remains.
636
+
637
+ **Ask for a statement.** Everything owed to you and everything you owe, sorted into 0-30, 31-60, 61-90 and 90+ days overdue, plus net cash.
638
+
639
+ ## Three behaviours worth knowing about
640
+
641
+ **What is outstanding is never stored.** It is worked out from the payments each time you ask. This means the figure cannot fall out of step with the payments behind it. If it were stored, a payment recorded by any route that forgot to update it would leave the invoice quietly reporting a wrong number, and nothing would fail.
642
+
643
+ **Paying more than is owed is refused.** If an invoice has 4,400 left and you try to record 5,000, nothing is written and you are told the figure that would settle it. It is not rounded down to 4,400 and it is not recorded anyway. A ledger that quietly accepts an impossible payment is worse than one that says no.
644
+
645
+ **Every payment is read back.** After a payment is written, the invoice is re-read from the graph and the outstanding figure is checked against what was expected. If they disagree you are told to stop recording against that invoice until it is sorted out. Writing something is not the same as it having landed.
646
+
647
+ ## The health check
648
+
649
+ Some ways this can go wrong announce themselves. Others do not: a payment that ends up attached to no invoice, a stale balance field reappearing, or the same payment recorded twice under two different ids. None of those produce an error at the time, because nothing goes wrong at the moment they happen.
650
+
651
+ So there is a check that runs by itself every fifteen minutes and writes one line to the log beginning `[ledger-census]`. When it is healthy the counters are all zero. When something is wrong the line ends with `alarm=true` and says which of the four it found.
652
+
653
+ You can also ask for that check on demand at any time, and it reports the same thing in plain words.
654
+
655
+ If you never see the line at all, that is itself the problem: it means the periodic check is not running.
656
+
609
657
  ---
610
658
  # Install Overview
611
659
  Source: https://docs.getmaxy.com/install.md
@@ -2152,6 +2200,79 @@ Latency triage: `mail-list count=0 elapsedMs<200` consistent → permissions iss
2152
2200
 
2153
2201
  Mail move to arbitrary folders / flag, hard (permanent) delete, removing an attachment from a **sent** message (Graph forbids it), ingesting reviewed mail into the business graph (the `email-ingest` analogue), contacts write, recurring-series occurrence editing, the `scheduling` Neo4j calendar and any auto-sync with it, OneDrive / Files, push notifications, on-premises Exchange, M365 admin scopes (`User.Read.All`, `AuditLog.Read.All`), public-agent exposure, multi-tenant federation. Mail read, full-body read, inbound attachment list/download, reply, delete (to Deleted Items), and compose — all four outbound paths carrying attachments, with a draft's attachments editable in place (add, remove, replace) — are supported (`outlook-mail-fetch-body`, `outlook-mail-attachment`, `outlook-mail-reply`, `outlook-mail-delete`, `outlook-mail-otp-extract`, `outlook-mail-send`, `outlook-draft`, `outlook-draft-edit`, `outlook-draft-send`), and the calendar is read plus control. See `platform/plugins/outlook/PLUGIN.md` for the full out-of-scope list.
2154
2202
 
2203
+ ---
2204
+ # Google Calendar
2205
+ Source: https://docs.getmaxy.com/google-guide.md
2206
+
2207
+ # Google Calendar
2208
+
2209
+ Live Google Calendar for one account: read the user's real events, and create,
2210
+ update, cancel, respond to and check availability against them. Eleven tools,
2211
+ admin agent only.
2212
+
2213
+ There is deliberately no Gmail surface here. Mail runs on the `email` plugin's
2214
+ IMAP/SMTP path, and Gmail's read/modify scopes are *restricted*, which would
2215
+ force Google's annual third-party security assessment. Calendar is only a
2216
+ *sensitive* scope, so it needs the lighter brand verification.
2217
+
2218
+ ## Connecting an account
2219
+
2220
+ `google-account-register` returns a consent URL. Relay it to the user: they open
2221
+ it on any device, sign in, and approve. Google redirects to this install's
2222
+ public host at `/api/google/oauth/callback`, which exchanges the code and stores
2223
+ encrypted tokens. `google-account-register-poll` then reports the outcome.
2224
+
2225
+ The register tool does not block and does not wait. A second register while one
2226
+ is already live is refused rather than overwriting it; that refusal names how
2227
+ long the live one has been pending.
2228
+
2229
+ One account can connect several Google accounts. Every operational tool takes an
2230
+ optional `account` (the email address). Omit it when there is one; with two or
2231
+ more, omitting it returns a refusal listing them.
2232
+
2233
+ ## Reading and controlling
2234
+
2235
+ - `google-calendar-list` — events in the next `rangeDays` days (default 7).
2236
+ Recurring series are expanded into instances, so this returns what the user
2237
+ actually sees in their calendar.
2238
+ - `google-calendar-event` — one event by id.
2239
+ - `google-calendar-create` — times are RFC3339 (`2026-07-20T09:00:00Z`) unless
2240
+ `allDay` is true, in which case they are plain dates (`2026-07-20`).
2241
+ - `google-calendar-update` — changes only the fields you name. Omitted fields
2242
+ are left alone, so an update never blanks something by accident.
2243
+ - `google-calendar-cancel` — deletes the event and notifies attendees.
2244
+ - `google-calendar-respond` — accept, decline or tentatively accept an
2245
+ invitation. Every other attendee's response is preserved.
2246
+ - `google-calendar-freebusy` — busy blocks between two times. Free time is the
2247
+ complement, which you work out.
2248
+
2249
+ ## Checking health
2250
+
2251
+ `google-account-list` answers "is anything connected?" from local stores with no
2252
+ network call. Read each entry's `status` — a count alone is not "connected", and
2253
+ an unreadable store still counts. `google-account-info` gives one account's auth
2254
+ state, including whether its access token is near expiry and which scopes were
2255
+ granted.
2256
+
2257
+ ## When something is wrong
2258
+
2259
+ **"GOOGLE_CLIENT_ID is not configured"** — this brand has no Google vendor app
2260
+ yet. Nothing the operator does on the device fixes it; the brand needs its own
2261
+ Google Cloud OAuth client. See `platform/plugins/google/references/auth.md`.
2262
+
2263
+ **A 403 naming insufficient scope** — the connected account approved something
2264
+ narrower than the full calendar scope. Re-run `google-account-register` so they
2265
+ re-consent.
2266
+
2267
+ **The user re-consents every week** — the brand's Google app is still in
2268
+ Testing, where Google caps refresh tokens at seven days and limits the app to
2269
+ 100 named test users. Completing brand verification removes both. This is a
2270
+ property of the vendor app, not a fault in the connection.
2271
+
2272
+ **A registration never completes** — the consent URL was opened but not
2273
+ approved, or the callback could not be reached. `google-account-register-poll`
2274
+ reports `expired` once the pending entry outlives Google's code lifetime.
2275
+
2155
2276
  ---
2156
2277
  # QuickBooks Online
2157
2278
  Source: https://docs.getmaxy.com/quickbooks.md
@@ -3065,6 +3186,54 @@ A 0.000 reading that persists across many polls while `top` shows load
3065
3186
  is the delta-cache regression signature (the single-flight promise was
3066
3187
  not released).
3067
3188
 
3189
+ ## Activity view
3190
+
3191
+ Source:
3192
+ [`server/routes/admin/activity.ts`](../../../ui/server/routes/admin/activity.ts)
3193
+ (house-gated proxy) and
3194
+ [`platform/ui/app/activity/page.tsx`](../../../ui/app/activity/page.tsx)
3195
+ (page). CSS lives under `.activity*` in
3196
+ [`platform/ui/app/globals.css`](../../../ui/app/globals.css).
3197
+
3198
+ **What it shows.** `/activity` lists every live session across every account
3199
+ in the install, one row per session: account, title, model, status, age, idle,
3200
+ turn count, RSS, CPU %, cumulative tokens, and imputed GBP cost. Columns sort
3201
+ on click. The sidebar session list is scoped to the caller's own account, so
3202
+ this is the only surface that answers "where is the activity" install-wide.
3203
+
3204
+ **House account only.** The manager binds loopback and gates nothing, so the
3205
+ scoping lives on the admin route. The house is resolved by the same three-arm
3206
+ predicate the spawn path uses (`resolveAccountDir`): exactly one `role:"house"`
3207
+ account, else a sole account declaring no role, else deny — two houses is
3208
+ corruption and denies rather than picking one. Both operands are null-checked,
3209
+ because `caller === house` alone is true for two nulls. A caller pinned
3210
+ elsewhere receives 403 and a logged `op=gate decision=deny` carrying its
3211
+ reason. The `HeaderMenu` item is admin-variant only for the same reason.
3212
+
3213
+ Be precise about what this is. Under the install-wide access model every
3214
+ authenticated install admin can re-pin to any account via
3215
+ `POST /api/admin/session/switch-account`, so the gate is a scope selector, not
3216
+ an authority boundary between privilege levels.
3217
+
3218
+ **Where the numbers come from.** The manager assembles them at its own
3219
+ `GET /activity`, joining the live PTY set, the watcher's per-row `accountId`,
3220
+ the JSONL enumerator (turns, model, tokens, last message), the PTY census
3221
+ (RSS and CPU), and a bounded metering fan-out. The admin route adds the one
3222
+ fact the manager cannot see: the `data/accounts` directory count, taken
3223
+ independently of `listValidAccounts()` so a rejected account shows as
3224
+ `onDisk > valid` rather than silently shortening the list.
3225
+
3226
+ **Reading the cells.** A dash means unmeasured, never zero — an unmeasured CPU
3227
+ is not an idle session. Cost is the price arm matching the row's own model,
3228
+ because the metering core prices one token count under both Opus and Sonnet
3229
+ assumptions without knowing which ran; a model in neither family shows a dash.
3230
+ Every dashed cost carries its reason as a tooltip, so an unpriced model, a
3231
+ failed parse and a session with no transcript are distinguishable rather than
3232
+ three readings of the same glyph.
3233
+ A session whose sidecar carries no account shows `untagged` rather than being
3234
+ folded into the house. The census age is rendered because the snapshot is a 60s
3235
+ sample and a dead collector emits nothing.
3236
+
3068
3237
  ## Sidebar surfaces
3069
3238
 
3070
3239
  The sidebar is the entire left column of the admin UI. Its full layout,
@@ -3900,7 +4069,7 @@ This tool is read-only and available to both public and admin agents.
3900
4069
 
3901
4070
  ### When conversations are created
3902
4071
 
3903
- `:Conversation` nodes on webchat (admin login, "New conversation" in the burger, a new public visitor) are created lazily. Opening the chat or logging in does not write anything to the graph — Maxy only records the conversation once the user sends a second message. This keeps `conversation-search` and the Conversations modal free of one-turn abandoned threads. WhatsApp and Telegram take the opposite posture: every inbound — DM or group, allowed or activation-off, agent-invoked or gated — MERGEs the `:Conversation` and writes a forensic `:Message:WhatsAppMessage` row before any access-control decision. The graph is the durable record of every message the device received, not just the ones the agent replied to. See `.docs/web-chat.md` "Deferred conversation persistence" and `.docs/whatsapp.md` "Session continuity" for the full contract.
4072
+ `:Conversation` nodes on webchat (admin login, "New conversation" in the burger, a new public visitor) are created lazily. Opening the chat or logging in does not write anything to the graph — Maxy only records the conversation once the user sends a second message. This keeps `conversation-search` and the Conversations modal free of one-turn abandoned threads. WhatsApp and Telegram take the opposite posture: every inbound — DM or group, allowed or activation-off, agent-invoked or gated — MERGEs the `:Conversation` and writes a forensic `:Message:WhatsAppMessage` row before any access-control decision. The graph is the durable record of every message the device received, not just the ones the agent replied to. That forensic row is addressed by cacheKey (`default:<phone>`) on the socket-owning account and deliberately has no session behind it. A WhatsApp sender therefore carries a **second** `:Conversation`: the anchored row written at spawn on the account the session actually runs under, keyed by the session's own deterministic id, which is the row every `MATCH (c:Conversation {sessionId: $SESSION_ID})` resolves. Two rows, two jobs, not duplicates. See `.docs/web-chat.md` "Deferred conversation persistence", `.docs/whatsapp.md` "Session continuity", and `.docs/session-conversation-identity.md` for the per-channel identity contract.
3904
4073
 
3905
4074
  Each row in the Conversations modal exposes a `View logs` row-action that opens a popover with three links — **Stream**, **Errors**, **SSE** — each of which targets `/api/admin/logs?type={stream|error|sse}&sessionId={full-id}` in a new tab. The row's 8-char id chip is click-to-copy; hover reveals the full `sessionId` as a tooltip. See `.docs/web-chat.md` "In-chat retrieval" for the route contract and `console.debug` observability.
3906
4075
 
@@ -0,0 +1,139 @@
1
+ #!/usr/bin/env bash
2
+ # Regression test for portal-brand-css.mjs - the assemble-time :root rewriter
3
+ # that bakes a brand's consent.palette into an assembled data portal's
4
+ # stylesheet. Pure file transformation: no network, no Cloudflare credential,
5
+ # no wrangler.
6
+ #
7
+ # Covers:
8
+ # 1. a full palette sets all five mapped variables, exit 0
9
+ # 2. everything outside the :root block is byte-identical to the template
10
+ # 3. a brand with no consent.palette leaves the file byte-identical, exit 0
11
+ # 4. a partial palette sets only the fields it carries
12
+ # 5. --dp-muted is never brand-written, even if the palette carries a muted
13
+ # 6. a value failing the CSS colour rule fails closed: exit 1, file unchanged
14
+ # 7. running twice is idempotent
15
+ # 8. a stylesheet with no :root block fails closed: exit 1, file unchanged
16
+ # 9. a missing --css argument fails closed (exit 1)
17
+ set -u
18
+
19
+ BIN_DIR="$(cd "$(dirname "$0")/.." && pwd)"
20
+ SCRIPT="$BIN_DIR/portal-brand-css.mjs"
21
+ TEMPLATE="$BIN_DIR/../skills/data-portal/template/public/portal.css"
22
+ [ -f "$SCRIPT" ] || { echo "FAIL: $SCRIPT not found" >&2; exit 1; }
23
+ [ -f "$TEMPLATE" ] || { echo "FAIL: $TEMPLATE not found" >&2; exit 1; }
24
+ # Case 2 strips the :root block with perl and diffs what is left. Without perl
25
+ # both sides strip to nothing and the diff succeeds, so the one check that
26
+ # guards byte-identity would report PASS while asserting nothing. Refuse instead.
27
+ command -v perl >/dev/null || { echo "FAIL: perl not found; case 2 would pass vacuously" >&2; exit 1; }
28
+
29
+ PASS=0; FAIL=0
30
+ ok() { echo "PASS: $1"; PASS=$((PASS+1)); }
31
+ bad() { echo "FAIL: $1" >&2; FAIL=$((FAIL+1)); }
32
+
33
+ WORK=$(mktemp -d)
34
+ trap 'rm -rf "$WORK"' EXIT
35
+
36
+ # Write a brand.json carrying the given consent.palette object literal, or no
37
+ # palette at all when passed NONE. Echoes the path.
38
+ brand() {
39
+ local path="$WORK/$1"
40
+ if [ "$2" = "NONE" ]; then
41
+ printf '{"name":"t","consent":{}}\n' > "$path"
42
+ else
43
+ printf '{"name":"t","consent":{"palette":%s}}\n' "$2" > "$path"
44
+ fi
45
+ echo "$path"
46
+ }
47
+
48
+ # Fresh copy of the real template stylesheet. Using the real file rather than a
49
+ # fixture is deliberate: a template edit that breaks the rewriter fails here.
50
+ css() { local path="$WORK/$1"; cp "$TEMPLATE" "$path"; echo "$path"; }
51
+
52
+ run() {
53
+ local of ef
54
+ of=$(mktemp); ef=$(mktemp)
55
+ node "$SCRIPT" "$@" >"$of" 2>"$ef"
56
+ RC=$?
57
+ OUT=$(cat "$of"); ERR=$(cat "$ef"); rm -f "$of" "$ef"
58
+ }
59
+
60
+ # The file with its :root block removed, for byte-identity comparison.
61
+ sans_root() { perl -0777 -pe 's/:root\s*\{[^}]*\}//' "$1"; }
62
+
63
+ FULL='{"bg":"#FAFAF8","fg":"#1a1a1a","accent":"#ea7232","accentFg":"#FFFFFF","border":"#E0E0DB"}'
64
+
65
+ # 1. full palette sets all five
66
+ B=$(brand full.json "$FULL"); C=$(css full.css)
67
+ run --css "$C" --brand "$B"
68
+ { [ "$RC" -eq 0 ] \
69
+ && grep -q -- '--dp-accent: #ea7232;' "$C" \
70
+ && grep -q -- '--dp-accent-ink: #FFFFFF;' "$C" \
71
+ && grep -q -- '--dp-paper: #FAFAF8;' "$C" \
72
+ && grep -q -- '--dp-ink: #1a1a1a;' "$C" \
73
+ && grep -q -- '--dp-rule: #E0E0DB;' "$C"; } \
74
+ && ok "full palette sets all five mapped variables (exit=$RC)" \
75
+ || bad "full palette (exit=$RC err='$ERR')"
76
+
77
+ # 2. everything outside :root is byte-identical to the template
78
+ { [ -n "$(sans_root "$TEMPLATE")" ] && diff <(sans_root "$TEMPLATE") <(sans_root "$C") >/dev/null; } \
79
+ && ok "substitution touched only the :root block" \
80
+ || bad "bytes outside :root changed, or the strip produced nothing to compare"
81
+
82
+ # 3. no consent.palette -> file byte-identical, exit 0
83
+ B=$(brand none.json NONE); C=$(css none.css)
84
+ run --css "$C" --brand "$B"
85
+ { [ "$RC" -eq 0 ] && diff "$TEMPLATE" "$C" >/dev/null; } \
86
+ && ok "brand with no consent.palette leaves the file untouched (exit=$RC)" \
87
+ || bad "no-palette case (exit=$RC err='$ERR')"
88
+
89
+ # 4. partial palette sets only what it carries
90
+ B=$(brand part.json '{"accent":"#CCFF00"}'); C=$(css part.css)
91
+ run --css "$C" --brand "$B"
92
+ { [ "$RC" -eq 0 ] \
93
+ && grep -q -- '--dp-accent: #CCFF00;' "$C" \
94
+ && grep -q -- '--dp-accent-ink: #ffffff;' "$C" \
95
+ && grep -q -- '--dp-ink: #14181f;' "$C"; } \
96
+ && ok "partial palette leaves unmapped fields at template defaults (exit=$RC)" \
97
+ || bad "partial palette (exit=$RC err='$ERR')"
98
+
99
+ # 5. --dp-muted is never brand-written
100
+ B=$(brand muted.json '{"muted":"#123456","accent":"#ea7232"}'); C=$(css muted.css)
101
+ run --css "$C" --brand "$B"
102
+ { [ "$RC" -eq 0 ] && grep -q -- '--dp-muted: #667085;' "$C" && ! grep -q '#123456' "$C"; } \
103
+ && ok "--dp-muted keeps the template default (exit=$RC)" \
104
+ || bad "--dp-muted (exit=$RC err='$ERR')"
105
+
106
+ # 6. an unsafe value fails closed and writes nothing
107
+ B=$(brand bad.json '{"accent":"red;background:url(//x)"}'); C=$(css bad.css)
108
+ BEFORE=$(cat "$C")
109
+ run --css "$C" --brand "$B"
110
+ { [ "$RC" -ne 0 ] && [ "$(cat "$C")" = "$BEFORE" ] && [ -n "$ERR" ]; } \
111
+ && ok "a value failing the CSS colour rule exits $RC and writes nothing" \
112
+ || bad "unsafe value not refused (exit=$RC)"
113
+
114
+ # 7. idempotent
115
+ B=$(brand idem.json "$FULL"); C=$(css idem.css)
116
+ run --css "$C" --brand "$B"; ONCE=$(cat "$C")
117
+ run --css "$C" --brand "$B"; TWICE=$(cat "$C")
118
+ { [ "$RC" -eq 0 ] && [ "$ONCE" = "$TWICE" ]; } \
119
+ && ok "second run is a no-op (exit=$RC)" \
120
+ || bad "not idempotent (exit=$RC)"
121
+
122
+ # 8. no :root block fails closed
123
+ B=$(brand root.json "$FULL"); C="$WORK/noroot.css"
124
+ printf 'body { color: red; }\n' > "$C"; BEFORE=$(cat "$C")
125
+ run --css "$C" --brand "$B"
126
+ { [ "$RC" -ne 0 ] && [ "$(cat "$C")" = "$BEFORE" ]; } \
127
+ && ok "a stylesheet with no :root block is refused (exit=$RC)" \
128
+ || bad "missing :root not refused (exit=$RC)"
129
+
130
+ # 9. missing --css fails closed
131
+ B=$(brand args.json "$FULL")
132
+ run --brand "$B"
133
+ { [ "$RC" -ne 0 ] && [ -n "$ERR" ]; } \
134
+ && ok "missing --css is refused (exit=$RC)" \
135
+ || bad "missing --css not refused (exit=$RC)"
136
+
137
+ echo
138
+ echo "passed: $PASS failed: $FAIL"
139
+ [ "$FAIL" -eq 0 ]
@@ -0,0 +1,128 @@
1
+ #!/usr/bin/env node
2
+ // Bake a brand's client-facing palette into an assembled data portal's
3
+ // stylesheet, by rewriting the one `:root` block in place.
4
+ //
5
+ // This is a script and not a sentence in SKILL.md for the same reason
6
+ // portal-enrol.mjs is one: "replace the :root block and leave the rest alone"
7
+ // is not a constraint when a model performs it by hand. The portal's done-gate
8
+ // asks for the rest of the stylesheet to be byte-identical to the template, and
9
+ // only a transformation that can be run and diffed makes that checkable.
10
+ //
11
+ // The palette read is `consent.palette`, not `defaultColors`. consent.palette
12
+ // is the brand's existing answer to "what colours does this brand show a
13
+ // customer on a surface that is not the install", which is exactly what the
14
+ // portal is; and its accent/accentFg and bg/fg are designed contrast pairs.
15
+ // Taking the accent from defaultColors.primary while taking the foreground from
16
+ // consent.palette.accentFg mixes two palettes, and on a shipped brand that
17
+ // yields #0F3A61 text on a #0F3A61 button.
18
+ //
19
+ // Usage:
20
+ // node platform/plugins/cloudflare/bin/portal-brand-css.mjs \
21
+ // --css <accountDir>/pages/<project>/public/portal.css
22
+
23
+ import { readFileSync, writeFileSync } from 'node:fs'
24
+ import { fileURLToPath } from 'node:url'
25
+
26
+ /** The install's stamped brand manifest. Resolved from this file's own location
27
+ * rather than cwd, because the skill's other commands run from the install root
28
+ * and this one must not silently depend on that. `--brand` overrides it, which
29
+ * is what lets the regression test run off-device: platform/config/ is written
30
+ * by the bundler and does not exist in the source tree. */
31
+ const DEFAULT_BRAND = fileURLToPath(new URL('../../../config/brand.json', import.meta.url))
32
+
33
+ /** Copied verbatim from packages/create-maxy-code/scripts/bundle.js, which
34
+ * applies it to defaultColors for the same reason: a brand value lands inside a
35
+ * CSS declaration, so anything that is not a colour is an injection. The
36
+ * bundler does NOT validate consent.palette, so this is the only gate on it. */
37
+ const CSS_COLOR_RE = /^(#[0-9a-fA-F]{3,8}|rgba?\([^)]{1,60}\))$/
38
+
39
+ /** The whole mapping. `--dp-muted` is deliberately absent: no brand carries a
40
+ * muted tone, and deriving one from another field would be a colour the brand
41
+ * never chose. It keeps the template default on every brand. */
42
+ const MAP = [
43
+ ['--dp-accent', 'accent'],
44
+ ['--dp-accent-ink', 'accentFg'],
45
+ ['--dp-paper', 'bg'],
46
+ ['--dp-ink', 'fg'],
47
+ ['--dp-rule', 'border'],
48
+ ]
49
+
50
+ /** @param {string} message */
51
+ function die(message) {
52
+ console.error(`portal-brand-css: ${message}`)
53
+ process.exit(1)
54
+ }
55
+
56
+ /** @param {string} flag @returns {string | undefined} */
57
+ function arg(flag) {
58
+ const i = process.argv.indexOf(flag)
59
+ return i === -1 ? undefined : process.argv[i + 1]
60
+ }
61
+
62
+ const cssPath = arg('--css')
63
+ const brandPath = arg('--brand') ?? DEFAULT_BRAND
64
+
65
+ if (!cssPath) die('--css <path to the assembled portal.css> is required')
66
+
67
+ let css
68
+ try {
69
+ css = readFileSync(/** @type {string} */ (cssPath), 'utf8')
70
+ } catch (err) {
71
+ die(`cannot read --css ${cssPath}: ${/** @type {Error} */ (err).message}`)
72
+ }
73
+
74
+ let brand
75
+ try {
76
+ brand = JSON.parse(readFileSync(brandPath, 'utf8'))
77
+ } catch (err) {
78
+ // An unreadable or malformed brand manifest is a broken install, not a brand
79
+ // that declined to declare a palette. Refuse, rather than silently shipping
80
+ // the template's default and calling it the brand's colour.
81
+ die(`cannot read brand manifest ${brandPath}: ${/** @type {Error} */ (err).message}`)
82
+ }
83
+
84
+ const palette = brand?.consent?.palette
85
+ if (!palette || typeof palette !== 'object') {
86
+ // Not a failure. A brand that declares no client-facing palette gets the
87
+ // template defaults, which are legible on their own.
88
+ console.log('portal-brand-css: brand declares no consent.palette; template defaults kept')
89
+ process.exit(0)
90
+ }
91
+
92
+ // Exactly one :root block, or refuse. Two would make "which one wins" a
93
+ // question the stylesheet answers by source order and this script would answer
94
+ // by position; none means the template this tree was assembled from is not the
95
+ // template this script knows.
96
+ const roots = css.match(/:root\s*\{[^}]*\}/g) ?? []
97
+ if (roots.length !== 1) {
98
+ die(`expected exactly one :root block in ${cssPath}, found ${roots.length}`)
99
+ }
100
+ const root = roots[0]
101
+
102
+ let next = root
103
+ const applied = []
104
+ for (const [cssVar, field] of MAP) {
105
+ const value = palette[field]
106
+ if (value === undefined || value === null || value === '') continue
107
+ if (typeof value !== 'string' || !CSS_COLOR_RE.test(value)) {
108
+ die(`consent.palette.${field} is not a valid CSS colour: ${JSON.stringify(value)}`)
109
+ }
110
+ // The declaration must already be in the block. If it is not, the template
111
+ // and this mapping have drifted apart, and appending a declaration the
112
+ // template never had would hide that.
113
+ const declRe = new RegExp(`(${cssVar}\\s*:\\s*)[^;]*(;)`)
114
+ if (!declRe.test(next)) {
115
+ die(`${cssVar} is not declared in the :root block of ${cssPath}`)
116
+ }
117
+ next = next.replace(declRe, `$1${value}$2`)
118
+ applied.push(`${cssVar}=${value}`)
119
+ }
120
+
121
+ // Function replacement, so a `$` sequence in the rewritten block is never read
122
+ // as a replacement pattern.
123
+ if (next !== root) {
124
+ writeFileSync(/** @type {string} */ (cssPath), css.replace(root, () => next), 'utf8')
125
+ }
126
+ console.log(
127
+ `portal-brand-css: ${applied.length ? applied.join(' ') : 'no mapped fields in consent.palette'}`,
128
+ )
@@ -39,7 +39,7 @@ async function d1(dir: string) {
39
39
  const db = resolve(root, 'portal.db')
40
40
  const schema = resolve(
41
41
  dirname(fileURLToPath(import.meta.url)),
42
- '../../skills/data-portal/template/schema.sql',
42
+ '../../skills/data-portal/schema.sql',
43
43
  )
44
44
  // SQL as an argv entry, not stdin: execFile has no `input` option. The schema
45
45
  // goes via `.read` because it opens with a `--` comment, which sqlite3 would
@@ -4,6 +4,31 @@ import { join } from 'node:path'
4
4
 
5
5
  const skill = () => readFileSync(join(__dirname, '../../skills/data-portal/SKILL.md'), 'utf8')
6
6
 
7
+ // resolveSiteDir (platform/ui/server/routes/storage-broker.ts) resolves
8
+ // <accountsDir>/<account>/sites/<site> and refuses anything else with
9
+ // invalid-site. A tree assembled where the skill used to say could not be
10
+ // deployed at all.
11
+ describe('data-portal SKILL.md names paths the broker accepts', () => {
12
+ it('assembles into sites/, not pages/', () => {
13
+ expect(skill()).toMatch(/<accountDir>\/sites\/<project>\//)
14
+ expect(skill()).not.toMatch(/assembled tree.*\bpages\/<project>\//)
15
+ expect(skill()).not.toMatch(/Copy `template\/` to [^\n]*pages\//)
16
+ })
17
+
18
+ it('never tells the assembler to copy a public/ level', () => {
19
+ expect(skill()).not.toMatch(/template\/public/)
20
+ })
21
+
22
+ // site-deploy's canonical-tree section names pages/<project>/ and instructs
23
+ // reconciling stray trees into it. An assembler that follows it after
24
+ // assembling at sites/ moves the tree somewhere the broker refuses, so the
25
+ // skill has to say not to, for as long as the two disagree.
26
+ it('warns against relocating the tree to site-deploy canonical path', () => {
27
+ expect(skill()).toMatch(/Leave the tree where you assembled it/)
28
+ expect(skill()).toMatch(/invalid-site/)
29
+ })
30
+ })
31
+
7
32
  describe('data-portal SKILL.md', () => {
8
33
  it('opens with frontmatter carrying name and description', () => {
9
34
  // Without these the skill is invisible to the available-skills reminder and
@@ -1,10 +1,11 @@
1
1
  import { describe, it, expect } from 'vitest'
2
- import { readFileSync } from 'node:fs'
2
+ import { readFileSync, existsSync, readdirSync } from 'node:fs'
3
3
  import { join } from 'node:path'
4
4
 
5
- const TEMPLATE = join(__dirname, '../../skills/data-portal/template')
5
+ const SKILL = join(__dirname, '../../skills/data-portal')
6
+ const TEMPLATE = join(SKILL, 'template')
6
7
  const wrangler = () => readFileSync(join(TEMPLATE, 'wrangler.toml'), 'utf8')
7
- const schema = () => readFileSync(join(TEMPLATE, 'schema.sql'), 'utf8')
8
+ const schema = () => readFileSync(join(SKILL, 'schema.sql'), 'utf8')
8
9
 
9
10
  describe('wrangler.toml', () => {
10
11
  it('binds D1 as DB', () => {
@@ -53,3 +54,54 @@ describe('schema.sql', () => {
53
54
  expect(schema()).toMatch(/CREATE UNIQUE INDEX IF NOT EXISTS auth_attempts_scope_window/i)
54
55
  })
55
56
  })
57
+
58
+ // The broker runs `wrangler pages deploy <dir>` with <dir> = the site root, so
59
+ // the positional directory IS the asset root and pages_build_output_dir is
60
+ // overridden. A nested public/ therefore publishes the site one level too deep
61
+ // and `/` returns 404 — the live failure Task 1774 closes.
62
+ describe('template layout matches what the deploy publishes', () => {
63
+ it('has no public/ level', () => {
64
+ expect(existsSync(join(TEMPLATE, 'public'))).toBe(false)
65
+ })
66
+
67
+ it('serves index.html, portal.css and portal.js from the tree root', () => {
68
+ for (const f of ['index.html', 'portal.css', 'portal.js']) {
69
+ expect(existsSync(join(TEMPLATE, f))).toBe(true)
70
+ }
71
+ })
72
+
73
+ it('keeps functions/ at the tree root', () => {
74
+ expect(existsSync(join(TEMPLATE, 'functions', 'api', 'auth.ts'))).toBe(true)
75
+ })
76
+
77
+ it('sets pages_build_output_dir to the tree root', () => {
78
+ expect(wrangler()).toMatch(/pages_build_output_dir\s*=\s*"\."/)
79
+ })
80
+
81
+ // Wrangler's Pages upload ignore list is fixed (_worker.js, _redirects,
82
+ // _headers, _routes.json, functions, .DS_Store, node_modules, .git,
83
+ // .wrangler) and Pages honours no .assetsignore, so every other file in the
84
+ // tree is served publicly. schema.sql has no reason to be, so it lives beside
85
+ // template/ rather than inside it. wrangler.toml has to stay and its exposure
86
+ // is tracked separately.
87
+ it('keeps schema.sql out of the publishable tree', () => {
88
+ expect(existsSync(join(TEMPLATE, 'schema.sql'))).toBe(false)
89
+ expect(existsSync(join(SKILL, 'schema.sql'))).toBe(true)
90
+ })
91
+
92
+ // Dotfiles are excluded because the ignore list already covers the ones that
93
+ // matter (.DS_Store, .git, .wrangler) and a stray Finder .DS_Store would
94
+ // otherwise fail this test for a reason it does not name.
95
+ it('publishes nothing beyond the site files, functions/ and wrangler.toml', () => {
96
+ const published = readdirSync(TEMPLATE)
97
+ .filter((e) => !e.startsWith('.'))
98
+ .sort()
99
+ expect(published).toEqual([
100
+ 'functions',
101
+ 'index.html',
102
+ 'portal.css',
103
+ 'portal.js',
104
+ 'wrangler.toml',
105
+ ])
106
+ })
107
+ })
@@ -76,12 +76,39 @@ files. The gate enforces it too, but do not enrol a name it will reject.
76
76
 
77
77
  ## Assemble the canonical tree
78
78
 
79
- Copy `template/` to `<accountDir>/pages/<project>/` and fill `wrangler.toml`'s `__PROJECT_NAME__`,
79
+ Copy `template/` to `<accountDir>/sites/<project>/` and fill `wrangler.toml`'s `__PROJECT_NAME__`,
80
80
  `__D1_DATABASE_NAME__`, `__D1_DATABASE_ID__`, and `__R2_BUCKET_NAME__`. **Leave no `__...__`
81
81
  placeholder in the assembled tree.**
82
82
 
83
- Name the D1 database with the same `bin/cf-store-name.sh` scheme as the bucket, and apply
84
- `schema.sql` to the remote database. It is `IF NOT EXISTS` throughout, so re-applying is safe.
83
+ `sites/<project>/` is the only location the broker will publish: `resolveSiteDir` resolves
84
+ `<accountDir>/sites/<name>` and refuses anything else with `invalid-site`. The tree is flat because
85
+ the deploy publishes the directory it is handed — `index.html`, `portal.css`, `portal.js`,
86
+ `functions/` and `wrangler.toml` all sit at its root, with no nested asset level, and
87
+ `pages_build_output_dir` reads `"."` to match. A nested asset level publishes the site one level too
88
+ deep, which serves 404 at the domain root.
89
+
90
+ The deploy serves everything in that tree except a fixed ignore list — `functions/`, `_worker.js`,
91
+ `_redirects`, `_headers`, `_routes.json`, `node_modules`, `.git`, `.DS_Store`, `.wrangler` — and
92
+ `pages deploy` reads no `.assetsignore`, so anything else you leave there is public. Nothing else
93
+ belongs in it. `schema.sql` therefore lives beside `template/` in the skill, not inside it. Name the D1 database
94
+ with the same `bin/cf-store-name.sh` scheme as the bucket, and apply
95
+ `platform/plugins/cloudflare/skills/data-portal/schema.sql` to the remote database from there. It is
96
+ `IF NOT EXISTS` throughout, so re-applying is safe.
97
+
98
+ The copied `portal.css` carries the template's default palette, which is nobody's brand. Apply the
99
+ install's before handing the tree to the deploy:
100
+
101
+ ```bash
102
+ node platform/plugins/cloudflare/bin/portal-brand-css.mjs \
103
+ --css <accountDir>/pages/<project>/public/portal.css
104
+ ```
105
+
106
+ It rewrites that stylesheet's one `:root` block from `consent.palette` in the install's brand
107
+ manifest and touches nothing else in the file. Do not set the colours by hand: the script validates
108
+ every value against the same rule the bundler applies to brand colours, refuses without writing
109
+ anything if the stylesheet or a value is not what it expects, and leaves the template defaults in
110
+ place for a brand that declares no palette — which is the case a hand-edit turns into an empty
111
+ declaration nobody sees until the page loads.
85
112
 
86
113
  ## Deploy through site-deploy
87
114
 
@@ -97,6 +124,14 @@ Hand the assembled tree to `cloudflare:site-deploy`. Both bindings — D1 and R2
97
124
  config in `wrangler.toml`, carried in the deploy payload under the Pages scope; the deploy needs no
98
125
  R2 token. Only the bucket *creation* in step 1 needs R2 write, and the broker holds that.
99
126
 
127
+ Read `site-deploy` for its auth, custom-domain and DNS guidance, but **not** for the tree location.
128
+ Its canonical-tree section names `<accountDir>/pages/<project>/` and tells you to reconcile stray
129
+ trees into it; that section describes the house path, which publishes with an explicit positional
130
+ output dir. A portal publishes through `storage-pages-deploy`, which accepts `<accountDir>/sites/`
131
+ and nothing else. Leave the tree where you assembled it. Moving it to `pages/` to satisfy that
132
+ section is the one way to turn a working portal into `invalid-site`. The two skills disagree on this
133
+ today and the disagreement is tracked, not resolved.
134
+
100
135
  ## Done-gate
101
136
 
102
137
  The portal is not done until all five hold on the live domain: