@rubytech/create-maxy-code 0.1.470 → 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 (423) hide show
  1. package/package.json +1 -1
  2. package/payload/platform/config/brand.json +1 -1
  3. package/payload/platform/docs/superpowers/plans/2026-07-18-graph-native-ledger.md +807 -0
  4. package/payload/platform/docs/superpowers/specs/2026-07-18-graph-native-ledger-design.md +241 -0
  5. package/payload/platform/lib/ledger-core/dist/ageing.d.ts +14 -0
  6. package/payload/platform/lib/ledger-core/dist/ageing.d.ts.map +1 -0
  7. package/payload/platform/lib/ledger-core/dist/ageing.js +38 -0
  8. package/payload/platform/lib/ledger-core/dist/ageing.js.map +1 -0
  9. package/payload/platform/lib/ledger-core/dist/balance.d.ts +25 -0
  10. package/payload/platform/lib/ledger-core/dist/balance.d.ts.map +1 -0
  11. package/payload/platform/lib/ledger-core/dist/balance.js +38 -0
  12. package/payload/platform/lib/ledger-core/dist/balance.js.map +1 -0
  13. package/payload/platform/lib/ledger-core/dist/index.d.ts +5 -0
  14. package/payload/platform/lib/ledger-core/dist/index.d.ts.map +1 -0
  15. package/payload/platform/lib/ledger-core/dist/index.js +5 -0
  16. package/payload/platform/lib/ledger-core/dist/index.js.map +1 -0
  17. package/payload/platform/lib/ledger-core/dist/money.d.ts +12 -0
  18. package/payload/platform/lib/ledger-core/dist/money.d.ts.map +1 -0
  19. package/payload/platform/lib/ledger-core/dist/money.js +16 -0
  20. package/payload/platform/lib/ledger-core/dist/money.js.map +1 -0
  21. package/payload/platform/lib/ledger-core/dist/reconcile.d.ts +51 -0
  22. package/payload/platform/lib/ledger-core/dist/reconcile.d.ts.map +1 -0
  23. package/payload/platform/lib/ledger-core/dist/reconcile.js +77 -0
  24. package/payload/platform/lib/ledger-core/dist/reconcile.js.map +1 -0
  25. package/payload/platform/lib/ledger-core/package.json +16 -0
  26. package/payload/platform/lib/ledger-core/src/__tests__/ageing.test.ts +42 -0
  27. package/payload/platform/lib/ledger-core/src/__tests__/balance.test.ts +48 -0
  28. package/payload/platform/lib/ledger-core/src/__tests__/reconcile.test.ts +87 -0
  29. package/payload/platform/lib/ledger-core/src/ageing.ts +51 -0
  30. package/payload/platform/lib/ledger-core/src/balance.ts +52 -0
  31. package/payload/platform/lib/ledger-core/src/index.ts +4 -0
  32. package/payload/platform/lib/ledger-core/src/money.ts +17 -0
  33. package/payload/platform/lib/ledger-core/src/reconcile.ts +109 -0
  34. package/payload/platform/lib/ledger-core/tsconfig.json +6 -0
  35. package/payload/platform/neo4j/schema.cypher +16 -0
  36. package/payload/platform/package.json +2 -2
  37. package/payload/platform/plugins/.claude-plugin/marketplace.json +5 -0
  38. package/payload/platform/plugins/admin/hooks/lib/maxy-mcp-plugins.txt +1 -0
  39. package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +98 -2
  40. package/payload/platform/plugins/cloudflare/bin/__tests__/portal-brand-css.test.sh +139 -0
  41. package/payload/platform/plugins/cloudflare/bin/portal-brand-css.mjs +128 -0
  42. package/payload/platform/plugins/cloudflare/mcp/__tests__/portal-enrol.test.ts +1 -1
  43. package/payload/platform/plugins/cloudflare/mcp/__tests__/skill-contract.test.ts +25 -0
  44. package/payload/platform/plugins/cloudflare/mcp/__tests__/template-config.test.ts +55 -3
  45. package/payload/platform/plugins/cloudflare/skills/data-portal/SKILL.md +38 -3
  46. package/payload/platform/plugins/cloudflare/skills/data-portal/template/{public/portal.css → portal.css} +13 -2
  47. package/payload/platform/plugins/cloudflare/skills/data-portal/template/wrangler.toml +1 -1
  48. package/payload/platform/plugins/docs/references/admin-ui.md +48 -0
  49. package/payload/platform/plugins/docs/references/internals.md +1 -1
  50. package/payload/platform/plugins/docs/references/ledger.md +43 -0
  51. package/payload/platform/plugins/email/PLUGIN.md +4 -0
  52. package/payload/platform/plugins/email/mcp/dist/__tests__/display-truncation.test.d.ts +2 -0
  53. package/payload/platform/plugins/email/mcp/dist/__tests__/display-truncation.test.d.ts.map +1 -0
  54. package/payload/platform/plugins/email/mcp/dist/__tests__/display-truncation.test.js +196 -0
  55. package/payload/platform/plugins/email/mcp/dist/__tests__/display-truncation.test.js.map +1 -0
  56. package/payload/platform/plugins/email/mcp/dist/__tests__/source-fetch-efficiency.test.js +10 -8
  57. package/payload/platform/plugins/email/mcp/dist/__tests__/source-fetch-efficiency.test.js.map +1 -1
  58. package/payload/platform/plugins/email/mcp/dist/__tests__/truncation-tool-output.test.d.ts +2 -0
  59. package/payload/platform/plugins/email/mcp/dist/__tests__/truncation-tool-output.test.d.ts.map +1 -0
  60. package/payload/platform/plugins/email/mcp/dist/__tests__/truncation-tool-output.test.js +110 -0
  61. package/payload/platform/plugins/email/mcp/dist/__tests__/truncation-tool-output.test.js.map +1 -0
  62. package/payload/platform/plugins/email/mcp/dist/lib/imap.d.ts +22 -0
  63. package/payload/platform/plugins/email/mcp/dist/lib/imap.d.ts.map +1 -1
  64. package/payload/platform/plugins/email/mcp/dist/lib/imap.js +90 -36
  65. package/payload/platform/plugins/email/mcp/dist/lib/imap.js.map +1 -1
  66. package/payload/platform/plugins/email/mcp/dist/tools/email-read.d.ts.map +1 -1
  67. package/payload/platform/plugins/email/mcp/dist/tools/email-read.js +6 -2
  68. package/payload/platform/plugins/email/mcp/dist/tools/email-read.js.map +1 -1
  69. package/payload/platform/plugins/email/mcp/dist/tools/email-search.d.ts.map +1 -1
  70. package/payload/platform/plugins/email/mcp/dist/tools/email-search.js +6 -2
  71. package/payload/platform/plugins/email/mcp/dist/tools/email-search.js.map +1 -1
  72. package/payload/platform/plugins/email/skills/email-ingest/SKILL.md +4 -0
  73. package/payload/platform/plugins/google/mcp/dist/__tests__/account-register.test.d.ts +2 -0
  74. package/payload/platform/plugins/google/mcp/dist/__tests__/account-register.test.d.ts.map +1 -0
  75. package/payload/platform/plugins/google/mcp/dist/__tests__/account-register.test.js +60 -0
  76. package/payload/platform/plugins/google/mcp/dist/__tests__/account-register.test.js.map +1 -0
  77. package/payload/platform/plugins/google/mcp/dist/__tests__/calendar-tools.test.d.ts +2 -0
  78. package/payload/platform/plugins/google/mcp/dist/__tests__/calendar-tools.test.d.ts.map +1 -0
  79. package/payload/platform/plugins/google/mcp/dist/__tests__/calendar-tools.test.js +48 -0
  80. package/payload/platform/plugins/google/mcp/dist/__tests__/calendar-tools.test.js.map +1 -0
  81. package/payload/platform/plugins/google/mcp/dist/__tests__/google-client.test.d.ts +2 -0
  82. package/payload/platform/plugins/google/mcp/dist/__tests__/google-client.test.d.ts.map +1 -0
  83. package/payload/platform/plugins/google/mcp/dist/__tests__/google-client.test.js +25 -0
  84. package/payload/platform/plugins/google/mcp/dist/__tests__/google-client.test.js.map +1 -0
  85. package/payload/platform/plugins/google/mcp/dist/__tests__/index-registration.test.d.ts +2 -0
  86. package/payload/platform/plugins/google/mcp/dist/__tests__/index-registration.test.d.ts.map +1 -0
  87. package/payload/platform/plugins/google/mcp/dist/__tests__/index-registration.test.js +40 -0
  88. package/payload/platform/plugins/google/mcp/dist/__tests__/index-registration.test.js.map +1 -0
  89. package/payload/platform/plugins/google/mcp/dist/__tests__/pending-store.test.d.ts +2 -0
  90. package/payload/platform/plugins/google/mcp/dist/__tests__/pending-store.test.d.ts.map +1 -0
  91. package/payload/platform/plugins/google/mcp/dist/__tests__/pending-store.test.js +35 -0
  92. package/payload/platform/plugins/google/mcp/dist/__tests__/pending-store.test.js.map +1 -0
  93. package/payload/platform/plugins/google/mcp/dist/__tests__/token-store.test.d.ts +2 -0
  94. package/payload/platform/plugins/google/mcp/dist/__tests__/token-store.test.d.ts.map +1 -0
  95. package/payload/platform/plugins/google/mcp/dist/__tests__/token-store.test.js +85 -0
  96. package/payload/platform/plugins/google/mcp/dist/__tests__/token-store.test.js.map +1 -0
  97. package/payload/platform/plugins/google/mcp/dist/auth/pending-store.d.ts +25 -0
  98. package/payload/platform/plugins/google/mcp/dist/auth/pending-store.d.ts.map +1 -0
  99. package/payload/platform/plugins/google/mcp/dist/auth/pending-store.js +87 -0
  100. package/payload/platform/plugins/google/mcp/dist/auth/pending-store.js.map +1 -0
  101. package/payload/platform/plugins/google/mcp/dist/auth/token-store.d.ts +46 -0
  102. package/payload/platform/plugins/google/mcp/dist/auth/token-store.d.ts.map +1 -0
  103. package/payload/platform/plugins/google/mcp/dist/auth/token-store.js +138 -0
  104. package/payload/platform/plugins/google/mcp/dist/auth/token-store.js.map +1 -0
  105. package/payload/platform/plugins/google/mcp/dist/index.d.ts +25 -0
  106. package/payload/platform/plugins/google/mcp/dist/index.d.ts.map +1 -0
  107. package/payload/platform/plugins/google/mcp/dist/index.js +300 -0
  108. package/payload/platform/plugins/google/mcp/dist/index.js.map +1 -0
  109. package/payload/platform/plugins/google/mcp/dist/lib/google-client.d.ts +28 -0
  110. package/payload/platform/plugins/google/mcp/dist/lib/google-client.d.ts.map +1 -0
  111. package/payload/platform/plugins/google/mcp/dist/lib/google-client.js +102 -0
  112. package/payload/platform/plugins/google/mcp/dist/lib/google-client.js.map +1 -0
  113. package/payload/platform/plugins/google/mcp/dist/lib/log.d.ts +3 -0
  114. package/payload/platform/plugins/google/mcp/dist/lib/log.d.ts.map +1 -0
  115. package/payload/platform/plugins/google/mcp/dist/lib/log.js +16 -0
  116. package/payload/platform/plugins/google/mcp/dist/lib/log.js.map +1 -0
  117. package/payload/platform/plugins/google/mcp/dist/tools/account-info.d.ts +12 -0
  118. package/payload/platform/plugins/google/mcp/dist/tools/account-info.d.ts.map +1 -0
  119. package/payload/platform/plugins/google/mcp/dist/tools/account-info.js +23 -0
  120. package/payload/platform/plugins/google/mcp/dist/tools/account-info.js.map +1 -0
  121. package/payload/platform/plugins/google/mcp/dist/tools/account-list.d.ts +12 -0
  122. package/payload/platform/plugins/google/mcp/dist/tools/account-list.d.ts.map +1 -0
  123. package/payload/platform/plugins/google/mcp/dist/tools/account-list.js +9 -0
  124. package/payload/platform/plugins/google/mcp/dist/tools/account-list.js.map +1 -0
  125. package/payload/platform/plugins/google/mcp/dist/tools/account-register.d.ts +42 -0
  126. package/payload/platform/plugins/google/mcp/dist/tools/account-register.d.ts.map +1 -0
  127. package/payload/platform/plugins/google/mcp/dist/tools/account-register.js +102 -0
  128. package/payload/platform/plugins/google/mcp/dist/tools/account-register.js.map +1 -0
  129. package/payload/platform/plugins/google/mcp/dist/tools/calendar-cancel.d.ts +10 -0
  130. package/payload/platform/plugins/google/mcp/dist/tools/calendar-cancel.d.ts.map +1 -0
  131. package/payload/platform/plugins/google/mcp/dist/tools/calendar-cancel.js +9 -0
  132. package/payload/platform/plugins/google/mcp/dist/tools/calendar-cancel.js.map +1 -0
  133. package/payload/platform/plugins/google/mcp/dist/tools/calendar-create.d.ts +20 -0
  134. package/payload/platform/plugins/google/mcp/dist/tools/calendar-create.d.ts.map +1 -0
  135. package/payload/platform/plugins/google/mcp/dist/tools/calendar-create.js +25 -0
  136. package/payload/platform/plugins/google/mcp/dist/tools/calendar-create.js.map +1 -0
  137. package/payload/platform/plugins/google/mcp/dist/tools/calendar-event.d.ts +6 -0
  138. package/payload/platform/plugins/google/mcp/dist/tools/calendar-event.d.ts.map +1 -0
  139. package/payload/platform/plugins/google/mcp/dist/tools/calendar-event.js +7 -0
  140. package/payload/platform/plugins/google/mcp/dist/tools/calendar-event.js.map +1 -0
  141. package/payload/platform/plugins/google/mcp/dist/tools/calendar-freebusy.d.ts +14 -0
  142. package/payload/platform/plugins/google/mcp/dist/tools/calendar-freebusy.d.ts.map +1 -0
  143. package/payload/platform/plugins/google/mcp/dist/tools/calendar-freebusy.js +16 -0
  144. package/payload/platform/plugins/google/mcp/dist/tools/calendar-freebusy.js.map +1 -0
  145. package/payload/platform/plugins/google/mcp/dist/tools/calendar-list.d.ts +44 -0
  146. package/payload/platform/plugins/google/mcp/dist/tools/calendar-list.d.ts.map +1 -0
  147. package/payload/platform/plugins/google/mcp/dist/tools/calendar-list.js +36 -0
  148. package/payload/platform/plugins/google/mcp/dist/tools/calendar-list.js.map +1 -0
  149. package/payload/platform/plugins/google/mcp/dist/tools/calendar-respond.d.ts +16 -0
  150. package/payload/platform/plugins/google/mcp/dist/tools/calendar-respond.d.ts.map +1 -0
  151. package/payload/platform/plugins/google/mcp/dist/tools/calendar-respond.js +29 -0
  152. package/payload/platform/plugins/google/mcp/dist/tools/calendar-respond.js.map +1 -0
  153. package/payload/platform/plugins/google/mcp/dist/tools/calendar-update.d.ts +16 -0
  154. package/payload/platform/plugins/google/mcp/dist/tools/calendar-update.d.ts.map +1 -0
  155. package/payload/platform/plugins/google/mcp/dist/tools/calendar-update.js +32 -0
  156. package/payload/platform/plugins/google/mcp/dist/tools/calendar-update.js.map +1 -0
  157. package/payload/platform/plugins/google/references/auth.md +35 -0
  158. package/payload/platform/plugins/ledger/.claude-plugin/plugin.json +21 -0
  159. package/payload/platform/plugins/ledger/PLUGIN.md +82 -0
  160. package/payload/platform/plugins/ledger/lib/mcp-spawn-tee/index.js +193 -0
  161. package/payload/platform/plugins/ledger/lib/mcp-spawn-tee/package.json +3 -0
  162. package/payload/platform/plugins/ledger/mcp/dist/index.d.ts +2 -0
  163. package/payload/platform/plugins/ledger/mcp/dist/index.d.ts.map +1 -0
  164. package/payload/platform/plugins/ledger/mcp/dist/index.js +168 -0
  165. package/payload/platform/plugins/ledger/mcp/dist/index.js.map +1 -0
  166. package/payload/platform/plugins/ledger/mcp/dist/lib/ids.d.ts +4 -0
  167. package/payload/platform/plugins/ledger/mcp/dist/lib/ids.d.ts.map +1 -0
  168. package/payload/platform/plugins/ledger/mcp/dist/lib/ids.js +0 -0
  169. package/payload/platform/plugins/ledger/mcp/dist/lib/ids.js.map +1 -0
  170. package/payload/platform/plugins/ledger/mcp/dist/lib/neo4j.d.ts +5 -0
  171. package/payload/platform/plugins/ledger/mcp/dist/lib/neo4j.d.ts.map +1 -0
  172. package/payload/platform/plugins/ledger/mcp/dist/lib/neo4j.js +40 -0
  173. package/payload/platform/plugins/ledger/mcp/dist/lib/neo4j.js.map +1 -0
  174. package/payload/platform/plugins/ledger/mcp/dist/lib/read.d.ts +29 -0
  175. package/payload/platform/plugins/ledger/mcp/dist/lib/read.d.ts.map +1 -0
  176. package/payload/platform/plugins/ledger/mcp/dist/lib/read.js +138 -0
  177. package/payload/platform/plugins/ledger/mcp/dist/lib/read.js.map +1 -0
  178. package/payload/platform/plugins/ledger/mcp/dist/lib/types.d.ts +47 -0
  179. package/payload/platform/plugins/ledger/mcp/dist/lib/types.d.ts.map +1 -0
  180. package/payload/platform/plugins/ledger/mcp/dist/lib/types.js +14 -0
  181. package/payload/platform/plugins/ledger/mcp/dist/lib/types.js.map +1 -0
  182. package/payload/platform/plugins/ledger/mcp/dist/tools/balance.d.ts +18 -0
  183. package/payload/platform/plugins/ledger/mcp/dist/tools/balance.d.ts.map +1 -0
  184. package/payload/platform/plugins/ledger/mcp/dist/tools/balance.js +23 -0
  185. package/payload/platform/plugins/ledger/mcp/dist/tools/balance.js.map +1 -0
  186. package/payload/platform/plugins/ledger/mcp/dist/tools/cash-record.d.ts +26 -0
  187. package/payload/platform/plugins/ledger/mcp/dist/tools/cash-record.d.ts.map +1 -0
  188. package/payload/platform/plugins/ledger/mcp/dist/tools/cash-record.js +33 -0
  189. package/payload/platform/plugins/ledger/mcp/dist/tools/cash-record.js.map +1 -0
  190. package/payload/platform/plugins/ledger/mcp/dist/tools/invoice-record.d.ts +27 -0
  191. package/payload/platform/plugins/ledger/mcp/dist/tools/invoice-record.d.ts.map +1 -0
  192. package/payload/platform/plugins/ledger/mcp/dist/tools/invoice-record.js +40 -0
  193. package/payload/platform/plugins/ledger/mcp/dist/tools/invoice-record.js.map +1 -0
  194. package/payload/platform/plugins/ledger/mcp/dist/tools/payment-record.d.ts +46 -0
  195. package/payload/platform/plugins/ledger/mcp/dist/tools/payment-record.d.ts.map +1 -0
  196. package/payload/platform/plugins/ledger/mcp/dist/tools/payment-record.js +68 -0
  197. package/payload/platform/plugins/ledger/mcp/dist/tools/payment-record.js.map +1 -0
  198. package/payload/platform/plugins/ledger/mcp/dist/tools/reconcile.d.ts +20 -0
  199. package/payload/platform/plugins/ledger/mcp/dist/tools/reconcile.d.ts.map +1 -0
  200. package/payload/platform/plugins/ledger/mcp/dist/tools/reconcile.js +30 -0
  201. package/payload/platform/plugins/ledger/mcp/dist/tools/reconcile.js.map +1 -0
  202. package/payload/platform/plugins/ledger/mcp/dist/tools/statement.d.ts +26 -0
  203. package/payload/platform/plugins/ledger/mcp/dist/tools/statement.d.ts.map +1 -0
  204. package/payload/platform/plugins/ledger/mcp/dist/tools/statement.js +35 -0
  205. package/payload/platform/plugins/ledger/mcp/dist/tools/statement.js.map +1 -0
  206. package/payload/platform/plugins/ledger/mcp/package.json +21 -0
  207. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/ledger-vertical-coverage.test.d.ts +2 -0
  208. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/ledger-vertical-coverage.test.d.ts.map +1 -0
  209. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/ledger-vertical-coverage.test.js +81 -0
  210. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/ledger-vertical-coverage.test.js.map +1 -0
  211. package/payload/platform/plugins/memory/mcp/dist/lib/schema-loader.d.ts +9 -7
  212. package/payload/platform/plugins/memory/mcp/dist/lib/schema-loader.d.ts.map +1 -1
  213. package/payload/platform/plugins/memory/mcp/dist/lib/schema-loader.js +10 -8
  214. package/payload/platform/plugins/memory/mcp/dist/lib/schema-loader.js.map +1 -1
  215. package/payload/platform/plugins/memory/references/schema-base.md +22 -0
  216. package/payload/platform/plugins/memory/references/schema-construction.md +0 -18
  217. package/payload/platform/plugins/scheduling/PLUGIN.md +20 -0
  218. package/payload/platform/plugins/scheduling/mcp/dist/lib/meeting-write.d.ts.map +1 -1
  219. package/payload/platform/plugins/scheduling/mcp/dist/lib/meeting-write.js +11 -2
  220. package/payload/platform/plugins/scheduling/mcp/dist/lib/meeting-write.js.map +1 -1
  221. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-event.js +1 -1
  222. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-event.js.map +1 -1
  223. package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-create.js +3 -3
  224. package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-create.js.map +1 -1
  225. package/payload/platform/services/claude-session-manager/dist/activity-rows.d.ts +138 -0
  226. package/payload/platform/services/claude-session-manager/dist/activity-rows.d.ts.map +1 -0
  227. package/payload/platform/services/claude-session-manager/dist/activity-rows.js +268 -0
  228. package/payload/platform/services/claude-session-manager/dist/activity-rows.js.map +1 -0
  229. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.d.ts.map +1 -1
  230. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js +8 -0
  231. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js.map +1 -1
  232. package/payload/platform/services/claude-session-manager/dist/http-server.d.ts +6 -0
  233. package/payload/platform/services/claude-session-manager/dist/http-server.d.ts.map +1 -1
  234. package/payload/platform/services/claude-session-manager/dist/http-server.js +42 -0
  235. package/payload/platform/services/claude-session-manager/dist/http-server.js.map +1 -1
  236. package/payload/platform/services/claude-session-manager/dist/index.js +10 -1
  237. package/payload/platform/services/claude-session-manager/dist/index.js.map +1 -1
  238. package/payload/platform/services/claude-session-manager/dist/pty-census.d.ts +80 -6
  239. package/payload/platform/services/claude-session-manager/dist/pty-census.d.ts.map +1 -1
  240. package/payload/platform/services/claude-session-manager/dist/pty-census.js +96 -20
  241. package/payload/platform/services/claude-session-manager/dist/pty-census.js.map +1 -1
  242. package/payload/platform/services/claude-session-manager/dist/slice-memory-policy.d.ts.map +1 -1
  243. package/payload/platform/services/claude-session-manager/dist/slice-memory-policy.js +5 -1
  244. package/payload/platform/services/claude-session-manager/dist/slice-memory-policy.js.map +1 -1
  245. package/payload/platform/templates/specialists/agents/database-operator.md +1 -1
  246. package/payload/platform/templates/specialists/agents/project-manager.md +1 -1
  247. package/payload/server/{chunk-JRIBGO45.js → chunk-BSTIQX52.js} +44 -0
  248. package/payload/server/maxy-edge.js +1 -1
  249. package/payload/server/public/activity.html +17 -0
  250. package/payload/server/public/assets/AdminLoginScreens-Rf94VD9w.js +1 -0
  251. package/payload/server/public/assets/AdminShell-CyGKg7MG.js +2 -0
  252. package/payload/server/public/assets/Checkbox-B0jmI2y2.js +1 -0
  253. package/payload/server/public/assets/HeaderMenu-BnTYGNaB.js +9 -0
  254. package/payload/server/public/assets/{useSubAccountSwitcher-pu0jjLCz.css → HeaderMenu-DtIEBHOU.css} +1 -1
  255. package/payload/server/public/assets/activity-BOBTWFr-.js +1 -0
  256. package/payload/server/public/assets/admin-HDLzwbLl.js +1 -0
  257. package/payload/server/public/assets/{arc-qKfXIt8v.js → arc-CFMJyMm-.js} +1 -1
  258. package/payload/server/public/assets/architecture-YZFGNWBL-Cypt5qHL.js +1 -0
  259. package/payload/server/public/assets/{architectureDiagram-Q4EWVU46-DkVrGn7G.js → architectureDiagram-Q4EWVU46-CmOpXLsH.js} +1 -1
  260. package/payload/server/public/assets/{blockDiagram-DXYQGD6D-DqavswGH.js → blockDiagram-DXYQGD6D-CmS8f9YF.js} +1 -1
  261. package/payload/server/public/assets/browser-D4_XLSSm.js +1 -0
  262. package/payload/server/public/assets/{c4Diagram-AHTNJAMY-B7fyKiXe.js → c4Diagram-AHTNJAMY-BXIjMl6A.js} +1 -1
  263. package/payload/server/public/assets/calendar-s5V_gbI7.js +1 -0
  264. package/payload/server/public/assets/channel-DhrFAm7m.js +1 -0
  265. package/payload/server/public/assets/chat-40WyKzGb.js +1 -0
  266. package/payload/server/public/assets/chevron-left-C35qGP5f.js +1 -0
  267. package/payload/server/public/assets/{chunk-2KRD3SAO-DsgMtGkv.js → chunk-2KRD3SAO-D4fktAwW.js} +1 -1
  268. package/payload/server/public/assets/{chunk-336JU56O-CkdISfNt.js → chunk-336JU56O-C2MKnRTj.js} +2 -2
  269. package/payload/server/public/assets/chunk-426QAEUC-BhU7-TyS.js +1 -0
  270. package/payload/server/public/assets/{chunk-4BX2VUAB-CYpiF2bl.js → chunk-4BX2VUAB-C13Wz3zS.js} +1 -1
  271. package/payload/server/public/assets/{chunk-4TB4RGXK-CWyc_F7v.js → chunk-4TB4RGXK-CalYahTR.js} +1 -1
  272. package/payload/server/public/assets/{chunk-55IACEB6-BbzKMw2Z.js → chunk-55IACEB6-TmOKdy8a.js} +1 -1
  273. package/payload/server/public/assets/{chunk-5FUZZQ4R-u19Nai9e.js → chunk-5FUZZQ4R-C-i0BVag.js} +1 -1
  274. package/payload/server/public/assets/{chunk-5PVQY5BW-Ba8by6Uy.js → chunk-5PVQY5BW-D6bJMLnf.js} +1 -1
  275. package/payload/server/public/assets/{chunk-67CJDMHE-BwUuxtyb.js → chunk-67CJDMHE-Cif9Uu5C.js} +1 -1
  276. package/payload/server/public/assets/{chunk-7N4EOEYR-BRH9iM9L.js → chunk-7N4EOEYR-WjTHCXnd.js} +1 -1
  277. package/payload/server/public/assets/{chunk-AA7GKIK3-BGoztgLL.js → chunk-AA7GKIK3-CwCfdQmb.js} +1 -1
  278. package/payload/server/public/assets/{chunk-BSJP7CBP-C5GDAZTl.js → chunk-BSJP7CBP-9A67yXwT.js} +1 -1
  279. package/payload/server/public/assets/{chunk-CIAEETIT-ChY32VPS.js → chunk-CIAEETIT-DPMiWSBi.js} +1 -1
  280. package/payload/server/public/assets/{chunk-EDXVE4YY-CPSD3p6p.js → chunk-EDXVE4YY-B9PBdwiT.js} +1 -1
  281. package/payload/server/public/assets/{chunk-ENJZ2VHE-D1oL-iUr.js → chunk-ENJZ2VHE-CM5OqcQ5.js} +1 -1
  282. package/payload/server/public/assets/{chunk-FMBD7UC4-CPvYEr54.js → chunk-FMBD7UC4-Bmt_ZRlV.js} +1 -1
  283. package/payload/server/public/assets/{chunk-FOC6F5B3-9Yyj7Vbz.js → chunk-FOC6F5B3-DfGJGxpX.js} +1 -1
  284. package/payload/server/public/assets/{chunk-ICPOFSXX-BaYgh8FR.js → chunk-ICPOFSXX-D6M89DA0.js} +2 -2
  285. package/payload/server/public/assets/{chunk-K5T4RW27-5ucpeRvm.js → chunk-K5T4RW27-DMpoaqw2.js} +1 -1
  286. package/payload/server/public/assets/{chunk-KGLVRYIC-DW-FHk7T.js → chunk-KGLVRYIC-DYUFgri2.js} +1 -1
  287. package/payload/server/public/assets/{chunk-LIHQZDEY-CGGVfAyG.js → chunk-LIHQZDEY-x3LmeKmx.js} +1 -1
  288. package/payload/server/public/assets/{chunk-ORNJ4GCN-Bv8EUa5d.js → chunk-ORNJ4GCN-BeuNAWkW.js} +1 -1
  289. package/payload/server/public/assets/{chunk-OYMX7WX6-C1mXJnyw.js → chunk-OYMX7WX6-Bv6gisQ6.js} +1 -1
  290. package/payload/server/public/assets/chunk-QZHKN3VN-7_kS3RBy.js +1 -0
  291. package/payload/server/public/assets/{chunk-U2HBQHQK-PwSQz6EP.js → chunk-U2HBQHQK-C2iT9Av2.js} +1 -1
  292. package/payload/server/public/assets/{chunk-X2U36JSP--NPHTBlU.js → chunk-X2U36JSP-Bd6bv87A.js} +1 -1
  293. package/payload/server/public/assets/{chunk-XPW4576I-Bbc7Da32.js → chunk-XPW4576I-ByFnN4JO.js} +1 -1
  294. package/payload/server/public/assets/{chunk-YZCP3GAM-D-9v8e--.js → chunk-YZCP3GAM-Bt8I6ZDb.js} +1 -1
  295. package/payload/server/public/assets/{chunk-ZZ45TVLE-CMPg670Q.js → chunk-ZZ45TVLE-DBMPgZ_A.js} +1 -1
  296. package/payload/server/public/assets/classDiagram-6PBFFD2Q-BrWqm88p.js +1 -0
  297. package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-D0wzG5Fd.js +1 -0
  298. package/payload/server/public/assets/clone-DQA-nNrl.js +1 -0
  299. package/payload/server/public/assets/{cose-bilkent-S5V4N54A-NVSb1Bhy.js → cose-bilkent-S5V4N54A-DgLEaApT.js} +1 -1
  300. package/payload/server/public/assets/{dagre-KV5264BT-V2Ai1DFl.js → dagre-KV5264BT-kWVPgmmL.js} +1 -1
  301. package/payload/server/public/assets/{dagre-QfLcO-1j.js → dagre-mSazqXgG.js} +1 -1
  302. package/payload/server/public/assets/data-Bq0vU3qJ.js +1 -0
  303. package/payload/server/public/assets/{diagram-5BDNPKRD-Caw21693.js → diagram-5BDNPKRD-BHe_omJD.js} +1 -1
  304. package/payload/server/public/assets/{diagram-G4DWMVQ6-CgwVkT2q.js → diagram-G4DWMVQ6-fUFsmM3x.js} +1 -1
  305. package/payload/server/public/assets/{diagram-MMDJMWI5-DFUrLZhs.js → diagram-MMDJMWI5-DmujPLlM.js} +1 -1
  306. package/payload/server/public/assets/{diagram-TYMM5635-CEEiEuZJ.js → diagram-TYMM5635-B7Aoas89.js} +1 -1
  307. package/payload/server/public/assets/{dist-Cvgxm7qv.js → dist-C9D_xz81.js} +1 -1
  308. package/payload/server/public/assets/{erDiagram-SMLLAGMA-CG10NrT3.js → erDiagram-SMLLAGMA-6hS1rGiO.js} +1 -1
  309. package/payload/server/public/assets/{flatten-DAO0sfCM.js → flatten-BXzaHgtL.js} +1 -1
  310. package/payload/server/public/assets/{flowDiagram-DWJPFMVM-BqkV9Okv.js → flowDiagram-DWJPFMVM-Bqhr_r5b.js} +1 -1
  311. package/payload/server/public/assets/{ganttDiagram-T4ZO3ILL-DRiCDQF6.js → ganttDiagram-T4ZO3ILL-B8kjyN4o.js} +1 -1
  312. package/payload/server/public/assets/gitGraph-7Q5UKJZL-C_-XjxLd.js +1 -0
  313. package/payload/server/public/assets/{gitGraphDiagram-UUTBAWPF-BZV2voyy.js → gitGraphDiagram-UUTBAWPF--TTRq4ir.js} +1 -1
  314. package/payload/server/public/assets/{graph-BZiudUGr.js → graph-C252ZHjp.js} +3 -3
  315. package/payload/server/public/assets/graph-labels-DzPbbhLc.js +1 -0
  316. package/payload/server/public/assets/{graphlib-DGPThX8M.js → graphlib-CWkjawc5.js} +1 -1
  317. package/payload/server/public/assets/info-OMHHGYJF-BH8yDCfL.js +1 -0
  318. package/payload/server/public/assets/infoDiagram-42DDH7IO-D8U4M0pK.js +2 -0
  319. package/payload/server/public/assets/{isEmpty-Cb_U81C6.js → isEmpty-uB1dEu_Z.js} +1 -1
  320. package/payload/server/public/assets/{ishikawaDiagram-UXIWVN3A-BKsAxSvp.js → ishikawaDiagram-UXIWVN3A-CR8B7VzM.js} +1 -1
  321. package/payload/server/public/assets/{journeyDiagram-VCZTEJTY-CmliuOd-.js → journeyDiagram-VCZTEJTY-45lWAvtq.js} +1 -1
  322. package/payload/server/public/assets/{kanban-definition-6JOO6SKY-DAlElMb-.js → kanban-definition-6JOO6SKY-DkrPXLVb.js} +1 -1
  323. package/payload/server/public/assets/{line-DKZqLKAC.js → line-Db0H446a.js} +1 -1
  324. package/payload/server/public/assets/{linear-CbSGdh1x.js → linear-X3O7lwK3.js} +1 -1
  325. package/payload/server/public/assets/maximize-2-CEbXFpQT.js +1 -0
  326. package/payload/server/public/assets/{mermaid-parser.core-DFvbao9C.js → mermaid-parser.core-CixBHOya.js} +2 -2
  327. package/payload/server/public/assets/{mermaid.core-C0vEBxDm.js → mermaid.core-BGQq98L7.js} +3 -3
  328. package/payload/server/public/assets/{mindmap-definition-QFDTVHPH-CShASUKd.js → mindmap-definition-QFDTVHPH-v9cJl1mj.js} +1 -1
  329. package/payload/server/public/assets/operator-zKk4HMdX.js +1 -0
  330. package/payload/server/public/assets/{ordinal-H0tVmBDP.js → ordinal-CZY9laxg.js} +1 -1
  331. package/payload/server/public/assets/packet-4T2RLAQJ-Bajcns4r.js +1 -0
  332. package/payload/server/public/assets/page-BrmN_vzV.js +1 -0
  333. package/payload/server/public/assets/page-Cts-ESBe.js +32 -0
  334. package/payload/server/public/assets/{pdf-render-jAQU0nXt.js → pdf-render-7HZBTZ0_.js} +1 -1
  335. package/payload/server/public/assets/pie-ZZUOXDRM-D7hjM5w1.js +1 -0
  336. package/payload/server/public/assets/{pieDiagram-DEJITSTG-CBZHpDAJ.js → pieDiagram-DEJITSTG-DQ0b6edX.js} +1 -1
  337. package/payload/server/public/assets/public-BItzHBc0.js +1 -0
  338. package/payload/server/public/assets/{quadrantDiagram-34T5L4WZ-n3bB3K91.js → quadrantDiagram-34T5L4WZ-B64mj7Ei.js} +1 -1
  339. package/payload/server/public/assets/radar-PYXPWWZC-B46d5x7a.js +1 -0
  340. package/payload/server/public/assets/{reduce-DAvwat-a.js → reduce-BGQzAenn.js} +1 -1
  341. package/payload/server/public/assets/{requirementDiagram-MS252O5E-DiDvQK3c.js → requirementDiagram-MS252O5E-CE2JIEWo.js} +1 -1
  342. package/payload/server/public/assets/rotate-ccw-B0EYGhGx.js +1 -0
  343. package/payload/server/public/assets/{sankeyDiagram-XADWPNL6-DWDQQZmd.js → sankeyDiagram-XADWPNL6-Cmt5cfCz.js} +1 -1
  344. package/payload/server/public/assets/{sequenceDiagram-FGHM5R23-DmAqRlBT.js → sequenceDiagram-FGHM5R23-B5vVn_sw.js} +1 -1
  345. package/payload/server/public/assets/{src-DV4LKv2E.js → src-BTKk8KAG.js} +1 -1
  346. package/payload/server/public/assets/{stateDiagram-FHFEXIEX-D4R-n6bZ.js → stateDiagram-FHFEXIEX-DiUEJ-RZ.js} +1 -1
  347. package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-DIm3XuAD.js +1 -0
  348. package/payload/server/public/assets/tasks-ClE8-be8.js +1 -0
  349. package/payload/server/public/assets/time-entry-format-BwjGLnXu.js +1 -0
  350. package/payload/server/public/assets/{timeline-definition-GMOUNBTQ-CjiHGP4l.js → timeline-definition-GMOUNBTQ-C9jprDVw.js} +1 -1
  351. package/payload/server/public/assets/treeView-SZITEDCU-3XQh5xQ1.js +1 -0
  352. package/payload/server/public/assets/treemap-W4RFUUIX-DwR-_l5z.js +1 -0
  353. package/payload/server/public/assets/triangle-alert-DejWYn1x.js +1 -0
  354. package/payload/server/public/assets/useCopyFeedback-CpSgOavK.js +1 -0
  355. package/payload/server/public/assets/useSelectionMode-ZQZQUgze.js +1 -0
  356. package/payload/server/public/assets/useSubAccountSwitcher-DuRLvCfO.js +1 -0
  357. package/payload/server/public/assets/useVoiceRecorder-BTYYeUJW.js +2 -0
  358. package/payload/server/public/assets/{vennDiagram-DHZGUBPP-CBdDQ7c-.js → vennDiagram-DHZGUBPP-DWqQJ9X0.js} +1 -1
  359. package/payload/server/public/assets/wardley-RL74JXVD-DZp9oCGv.js +1 -0
  360. package/payload/server/public/assets/{wardleyDiagram-NUSXRM2D-BlIQPKLb.js → wardleyDiagram-NUSXRM2D-Dg6c2Voh.js} +1 -1
  361. package/payload/server/public/assets/{xychartDiagram-5P7HB3ND-CslKgh9B.js → xychartDiagram-5P7HB3ND-EhN-zeDj.js} +1 -1
  362. package/payload/server/public/browser.html +7 -6
  363. package/payload/server/public/calendar.html +8 -7
  364. package/payload/server/public/chat.html +15 -14
  365. package/payload/server/public/data.html +14 -12
  366. package/payload/server/public/graph.html +13 -12
  367. package/payload/server/public/index.html +17 -15
  368. package/payload/server/public/operator.html +17 -16
  369. package/payload/server/public/public.html +15 -14
  370. package/payload/server/public/tasks.html +7 -6
  371. package/payload/server/server.js +525 -244
  372. package/payload/server/public/assets/AdminLoginScreens-Bed8SDxY.js +0 -1
  373. package/payload/server/public/assets/AdminShell-BLh9O6dn.js +0 -2
  374. package/payload/server/public/assets/Checkbox-BC_KSbpS.js +0 -1
  375. package/payload/server/public/assets/admin-DfWgJVI3.js +0 -1
  376. package/payload/server/public/assets/architecture-YZFGNWBL-FPHT7s-C.js +0 -1
  377. package/payload/server/public/assets/browser-C0vkv-hH.js +0 -1
  378. package/payload/server/public/assets/calendar-_pfHw0Hp.js +0 -1
  379. package/payload/server/public/assets/channel-j70eMvcL.js +0 -1
  380. package/payload/server/public/assets/chat-DYT5H81o.js +0 -1
  381. package/payload/server/public/assets/chevron-left-BnpVBu51.js +0 -1
  382. package/payload/server/public/assets/chunk-426QAEUC-D-NcAlTW.js +0 -1
  383. package/payload/server/public/assets/chunk-QZHKN3VN-CAwbQBtf.js +0 -1
  384. package/payload/server/public/assets/classDiagram-6PBFFD2Q-Ct6Rn0vt.js +0 -1
  385. package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-B_2XCIFv.js +0 -1
  386. package/payload/server/public/assets/clone-DjPWA_NH.js +0 -1
  387. package/payload/server/public/assets/data-CR-fbPWr.js +0 -1
  388. package/payload/server/public/assets/gitGraph-7Q5UKJZL-B8Hx7f-D.js +0 -1
  389. package/payload/server/public/assets/graph-labels-BzmmJwCR.js +0 -1
  390. package/payload/server/public/assets/info-OMHHGYJF-BicCJTqU.js +0 -1
  391. package/payload/server/public/assets/infoDiagram-42DDH7IO-tf-8rOez.js +0 -2
  392. package/payload/server/public/assets/maximize-2-Bhdsv0U-.js +0 -1
  393. package/payload/server/public/assets/operator-gm2eP4r6.js +0 -1
  394. package/payload/server/public/assets/packet-4T2RLAQJ-Ckn_5hq0.js +0 -1
  395. package/payload/server/public/assets/page-8AB3jUa_.js +0 -1
  396. package/payload/server/public/assets/page-BoPorLg-.js +0 -32
  397. package/payload/server/public/assets/pie-ZZUOXDRM-DzlGQW3J.js +0 -1
  398. package/payload/server/public/assets/public-DqJk6qgW.js +0 -1
  399. package/payload/server/public/assets/radar-PYXPWWZC-BHo2YD8b.js +0 -1
  400. package/payload/server/public/assets/rotate-ccw-D_5cETGt.js +0 -1
  401. package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-BcFRbGRu.js +0 -1
  402. package/payload/server/public/assets/tasks-EyKLXINi.js +0 -1
  403. package/payload/server/public/assets/time-entry-format-DAwmmR3i.js +0 -1
  404. package/payload/server/public/assets/treeView-SZITEDCU-BtRr5vXp.js +0 -1
  405. package/payload/server/public/assets/treemap-W4RFUUIX-D8p0sbyo.js +0 -1
  406. package/payload/server/public/assets/useCopyFeedback-rOWd_lzl.js +0 -1
  407. package/payload/server/public/assets/useSelectionMode-DDmLmPph.js +0 -1
  408. package/payload/server/public/assets/useSubAccountSwitcher-CU-lOxTd.js +0 -9
  409. package/payload/server/public/assets/useVoiceRecorder-azG8zSUI.js +0 -2
  410. package/payload/server/public/assets/wardley-RL74JXVD-DgMn-9ZJ.js +0 -1
  411. /package/payload/platform/plugins/cloudflare/skills/data-portal/{template/schema.sql → schema.sql} +0 -0
  412. /package/payload/platform/plugins/cloudflare/skills/data-portal/template/{public/index.html → index.html} +0 -0
  413. /package/payload/platform/plugins/cloudflare/skills/data-portal/template/{public/portal.js → portal.js} +0 -0
  414. /package/payload/server/public/assets/{_baseFor-D3BqAOBr.js → _baseFor-CMWvz0uA.js} +0 -0
  415. /package/payload/server/public/assets/{array-iYP9fzJp.js → array-Cny_NT6T.js} +0 -0
  416. /package/payload/server/public/assets/{chunk-Ck5AOu5v.js → chunk-TPjIAO9U.js} +0 -0
  417. /package/payload/server/public/assets/{cytoscape.esm-BQSBzPsB.js → cytoscape.esm-De0CCmB2.js} +0 -0
  418. /package/payload/server/public/assets/{defaultLocale-BrdA1gDi.js → defaultLocale-BahRht8U.js} +0 -0
  419. /package/payload/server/public/assets/{init-Bx0S6pkp.js → init-DoIvjoJ_.js} +0 -0
  420. /package/payload/server/public/assets/{katex-BsiH_a6d.js → katex-Bfn1OZEl.js} +0 -0
  421. /package/payload/server/public/assets/{path-BLSgTy3w.js → path-CTezU8L5.js} +0 -0
  422. /package/payload/server/public/assets/{preload-helper-ClBMtM1g.js → preload-helper-CT1Z6Pdu.js} +0 -0
  423. /package/payload/server/public/assets/{rough.esm-BMOtDaaS.js → rough.esm-BJo4pvXu.js} +0 -0
@@ -0,0 +1,48 @@
1
+ import { describe, it, expect } from 'vitest'
2
+ import { computeOutstanding, applyPayment } from '../balance.js'
3
+
4
+ describe('computeOutstanding', () => {
5
+ it('reports the full total when nothing is applied', () => {
6
+ expect(computeOutstanding(6400, [], [])).toBe(6400)
7
+ })
8
+ it('leaves the exact remainder after a part payment', () => {
9
+ expect(computeOutstanding(6400, [2000], [])).toBe(4400)
10
+ })
11
+ it('reaches zero when payments sum to the total', () => {
12
+ expect(computeOutstanding(6400, [2000, 4400], [])).toBe(0)
13
+ })
14
+ it('treats a credit identically to a payment', () => {
15
+ expect(computeOutstanding(6400, [], [2000])).toBe(4400)
16
+ expect(computeOutstanding(6400, [1000], [1000])).toBe(computeOutstanding(6400, [2000], []))
17
+ })
18
+ it('does not drift on repeating decimals', () => {
19
+ expect(computeOutstanding(100, [33.33, 33.33, 33.34], [])).toBe(0)
20
+ })
21
+ })
22
+
23
+ describe('applyPayment', () => {
24
+ it('accepts a payment within the outstanding amount', () => {
25
+ expect(applyPayment(6400, [], [], 2000)).toEqual({ accepted: true, outstandingAfter: 4400 })
26
+ })
27
+ it('accepts a payment that exactly settles the invoice', () => {
28
+ expect(applyPayment(6400, [2000], [], 4400)).toEqual({ accepted: true, outstandingAfter: 0 })
29
+ })
30
+ it('rejects over-application and reports the remaining amount', () => {
31
+ expect(applyPayment(6400, [2000], [], 5000)).toEqual({
32
+ accepted: false,
33
+ reason: 'over-application',
34
+ remaining: 4400,
35
+ })
36
+ })
37
+ it('rejects any payment against a zero-total invoice', () => {
38
+ expect(applyPayment(0, [], [], 1)).toEqual({
39
+ accepted: false,
40
+ reason: 'over-application',
41
+ remaining: 0,
42
+ })
43
+ })
44
+ it('rejects a zero or negative candidate', () => {
45
+ expect(applyPayment(6400, [], [], 0).accepted).toBe(false)
46
+ expect(applyPayment(6400, [], [], -5).accepted).toBe(false)
47
+ })
48
+ })
@@ -0,0 +1,87 @@
1
+ import { describe, it, expect } from 'vitest'
2
+ import { reconcileLedger, formatCensusLine, LEDGER_INDEX_NAMES } from '../reconcile.js'
3
+
4
+ const clean = {
5
+ invoices: 12,
6
+ payments: 20,
7
+ orphanPayments: 0,
8
+ storedBalanceProps: 0,
9
+ paymentKeys: [],
10
+ declaredNotLive: [],
11
+ }
12
+ const NOW = Date.parse('2026-07-18T00:00:00Z')
13
+
14
+ describe('reconcileLedger', () => {
15
+ it('does not alarm on a clean ledger', () => {
16
+ expect(reconcileLedger(clean, NOW).alarm).toBe(false)
17
+ })
18
+ it('alarms on an orphan payment', () => {
19
+ expect(reconcileLedger({ ...clean, orphanPayments: 1 }, NOW).alarm).toBe(true)
20
+ })
21
+ it('alarms on a stored balance property', () => {
22
+ expect(reconcileLedger({ ...clean, storedBalanceProps: 1 }, NOW).alarm).toBe(true)
23
+ })
24
+ it('alarms when a declared label is not live', () => {
25
+ expect(reconcileLedger({ ...clean, declaredNotLive: ['CashEntry'] }, NOW).alarm).toBe(true)
26
+ })
27
+ it('counts n-1 duplicates per colliding group', () => {
28
+ const paymentKeys = [
29
+ { invoiceId: 'i1', paymentId: 'p1', amount: 100, date: '2026-07-01' },
30
+ { invoiceId: 'i1', paymentId: 'p2', amount: 100, date: '2026-07-01' },
31
+ { invoiceId: 'i1', paymentId: 'p3', amount: 100, date: '2026-07-01' },
32
+ ]
33
+ const f = reconcileLedger({ ...clean, paymentKeys }, NOW)
34
+ expect(f.suspectDuplicates).toBe(2)
35
+ expect(f.alarm).toBe(true)
36
+ })
37
+ it('does not flag the same paymentId seen once', () => {
38
+ const paymentKeys = [{ invoiceId: 'i1', paymentId: 'p1', amount: 100, date: '2026-07-01' }]
39
+ expect(reconcileLedger({ ...clean, paymentKeys }, NOW).suspectDuplicates).toBe(0)
40
+ })
41
+ it('does not flag two genuine same-day payments of different amounts', () => {
42
+ const paymentKeys = [
43
+ { invoiceId: 'i1', paymentId: 'p1', amount: 100, date: '2026-07-01' },
44
+ { invoiceId: 'i1', paymentId: 'p2', amount: 250, date: '2026-07-01' },
45
+ ]
46
+ expect(reconcileLedger({ ...clean, paymentKeys }, NOW).suspectDuplicates).toBe(0)
47
+ })
48
+ it('does not flag the same key repeated under one paymentId', () => {
49
+ const paymentKeys = [
50
+ { invoiceId: 'i1', paymentId: 'p1', amount: 100, date: '2026-07-01' },
51
+ { invoiceId: 'i1', paymentId: 'p1', amount: 100, date: '2026-07-01' },
52
+ ]
53
+ expect(reconcileLedger({ ...clean, paymentKeys }, NOW).suspectDuplicates).toBe(0)
54
+ })
55
+ it('separates groups by invoice', () => {
56
+ const paymentKeys = [
57
+ { invoiceId: 'i1', paymentId: 'p1', amount: 100, date: '2026-07-01' },
58
+ { invoiceId: 'i2', paymentId: 'p2', amount: 100, date: '2026-07-01' },
59
+ ]
60
+ expect(reconcileLedger({ ...clean, paymentKeys }, NOW).suspectDuplicates).toBe(0)
61
+ })
62
+
63
+ it('formats one census line carrying every counter', () => {
64
+ const line = formatCensusLine(reconcileLedger(clean, NOW))
65
+ for (const field of [
66
+ '[ledger-census]',
67
+ 'invoices=12',
68
+ 'payments=20',
69
+ 'orphanPayments=0',
70
+ 'storedBalanceProps=0',
71
+ 'suspectDuplicates=0',
72
+ 'declaredNotLive=none',
73
+ ]) {
74
+ expect(line).toContain(field)
75
+ }
76
+ })
77
+
78
+ it('names the missing indexes in the line when some are absent', () => {
79
+ const f = reconcileLedger({ ...clean, declaredNotLive: ['cash_entry_account'] }, NOW)
80
+ expect(formatCensusLine(f)).toContain('declaredNotLive=cash_entry_account')
81
+ })
82
+
83
+ it('declares the six ledger index names schema.cypher creates', () => {
84
+ expect(LEDGER_INDEX_NAMES).toHaveLength(6)
85
+ expect(LEDGER_INDEX_NAMES).toContain('cash_entry_account_entryid_unique')
86
+ })
87
+ })
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Task 1771 — receivables and payables ageing.
3
+ *
4
+ * Buckets are inclusive-lower on whole days past the due date: 0-30 is
5
+ * `current`, 31-60, 61-90, and 91 and above is `d90plus`. An invoice not yet
6
+ * due has a negative age, which clamps to 0 and lands in `current`.
7
+ *
8
+ * Every bucket is a summed money amount, not a count of invoices. Summing is
9
+ * done in minor units so a statement of many decimal amounts does not drift.
10
+ */
11
+ import { toMinor, fromMinor } from './money.js'
12
+
13
+ const DAY_MS = 86_400_000
14
+
15
+ export interface OpenInvoice {
16
+ ref: string
17
+ dueDateMs: number
18
+ outstanding: number
19
+ }
20
+
21
+ export interface AgeBuckets {
22
+ current: number
23
+ d31to60: number
24
+ d61to90: number
25
+ d90plus: number
26
+ total: number
27
+ }
28
+
29
+ export function ageBuckets(invoices: readonly OpenInvoice[], now: number): AgeBuckets {
30
+ let current = 0
31
+ let d31to60 = 0
32
+ let d61to90 = 0
33
+ let d90plus = 0
34
+
35
+ for (const inv of invoices) {
36
+ const ageDays = Math.max(0, Math.floor((now - inv.dueDateMs) / DAY_MS))
37
+ const minor = toMinor(inv.outstanding)
38
+ if (ageDays <= 30) current += minor
39
+ else if (ageDays <= 60) d31to60 += minor
40
+ else if (ageDays <= 90) d61to90 += minor
41
+ else d90plus += minor
42
+ }
43
+
44
+ return {
45
+ current: fromMinor(current),
46
+ d31to60: fromMinor(d31to60),
47
+ d61to90: fromMinor(d61to90),
48
+ d90plus: fromMinor(d90plus),
49
+ total: fromMinor(current + d31to60 + d61to90 + d90plus),
50
+ }
51
+ }
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Task 1771 — the outstanding invariant, defined exactly once.
3
+ *
4
+ * Nothing stores a balance. `outstanding`, `balance` and `amountPaid` are
5
+ * forbidden properties on :Invoice and :InboundInvoice (schema-base.md
6
+ * § Forbidden Properties), because a stored figure drifts silently the moment
7
+ * a payment lands by any other path. Every consumer computes from this module.
8
+ */
9
+ import { toMinor, fromMinor } from './money.js'
10
+
11
+ export type ApplyResult =
12
+ | { accepted: true; outstandingAfter: number }
13
+ | { accepted: false; reason: 'over-application'; remaining: number }
14
+
15
+ /**
16
+ * outstanding = totalPaymentDue - sum(payments) - sum(credits).
17
+ *
18
+ * A credit reduces what is owed exactly as a payment does; the two differ in
19
+ * provenance, not arithmetic, which is why they are summed together here.
20
+ */
21
+ export function computeOutstanding(
22
+ totalPaymentDue: number,
23
+ payments: readonly number[],
24
+ credits: readonly number[],
25
+ ): number {
26
+ const applied = [...payments, ...credits].reduce((acc, a) => acc + toMinor(a), 0)
27
+ return fromMinor(toMinor(totalPaymentDue) - applied)
28
+ }
29
+
30
+ /**
31
+ * Validates one candidate payment or credit against what is still outstanding.
32
+ *
33
+ * Never clamps. A candidate that would take applied-to-date above the invoice
34
+ * total is rejected outright and the caller reports it, rather than silently
35
+ * recording a figure that does not reconcile. A non-positive candidate is
36
+ * rejected for the same reason: it is not a payment.
37
+ */
38
+ export function applyPayment(
39
+ totalPaymentDue: number,
40
+ payments: readonly number[],
41
+ credits: readonly number[],
42
+ candidate: number,
43
+ ): ApplyResult {
44
+ const remaining = computeOutstanding(totalPaymentDue, payments, credits)
45
+ if (toMinor(candidate) <= 0) {
46
+ return { accepted: false, reason: 'over-application', remaining }
47
+ }
48
+ if (toMinor(candidate) > toMinor(remaining)) {
49
+ return { accepted: false, reason: 'over-application', remaining }
50
+ }
51
+ return { accepted: true, outstandingAfter: fromMinor(toMinor(remaining) - toMinor(candidate)) }
52
+ }
@@ -0,0 +1,4 @@
1
+ export * from './money.js'
2
+ export * from './balance.js'
3
+ export * from './ageing.js'
4
+ export * from './reconcile.js'
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Task 1771 — money is compared and summed in integer minor units so the
3
+ * outstanding invariant is exact and never subject to float drift. Graph
4
+ * storage is unchanged: `amount` and `totalPaymentDue` stay decimal numbers,
5
+ * matching the existing JobLogic-exact construction rows. Conversion happens
6
+ * here, at the boundary, so no stored value changes shape.
7
+ */
8
+
9
+ /** Decimal major units to integer minor units. */
10
+ export function toMinor(n: number): number {
11
+ return Math.round(n * 100)
12
+ }
13
+
14
+ /** Integer minor units back to decimal major units. */
15
+ export function fromMinor(n: number): number {
16
+ return n / 100
17
+ }
@@ -0,0 +1,109 @@
1
+ /**
2
+ * Task 1771 — the ledger census finding.
3
+ *
4
+ * Four of the ledger's failure modes emit no event of their own: a write that
5
+ * was narrated but never landed, a payment with no parent invoice, a stored
6
+ * balance property that has drifted, and a duplicate payment created by a
7
+ * non-deterministic paymentId. None of them can be caught by logging an
8
+ * action, because no action occurs. This reconcile is the substitute, and it
9
+ * is called from two places: the `ledger-reconcile` tool (on demand) and the
10
+ * standing interval in the UI server. Both share this function so the two
11
+ * forms cannot report different things.
12
+ *
13
+ * Pure: no I/O, and `now` is a parameter rather than a Date.now() call.
14
+ */
15
+
16
+ /** The index names schema.cypher declares for the ledger. Their absence from a
17
+ * live database means schema.cypher was edited but seed-neo4j.sh never re-ran
18
+ * on that install, which emits nothing on its own. Defined here because both
19
+ * the on-demand tool and the standing census check the same list; two copies
20
+ * would let them disagree about whether the schema is applied. */
21
+ export const LEDGER_INDEX_NAMES: readonly string[] = [
22
+ "invoice_account_confirmation_unique",
23
+ "cash_entry_account_entryid_unique",
24
+ "cash_entry_account",
25
+ "invoice_line_account",
26
+ "invoice_payment_account",
27
+ "credit_account",
28
+ ]
29
+
30
+ export interface PaymentKey {
31
+ invoiceId: string
32
+ paymentId: string
33
+ amount: number
34
+ date: string
35
+ }
36
+
37
+ export interface LedgerCensusRows {
38
+ invoices: number
39
+ payments: number
40
+ orphanPayments: number
41
+ storedBalanceProps: number
42
+ paymentKeys: readonly PaymentKey[]
43
+ declaredNotLive: readonly string[]
44
+ }
45
+
46
+ export interface LedgerCensusFinding {
47
+ invoices: number
48
+ payments: number
49
+ orphanPayments: number
50
+ storedBalanceProps: number
51
+ suspectDuplicates: number
52
+ declaredNotLive: readonly string[]
53
+ alarm: boolean
54
+ }
55
+
56
+ /**
57
+ * Counts payments that share (invoiceId, amount, date) across two or more
58
+ * distinct paymentId values. For a group of n distinct ids the contribution is
59
+ * n-1, because exactly one member of the group is the legitimate payment.
60
+ *
61
+ * Grouping on distinct ids, not on row count, is what keeps a genuine retry
62
+ * quiet: a retry that MERGEs onto the same deterministic paymentId appears
63
+ * twice in the rows but once in the id set, and is not a duplicate.
64
+ */
65
+ function countSuspectDuplicates(keys: readonly PaymentKey[]): number {
66
+ const groups = new Map<string, Set<string>>()
67
+ for (const k of keys) {
68
+ const composite = `${k.invoiceId}|${k.amount}|${k.date}`
69
+ const ids = groups.get(composite) ?? new Set<string>()
70
+ ids.add(k.paymentId)
71
+ groups.set(composite, ids)
72
+ }
73
+ let total = 0
74
+ for (const ids of groups.values()) total += Math.max(0, ids.size - 1)
75
+ return total
76
+ }
77
+
78
+ export function reconcileLedger(rows: LedgerCensusRows, _now: number): LedgerCensusFinding {
79
+ const suspectDuplicates = countSuspectDuplicates(rows.paymentKeys)
80
+ const alarm =
81
+ rows.orphanPayments > 0 ||
82
+ rows.storedBalanceProps > 0 ||
83
+ suspectDuplicates > 0 ||
84
+ rows.declaredNotLive.length > 0
85
+
86
+ return {
87
+ invoices: rows.invoices,
88
+ payments: rows.payments,
89
+ orphanPayments: rows.orphanPayments,
90
+ storedBalanceProps: rows.storedBalanceProps,
91
+ suspectDuplicates,
92
+ declaredNotLive: rows.declaredNotLive,
93
+ alarm,
94
+ }
95
+ }
96
+
97
+ /**
98
+ * The one `[ledger-census]` line format. Shared by the on-demand tool and the
99
+ * standing interval so a log search finds both, and so a new counter cannot be
100
+ * added to one emission and missed by the other.
101
+ */
102
+ export function formatCensusLine(f: LedgerCensusFinding): string {
103
+ return (
104
+ `[ledger-census] invoices=${f.invoices} payments=${f.payments} ` +
105
+ `orphanPayments=${f.orphanPayments} storedBalanceProps=${f.storedBalanceProps} ` +
106
+ `suspectDuplicates=${f.suspectDuplicates} ` +
107
+ `declaredNotLive=${f.declaredNotLive.length === 0 ? "none" : f.declaredNotLive.join(",")}`
108
+ )
109
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "extends": "../../tsconfig.base.json",
3
+ "compilerOptions": { "outDir": "dist", "rootDir": "src" },
4
+ "include": ["src"],
5
+ "exclude": ["src/__tests__"]
6
+ }
@@ -467,6 +467,7 @@ FOR (n:LocalBusiness|Service|PriceSpecification|OpeningHoursSpecification|Organi
467
467
  |Listing|Property|Viewing|Offer|PostalAddress|Sale|Chain|ChainLink
468
468
  |Job|Quote|QuoteLine|Valuation|Milestone|VariationNote|Invoice|InboundInvoice|JobCost|WhatsAppGroup
469
469
  |Customer|Site|Asset|Visit|Part|PurchaseOrder|PpmContract|Contact|PurchaseOrderLine|InvoiceLine|InvoicePayment|Credit|Note
470
+ |CashEntry
470
471
  |Person|UserProfile|Preference|AdminUser|AccessGrant
471
472
  |KnowledgeDocument|ConversationArchive|Section|Chunk|DigitalDocument|CreativeWork|Question|FAQPage|DefinedTerm|Review|ImageObject
472
473
  |Conversation|AdminConversation|PublicConversation|Message|UserMessage|AssistantMessage|ToolCall
@@ -1563,9 +1564,24 @@ FOR (ii:InboundInvoice) REQUIRE (ii.accountId, ii.supplier, ii.confirmationNumbe
1563
1564
  CREATE INDEX inbound_invoice_account IF NOT EXISTS
1564
1565
  FOR (ii:InboundInvoice) ON (ii.accountId);
1565
1566
 
1567
+ // Task 1771 — :Invoice gained the uniqueness constraint schema-base.md already
1568
+ // claimed as its natural key but never declared, so a second write of the same
1569
+ // confirmationNumber merges rather than creating a rival invoice that would
1570
+ // split a payment history in two.
1571
+ CREATE CONSTRAINT invoice_account_confirmation_unique IF NOT EXISTS
1572
+ FOR (i:Invoice) REQUIRE (i.accountId, i.confirmationNumber) IS UNIQUE;
1573
+
1566
1574
  CREATE INDEX invoice_account IF NOT EXISTS
1567
1575
  FOR (i:Invoice) ON (i.accountId);
1568
1576
 
1577
+ // :CashEntry (Task 1771) — one movement of money not attached to any invoice.
1578
+ // Direction lives on the node because there is no parent label to derive it
1579
+ // from, unlike :InvoicePayment.
1580
+ CREATE CONSTRAINT cash_entry_account_entryid_unique IF NOT EXISTS
1581
+ FOR (ce:CashEntry) REQUIRE (ce.accountId, ce.entryId) IS UNIQUE;
1582
+ CREATE INDEX cash_entry_account IF NOT EXISTS
1583
+ FOR (ce:CashEntry) ON (ce.accountId);
1584
+
1569
1585
  // :CheckIn -> :JobCost (Task 1084). A logged labour day is an incurred labour
1570
1586
  // cost (costType:'labour'); JobCost also carries material/travel/mileage/
1571
1587
  // expense/subcontractor/scheduleOfRates lines. Clean natural key (costId).
@@ -6,8 +6,8 @@
6
6
  "services/*"
7
7
  ],
8
8
  "scripts": {
9
- "build": "tsc -p lib/models/tsconfig.json && tsc -p lib/mcp-spawn-tee/tsconfig.json && tsc -p lib/mcp-lifeline/tsconfig.json && tsc -p lib/mcp-eager/tsconfig.json && tsc -p lib/account-enumeration/tsconfig.json && tsc -p lib/account-scope/tsconfig.json && tsc -p lib/graph-write/tsconfig.json && tsc -p lib/embed-client/tsconfig.json && tsc -p lib/graph-mcp/tsconfig.json && tsc -p lib/graph-trash/tsconfig.json && tsc -p lib/active-rules/tsconfig.json && tsc -p lib/admin-conversation-purge/tsconfig.json && tsc -p lib/graph-search/tsconfig.json && tsc -p lib/graph-style/tsconfig.json && tsc -p lib/device-url/tsconfig.json && tsc -p lib/brand-templating/tsconfig.json && tsc -p lib/entitlement/tsconfig.json && tsc -p lib/task-secrets/tsconfig.json && tsc -p lib/admins-write/tsconfig.json && tsc -p lib/admin-access-password/tsconfig.json && tsc -p lib/persistent-components/tsconfig.json && tsc -p lib/require-port-env/tsconfig.json && tsc -p lib/aeo-llms-txt-writer/tsconfig.json && tsc -p lib/obsidian-parser/tsconfig.json && tsc -p lib/storage-broker/tsconfig.json && tsc -p services/claude-session-manager/tsconfig.json && tsc -p services/whatsapp-channel/tsconfig.json && tsc -p services/webchat-channel/tsconfig.json && tsc -p services/telegram-channel/tsconfig.json && NODE_OPTIONS='--max-old-space-size=8192' tsc -b plugins/*/mcp/tsconfig.json",
10
- "build:lib": "tsc -p lib/models/tsconfig.json && tsc -p lib/mcp-spawn-tee/tsconfig.json && tsc -p lib/mcp-lifeline/tsconfig.json && tsc -p lib/mcp-eager/tsconfig.json && tsc -p lib/account-enumeration/tsconfig.json && tsc -p lib/account-scope/tsconfig.json && tsc -p lib/graph-write/tsconfig.json && tsc -p lib/embed-client/tsconfig.json && tsc -p lib/graph-mcp/tsconfig.json && tsc -p lib/graph-trash/tsconfig.json && tsc -p lib/active-rules/tsconfig.json && tsc -p lib/admin-conversation-purge/tsconfig.json && tsc -p lib/graph-search/tsconfig.json && tsc -p lib/graph-style/tsconfig.json && tsc -p lib/device-url/tsconfig.json && tsc -p lib/brand-templating/tsconfig.json && tsc -p lib/entitlement/tsconfig.json && tsc -p lib/task-secrets/tsconfig.json && tsc -p lib/admins-write/tsconfig.json && tsc -p lib/admin-access-password/tsconfig.json && tsc -p lib/persistent-components/tsconfig.json && tsc -p lib/require-port-env/tsconfig.json && tsc -p lib/aeo-llms-txt-writer/tsconfig.json && tsc -p lib/obsidian-parser/tsconfig.json && tsc -p lib/storage-broker/tsconfig.json",
9
+ "build": "tsc -p lib/models/tsconfig.json && tsc -p lib/mcp-spawn-tee/tsconfig.json && tsc -p lib/mcp-lifeline/tsconfig.json && tsc -p lib/mcp-eager/tsconfig.json && tsc -p lib/account-enumeration/tsconfig.json && tsc -p lib/account-scope/tsconfig.json && tsc -p lib/graph-write/tsconfig.json && tsc -p lib/ledger-core/tsconfig.json && tsc -p lib/embed-client/tsconfig.json && tsc -p lib/graph-mcp/tsconfig.json && tsc -p lib/graph-trash/tsconfig.json && tsc -p lib/active-rules/tsconfig.json && tsc -p lib/admin-conversation-purge/tsconfig.json && tsc -p lib/graph-search/tsconfig.json && tsc -p lib/graph-style/tsconfig.json && tsc -p lib/device-url/tsconfig.json && tsc -p lib/brand-templating/tsconfig.json && tsc -p lib/entitlement/tsconfig.json && tsc -p lib/task-secrets/tsconfig.json && tsc -p lib/admins-write/tsconfig.json && tsc -p lib/admin-access-password/tsconfig.json && tsc -p lib/persistent-components/tsconfig.json && tsc -p lib/require-port-env/tsconfig.json && tsc -p lib/aeo-llms-txt-writer/tsconfig.json && tsc -p lib/obsidian-parser/tsconfig.json && tsc -p lib/storage-broker/tsconfig.json && tsc -p services/claude-session-manager/tsconfig.json && tsc -p services/whatsapp-channel/tsconfig.json && tsc -p services/webchat-channel/tsconfig.json && tsc -p services/telegram-channel/tsconfig.json && NODE_OPTIONS='--max-old-space-size=8192' tsc -b plugins/*/mcp/tsconfig.json",
10
+ "build:lib": "tsc -p lib/models/tsconfig.json && tsc -p lib/mcp-spawn-tee/tsconfig.json && tsc -p lib/mcp-lifeline/tsconfig.json && tsc -p lib/mcp-eager/tsconfig.json && tsc -p lib/account-enumeration/tsconfig.json && tsc -p lib/account-scope/tsconfig.json && tsc -p lib/graph-write/tsconfig.json && tsc -p lib/ledger-core/tsconfig.json && tsc -p lib/embed-client/tsconfig.json && tsc -p lib/graph-mcp/tsconfig.json && tsc -p lib/graph-trash/tsconfig.json && tsc -p lib/active-rules/tsconfig.json && tsc -p lib/admin-conversation-purge/tsconfig.json && tsc -p lib/graph-search/tsconfig.json && tsc -p lib/graph-style/tsconfig.json && tsc -p lib/device-url/tsconfig.json && tsc -p lib/brand-templating/tsconfig.json && tsc -p lib/entitlement/tsconfig.json && tsc -p lib/task-secrets/tsconfig.json && tsc -p lib/admins-write/tsconfig.json && tsc -p lib/admin-access-password/tsconfig.json && tsc -p lib/persistent-components/tsconfig.json && tsc -p lib/require-port-env/tsconfig.json && tsc -p lib/aeo-llms-txt-writer/tsconfig.json && tsc -p lib/obsidian-parser/tsconfig.json && tsc -p lib/storage-broker/tsconfig.json",
11
11
  "gen:canonical-tools": "node scripts/generate-canonical-tool-names.mjs",
12
12
  "build:memory": "tsc -p plugins/memory/mcp/tsconfig.json",
13
13
  "build:contacts": "tsc -p plugins/contacts/mcp/tsconfig.json",
@@ -74,6 +74,11 @@
74
74
  "source": "./graph-viewer",
75
75
  "version": "0.1.0"
76
76
  },
77
+ {
78
+ "name": "ledger",
79
+ "source": "./ledger",
80
+ "version": "0.1.0"
81
+ },
77
82
  {
78
83
  "name": "linkedin-extension",
79
84
  "source": "./linkedin-extension",
@@ -9,6 +9,7 @@ google
9
9
  graph
10
10
  graph-viewer
11
11
  joblogic
12
+ ledger
12
13
  memory
13
14
  outlook
14
15
  quickbooks
@@ -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:4f7717f06a4d87172809793ad9a02cfe57e3b4f6ec4b2f0f2dbff39792ef5456
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
@@ -3138,6 +3186,54 @@ A 0.000 reading that persists across many polls while `top` shows load
3138
3186
  is the delta-cache regression signature (the single-flight promise was
3139
3187
  not released).
3140
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
+
3141
3237
  ## Sidebar surfaces
3142
3238
 
3143
3239
  The sidebar is the entire left column of the admin UI. Its full layout,
@@ -3973,7 +4069,7 @@ This tool is read-only and available to both public and admin agents.
3973
4069
 
3974
4070
  ### When conversations are created
3975
4071
 
3976
- `: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.
3977
4073
 
3978
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.
3979
4075