@rubytech/create-maxy-code 0.1.500 → 0.1.502

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 (194) hide show
  1. package/package.json +1 -1
  2. package/payload/platform/docs/superpowers/plans/2026-07-25-task-1974-uploads-intake-inbox.md +235 -0
  3. package/payload/platform/docs/superpowers/plans/2026-07-25-task-1976-intra-folder-hygiene.md +215 -0
  4. package/payload/platform/lib/graph-style/dist/index.d.ts +9 -0
  5. package/payload/platform/lib/graph-style/dist/index.d.ts.map +1 -1
  6. package/payload/platform/lib/graph-style/dist/index.js +152 -6
  7. package/payload/platform/lib/graph-style/dist/index.js.map +1 -1
  8. package/payload/platform/lib/graph-style/src/__tests__/caption-coverage.test.ts +69 -0
  9. package/payload/platform/lib/graph-style/src/__tests__/coverage-source.ts +73 -0
  10. package/payload/platform/lib/graph-style/src/__tests__/icons.test.ts +20 -3
  11. package/payload/platform/lib/graph-style/src/index.ts +166 -5
  12. package/payload/platform/plugins/admin/PLUGIN.md +1 -0
  13. package/payload/platform/plugins/admin/skills/agent-builder/SKILL.md +15 -0
  14. package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +52 -2
  15. package/payload/platform/plugins/admin/skills/summarise/SKILL.md +37 -0
  16. package/payload/platform/plugins/admin/skills/whats-new/SKILL.md +7 -0
  17. package/payload/platform/plugins/docs/references/admin-ui.md +50 -0
  18. package/payload/platform/plugins/docs/references/internals.md +1 -1
  19. package/payload/platform/plugins/docs/references/summarise-skill.md +7 -0
  20. package/payload/platform/templates/account-schema/SCHEMA.md +36 -1
  21. package/payload/platform/templates/specialists/agents/data-manager.md +4 -2
  22. package/payload/server/public/activity.html +6 -6
  23. package/payload/server/public/assets/AdminLoginScreens-Bv_bKKeL.js +1 -0
  24. package/payload/server/public/assets/AdminShell-Gq8DU_ig.js +2 -0
  25. package/payload/server/public/assets/{activity-CKUYOLd2.js → activity-DZFYDHNA.js} +1 -1
  26. package/payload/server/public/assets/admin-DLUOixl6.js +1 -0
  27. package/payload/server/public/assets/{arc-CpWWSum8.js → arc-CvlzUHOf.js} +1 -1
  28. package/payload/server/public/assets/architecture-YZFGNWBL-BQc779DH.js +1 -0
  29. package/payload/server/public/assets/{architectureDiagram-Q4EWVU46-D2EzaII0.js → architectureDiagram-Q4EWVU46-I1Hs0RJA.js} +1 -1
  30. package/payload/server/public/assets/{blockDiagram-DXYQGD6D-8kDpaNSZ.js → blockDiagram-DXYQGD6D-Df5jTGtl.js} +1 -1
  31. package/payload/server/public/assets/{bot-DESMpxSQ.js → bot-CrEhKW1l.js} +1 -1
  32. package/payload/server/public/assets/{browser-CpLq_DIo.js → browser-Bt4-LKLU.js} +1 -1
  33. package/payload/server/public/assets/{c4Diagram-AHTNJAMY-CbwmZpK3.js → c4Diagram-AHTNJAMY-B6WY5IDv.js} +1 -1
  34. package/payload/server/public/assets/{calendar-ClOwyuz-.js → calendar-D0qIvLUR.js} +1 -1
  35. package/payload/server/public/assets/channel-AqnC3SHu.js +1 -0
  36. package/payload/server/public/assets/chat-geSkx0j2.js +1 -0
  37. package/payload/server/public/assets/chevron-left-CQ8rcBoO.js +1 -0
  38. package/payload/server/public/assets/chevron-right-CSRSqblN.js +1 -0
  39. package/payload/server/public/assets/{chunk-2KRD3SAO-PFheZ9mw.js → chunk-2KRD3SAO-BsIIFDkc.js} +1 -1
  40. package/payload/server/public/assets/{chunk-336JU56O-CCFOp5VK.js → chunk-336JU56O-j9z1VtyC.js} +2 -2
  41. package/payload/server/public/assets/chunk-426QAEUC-CO02gU5d.js +1 -0
  42. package/payload/server/public/assets/{chunk-4BX2VUAB-Hz4JWOf2.js → chunk-4BX2VUAB-C9i7prxq.js} +1 -1
  43. package/payload/server/public/assets/{chunk-4TB4RGXK-4xeV0s85.js → chunk-4TB4RGXK-DH0QVGcv.js} +1 -1
  44. package/payload/server/public/assets/{chunk-55IACEB6-4QKgBpoh.js → chunk-55IACEB6-Dnd_YCPA.js} +1 -1
  45. package/payload/server/public/assets/{chunk-5FUZZQ4R-ms3K6Z5z.js → chunk-5FUZZQ4R-D5sSz3yN.js} +1 -1
  46. package/payload/server/public/assets/{chunk-5PVQY5BW-CIC4GIGi.js → chunk-5PVQY5BW-CJrwWjAT.js} +1 -1
  47. package/payload/server/public/assets/{chunk-67CJDMHE-B-XrWVmV.js → chunk-67CJDMHE-SjP6_hUw.js} +1 -1
  48. package/payload/server/public/assets/{chunk-7N4EOEYR-C7ECOUYQ.js → chunk-7N4EOEYR-fAuhVJD0.js} +1 -1
  49. package/payload/server/public/assets/{chunk-AA7GKIK3-DOZ6Fxwk.js → chunk-AA7GKIK3-DZxa7KVg.js} +1 -1
  50. package/payload/server/public/assets/{chunk-BSJP7CBP-4cJ_b0c6.js → chunk-BSJP7CBP-DjeG3eQ-.js} +1 -1
  51. package/payload/server/public/assets/{chunk-CIAEETIT--mhZipWM.js → chunk-CIAEETIT-RYQULNJm.js} +1 -1
  52. package/payload/server/public/assets/{chunk-EDXVE4YY-jZpVwob_.js → chunk-EDXVE4YY-Cahapnon.js} +1 -1
  53. package/payload/server/public/assets/{chunk-ENJZ2VHE-Nn-p-xIA.js → chunk-ENJZ2VHE-1146X1L4.js} +1 -1
  54. package/payload/server/public/assets/{chunk-FMBD7UC4-DyNpcKOS.js → chunk-FMBD7UC4-BCFumtcP.js} +1 -1
  55. package/payload/server/public/assets/{chunk-FOC6F5B3-C0P8q1uz.js → chunk-FOC6F5B3-CkrtX8Zt.js} +1 -1
  56. package/payload/server/public/assets/{chunk-ICPOFSXX-CdXQ5SfI.js → chunk-ICPOFSXX-Ba1y3DaW.js} +2 -2
  57. package/payload/server/public/assets/{chunk-K5T4RW27-BVZJKKsT.js → chunk-K5T4RW27-CF-PI3uQ.js} +1 -1
  58. package/payload/server/public/assets/{chunk-KGLVRYIC-QlVWTe0V.js → chunk-KGLVRYIC-SFFC6ZC-.js} +1 -1
  59. package/payload/server/public/assets/{chunk-LIHQZDEY-CpGVZutP.js → chunk-LIHQZDEY-DbOLEP4R.js} +1 -1
  60. package/payload/server/public/assets/{chunk-ORNJ4GCN-WVcS6fUC.js → chunk-ORNJ4GCN-VB963bkN.js} +1 -1
  61. package/payload/server/public/assets/{chunk-OYMX7WX6-B7hiSTed.js → chunk-OYMX7WX6-DWYY1pQa.js} +1 -1
  62. package/payload/server/public/assets/chunk-QZHKN3VN-BiFMbK8t.js +1 -0
  63. package/payload/server/public/assets/{chunk-U2HBQHQK-DJPnZxGy.js → chunk-U2HBQHQK-DAH1diYP.js} +1 -1
  64. package/payload/server/public/assets/{chunk-X2U36JSP-DilBs1Yc.js → chunk-X2U36JSP-Dmw9coXz.js} +1 -1
  65. package/payload/server/public/assets/{chunk-XPW4576I-B5-uyrJ0.js → chunk-XPW4576I-DaAUON1-.js} +1 -1
  66. package/payload/server/public/assets/{chunk-YZCP3GAM-BNO4z0Mv.js → chunk-YZCP3GAM-BUcfkEef.js} +1 -1
  67. package/payload/server/public/assets/{chunk-ZZ45TVLE-BzHMQpip.js → chunk-ZZ45TVLE-BLD-vFRO.js} +1 -1
  68. package/payload/server/public/assets/classDiagram-6PBFFD2Q-BdRqzScB.js +1 -0
  69. package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-ChPLgHbS.js +1 -0
  70. package/payload/server/public/assets/clock-B7Ba8AZL.js +1 -0
  71. package/payload/server/public/assets/clone-TKtrjWBC.js +1 -0
  72. package/payload/server/public/assets/{cose-bilkent-S5V4N54A-3DWYmn1o.js → cose-bilkent-S5V4N54A-CoZX0uki.js} +1 -1
  73. package/payload/server/public/assets/{dagre-Cpy31xJW.js → dagre-Bmjs8zJA.js} +1 -1
  74. package/payload/server/public/assets/{dagre-KV5264BT-C8fyxHFd.js → dagre-KV5264BT-BZCP2wnq.js} +1 -1
  75. package/payload/server/public/assets/data-BdlGqjKM.js +1 -0
  76. package/payload/server/public/assets/{diagram-5BDNPKRD-ycyhnLvr.js → diagram-5BDNPKRD-8Nt9a4xX.js} +1 -1
  77. package/payload/server/public/assets/{diagram-G4DWMVQ6-BkeYicIt.js → diagram-G4DWMVQ6-vh5tcYqJ.js} +1 -1
  78. package/payload/server/public/assets/{diagram-MMDJMWI5-BcGZU-Q3.js → diagram-MMDJMWI5-DGf--jc8.js} +1 -1
  79. package/payload/server/public/assets/{diagram-TYMM5635-DuBDCFb_.js → diagram-TYMM5635-CsO7TIdw.js} +1 -1
  80. package/payload/server/public/assets/{dist-BYc_arNy.js → dist-cdnE4DqK.js} +1 -1
  81. package/payload/server/public/assets/{erDiagram-SMLLAGMA-DmkeOzvp.js → erDiagram-SMLLAGMA-4a78yV8_.js} +1 -1
  82. package/payload/server/public/assets/file-text-BpLcZKld.js +1 -0
  83. package/payload/server/public/assets/{flatten-QjnJ1Gcl.js → flatten-BtIc9jX7.js} +1 -1
  84. package/payload/server/public/assets/{flowDiagram-DWJPFMVM-CkE-pjuB.js → flowDiagram-DWJPFMVM-BoQBXs6t.js} +1 -1
  85. package/payload/server/public/assets/{ganttDiagram-T4ZO3ILL-Cjiy3kvE.js → ganttDiagram-T4ZO3ILL-CvzqwCC8.js} +1 -1
  86. package/payload/server/public/assets/gitGraph-7Q5UKJZL-DfcaTE9L.js +1 -0
  87. package/payload/server/public/assets/{gitGraphDiagram-UUTBAWPF-CsI4lKZm.js → gitGraphDiagram-UUTBAWPF-D_nBxay_.js} +1 -1
  88. package/payload/server/public/assets/graph-HB0199x-.js +51 -0
  89. package/payload/server/public/assets/graph-labels-Bx19ZYvJ.js +1 -0
  90. package/payload/server/public/assets/{graphlib-BkXxC6D5.js → graphlib-D0NdsKJI.js} +1 -1
  91. package/payload/server/public/assets/info-OMHHGYJF-CD-u6gw9.js +1 -0
  92. package/payload/server/public/assets/infoDiagram-42DDH7IO-C6gPQCvO.js +2 -0
  93. package/payload/server/public/assets/{isEmpty-BavJI9Cn.js → isEmpty-Bu4fZ8Kf.js} +1 -1
  94. package/payload/server/public/assets/{ishikawaDiagram-UXIWVN3A-D8lZtjRc.js → ishikawaDiagram-UXIWVN3A-auW-s0i0.js} +1 -1
  95. package/payload/server/public/assets/{journeyDiagram-VCZTEJTY-kLE7SiR_.js → journeyDiagram-VCZTEJTY-3Q6AWK4A.js} +1 -1
  96. package/payload/server/public/assets/{kanban-definition-6JOO6SKY-ove6JuD3.js → kanban-definition-6JOO6SKY-B4MPFp98.js} +1 -1
  97. package/payload/server/public/assets/{line-CZMSoxM6.js → line-26MdeG5Z.js} +1 -1
  98. package/payload/server/public/assets/{linear-Be8IK9hK.js → linear-Dq9s_bDO.js} +1 -1
  99. package/payload/server/public/assets/{maximize-2-BbzPm7o8.js → maximize-2-BhR-Ec21.js} +1 -1
  100. package/payload/server/public/assets/{mermaid-parser.core-BBADqbnp.js → mermaid-parser.core-vLEpOXgf.js} +2 -2
  101. package/payload/server/public/assets/{mermaid.core-mH3N7K0I.js → mermaid.core-Durk4m6D.js} +3 -3
  102. package/payload/server/public/assets/{mindmap-definition-QFDTVHPH-BjwwwWil.js → mindmap-definition-QFDTVHPH-DOIg-TyG.js} +1 -1
  103. package/payload/server/public/assets/operator-DejfJlvi.js +1 -0
  104. package/payload/server/public/assets/{ordinal-BHeIKG-K.js → ordinal-C2TEWKNu.js} +1 -1
  105. package/payload/server/public/assets/packet-4T2RLAQJ-BaLYeIrU.js +1 -0
  106. package/payload/server/public/assets/page-DomkejEB.js +32 -0
  107. package/payload/server/public/assets/page-FnCmAeHc.js +1 -0
  108. package/payload/server/public/assets/{pdf-render-fJz4vym9.js → pdf-render-C257cidb.js} +1 -1
  109. package/payload/server/public/assets/pie-ZZUOXDRM-CL-XJtk-.js +1 -0
  110. package/payload/server/public/assets/{pieDiagram-DEJITSTG-D-lXWWta.js → pieDiagram-DEJITSTG-DtgBHZfi.js} +1 -1
  111. package/payload/server/public/assets/{public-DqJjXR4p.js → public-Be_Galv6.js} +1 -1
  112. package/payload/server/public/assets/{quadrantDiagram-34T5L4WZ-13QyG_AO.js → quadrantDiagram-34T5L4WZ-BTc5Qn2y.js} +1 -1
  113. package/payload/server/public/assets/radar-PYXPWWZC-Cn-g6F_h.js +1 -0
  114. package/payload/server/public/assets/{reduce-Fm685to3.js → reduce-BGpN5lgn.js} +1 -1
  115. package/payload/server/public/assets/{requirementDiagram-MS252O5E-Bx45Bgn4.js → requirementDiagram-MS252O5E-BNgOO5YW.js} +1 -1
  116. package/payload/server/public/assets/{rotate-ccw-L42fL8ey.js → rotate-ccw-D9qOBLig.js} +1 -1
  117. package/payload/server/public/assets/routines-lGiYaMiX.js +1 -0
  118. package/payload/server/public/assets/{sankeyDiagram-XADWPNL6-Cj-Gq25v.js → sankeyDiagram-XADWPNL6-MyUATmVt.js} +1 -1
  119. package/payload/server/public/assets/{sequenceDiagram-FGHM5R23-B8RDRq93.js → sequenceDiagram-FGHM5R23-CBJrF5Vi.js} +1 -1
  120. package/payload/server/public/assets/skills-D9nWOqoz.js +1 -0
  121. package/payload/server/public/assets/{src-D3VYuW_Z.js → src-DekKlUCp.js} +1 -1
  122. package/payload/server/public/assets/{stateDiagram-FHFEXIEX-DoyFF1Kl.js → stateDiagram-FHFEXIEX-6wbUjn1Q.js} +1 -1
  123. package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-BdMdfBeC.js +1 -0
  124. package/payload/server/public/assets/{tasks-GNtvZL8F.js → tasks-Cyc1EfLA.js} +1 -1
  125. package/payload/server/public/assets/{time-entry-format-BreNpFLJ.js → time-entry-format-RkCW7hKu.js} +1 -1
  126. package/payload/server/public/assets/{timeline-definition-GMOUNBTQ-Df4RQXm6.js → timeline-definition-GMOUNBTQ-BHjTMCGY.js} +1 -1
  127. package/payload/server/public/assets/treeView-SZITEDCU-CmZoOGlC.js +1 -0
  128. package/payload/server/public/assets/treemap-W4RFUUIX-H27WFS-y.js +1 -0
  129. package/payload/server/public/assets/{triangle-alert-DumCWC08.js → triangle-alert-k8ulav-N.js} +1 -1
  130. package/payload/server/public/assets/{useCopyFeedback-DHCtz8qX.js → useCopyFeedback-DN1WA5vU.js} +1 -1
  131. package/payload/server/public/assets/useSubAccountSwitcher-fvBunpOC.css +1 -0
  132. package/payload/server/public/assets/useSubAccountSwitcher-iQGqNJhI.js +9 -0
  133. package/payload/server/public/assets/useVoiceRecorder-v--TIxaM.js +2 -0
  134. package/payload/server/public/assets/{vennDiagram-DHZGUBPP-D2o5og9l.js → vennDiagram-DHZGUBPP-yo3-cf0U.js} +1 -1
  135. package/payload/server/public/assets/wardley-RL74JXVD-DNpWIvU4.js +1 -0
  136. package/payload/server/public/assets/{wardleyDiagram-NUSXRM2D-BDueObbS.js → wardleyDiagram-NUSXRM2D-DZlyFzrg.js} +1 -1
  137. package/payload/server/public/assets/{wrench-iWkROE1V.js → wrench-CKhz8KnQ.js} +1 -1
  138. package/payload/server/public/assets/{xychartDiagram-5P7HB3ND-BTYs4Fgh.js → xychartDiagram-5P7HB3ND-B5HV5h80.js} +1 -1
  139. package/payload/server/public/browser.html +5 -5
  140. package/payload/server/public/calendar.html +8 -8
  141. package/payload/server/public/chat.html +16 -15
  142. package/payload/server/public/data.html +13 -12
  143. package/payload/server/public/graph.html +10 -10
  144. package/payload/server/public/index.html +17 -16
  145. package/payload/server/public/operator.html +17 -16
  146. package/payload/server/public/public.html +16 -15
  147. package/payload/server/public/routines.html +7 -7
  148. package/payload/server/public/skills.html +19 -0
  149. package/payload/server/public/tasks.html +7 -7
  150. package/payload/server/server.js +1157 -789
  151. package/payload/server/public/assets/AdminLoginScreens-CKg_Dlpv.js +0 -1
  152. package/payload/server/public/assets/AdminShell-BFVVkWFC.js +0 -2
  153. package/payload/server/public/assets/admin-wtSUU5N0.js +0 -1
  154. package/payload/server/public/assets/architecture-YZFGNWBL-D-AX2VuA.js +0 -1
  155. package/payload/server/public/assets/channel-DZ5sh6YQ.js +0 -1
  156. package/payload/server/public/assets/chat-DRuIxGtE.js +0 -1
  157. package/payload/server/public/assets/chevron-left-BzIg4in6.js +0 -1
  158. package/payload/server/public/assets/chevron-right-BcFSa9C3.js +0 -1
  159. package/payload/server/public/assets/chunk-426QAEUC-CcjBLp4a.js +0 -1
  160. package/payload/server/public/assets/chunk-QZHKN3VN-B2WjDYBE.js +0 -1
  161. package/payload/server/public/assets/classDiagram-6PBFFD2Q-CT3jNrze.js +0 -1
  162. package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-DJRsjAkC.js +0 -1
  163. package/payload/server/public/assets/clock-Dr3NGtjR.js +0 -1
  164. package/payload/server/public/assets/clone-FcEDnoMT.js +0 -1
  165. package/payload/server/public/assets/data-BdZCvyvN.js +0 -1
  166. package/payload/server/public/assets/gitGraph-7Q5UKJZL-D1uhnP9U.js +0 -1
  167. package/payload/server/public/assets/graph-B7niLM97.js +0 -51
  168. package/payload/server/public/assets/graph-labels-DykmXYHA.js +0 -1
  169. package/payload/server/public/assets/info-OMHHGYJF-oNm27GAV.js +0 -1
  170. package/payload/server/public/assets/infoDiagram-42DDH7IO-LxTftqyS.js +0 -2
  171. package/payload/server/public/assets/operator-DglDYmdV.js +0 -1
  172. package/payload/server/public/assets/packet-4T2RLAQJ-eQNVGiK3.js +0 -1
  173. package/payload/server/public/assets/page-C-5sTAE-.js +0 -32
  174. package/payload/server/public/assets/page-C8G5p4jr.js +0 -1
  175. package/payload/server/public/assets/pie-ZZUOXDRM-E1u-48rt.js +0 -1
  176. package/payload/server/public/assets/radar-PYXPWWZC-Cn2cGN90.js +0 -1
  177. package/payload/server/public/assets/routines-JOV6xdYh.js +0 -1
  178. package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-Bml-X4-H.js +0 -1
  179. package/payload/server/public/assets/treeView-SZITEDCU-CimabhXy.js +0 -1
  180. package/payload/server/public/assets/treemap-W4RFUUIX-Cg264rpM.js +0 -1
  181. package/payload/server/public/assets/useSubAccountSwitcher-Bc7XHr0L.js +0 -9
  182. package/payload/server/public/assets/useSubAccountSwitcher-DPHQJO7E.css +0 -1
  183. package/payload/server/public/assets/useVoiceRecorder-Cj-VZDKO.js +0 -2
  184. package/payload/server/public/assets/wardley-RL74JXVD-tbgfCg0E.js +0 -1
  185. /package/payload/server/public/assets/{_baseFor-Bvwvhrz4.js → _baseFor-Be3EKK3K.js} +0 -0
  186. /package/payload/server/public/assets/{array-D3d-WgFW.js → array-X0JlPOfd.js} +0 -0
  187. /package/payload/server/public/assets/{chunk-fQC6bEKA.js → chunk-CCr-iYLO.js} +0 -0
  188. /package/payload/server/public/assets/{cytoscape.esm-Cun-OM6a.js → cytoscape.esm-CI2IWMLB.js} +0 -0
  189. /package/payload/server/public/assets/{defaultLocale-CisKV5Ew.js → defaultLocale-B6RGN4id.js} +0 -0
  190. /package/payload/server/public/assets/{init-D9ZOVaZ3.js → init-BmUWJJHz.js} +0 -0
  191. /package/payload/server/public/assets/{katex-C-3ZDK8o.js → katex-BjZuzSQn.js} +0 -0
  192. /package/payload/server/public/assets/{path-BMOgQSLM.js → path-CoPyR7c2.js} +0 -0
  193. /package/payload/server/public/assets/{preload-helper-DL2DwvxV.js → preload-helper-BQrMkyGX.js} +0 -0
  194. /package/payload/server/public/assets/{rough.esm-BrTFrov0.js → rough.esm-jSt7UMyi.js} +0 -0
@@ -42,11 +42,12 @@ export const GRAPH_LABEL_COLOURS: Record<string, string> = {
42
42
  // JobLogic service-delivery ontology (Task 1084; supersedes the Task 650/773
43
43
  // homegrown set). Muted brick-red / earthy family — the service register sits
44
44
  // apart from people-terracotta, viewing-amber, and task-rose bands so
45
- // sitedesk legends read as their own group. Top-level (operator-entry)
46
- // labels only; child labels (QuoteLine, Milestone, Valuation, VariationNote,
47
- // JobCost, Contact, PurchaseOrderLine, InvoiceLine, InvoicePayment, Credit)
48
- // intentionally stay fallback-grey. :Engineer is an additional label on
49
- // Person and takes the Person colour; :Supplier is an additional label on
45
+ // sitedesk legends read as their own group. These are the top-level
46
+ // (operator-entry) labels; the child labels (QuoteLine, Milestone, Valuation,
47
+ // VariationNote, JobCost, Contact, PurchaseOrderLine, InvoiceLine,
48
+ // InvoicePayment, Credit) are coloured in the Task 1965 block appended at the
49
+ // end of this object, not here. :Engineer is an additional label on Person
50
+ // and takes the Person colour; :Supplier is an additional label on
50
51
  // Organization and takes the tobacco Organization colour. All hexes here are
51
52
  // pairwise-unique across the registry.
52
53
  Job: '#8C3B2E',
@@ -156,6 +157,93 @@ export const GRAPH_LABEL_COLOURS: Record<string, string> = {
156
157
  // (this object's key order), so a mid-table insert would silently change
157
158
  // the rendered shape of every label after it.
158
159
  CashEntry: '#7D5A2B',
160
+
161
+ // --- Task 1965: colour every node type that can appear in the graph ---
162
+ // Appended at the END so SHAPE_BY_LABEL indices for the labels above are
163
+ // unchanged. Grouped by family band; each hex is pairwise-unique across the
164
+ // whole registry (except the pre-existing UserMessage/Preference twin).
165
+
166
+ // Ledger / money children (brown-gold, beside Invoice and CashEntry).
167
+ InvoiceLine: '#7A5326',
168
+ InvoicePayment: '#8A6230',
169
+ QuoteLine: '#9A7038',
170
+ PurchaseOrderLine: '#A67E42',
171
+ JobCost: '#6A4A22',
172
+ Credit: '#B48A48',
173
+ Retainer: '#C29A55',
174
+ Sale: '#8E5E2A',
175
+ Valuation: '#A06A34',
176
+ Order: '#B87C3E',
177
+ DiningOrder: '#C9954E',
178
+
179
+ // Documents / content (steel-blue, hue-adjacent to the business slates).
180
+ Content: '#58708C',
181
+ Report: '#6D8299',
182
+ Page: '#849AAF',
183
+ Post: '#93A7BA',
184
+ VariationNote: '#47617B',
185
+ FileArtifact: '#A9BBCC',
186
+
187
+ // Marketing (bright teal-green, apart from workflow and research teals).
188
+ SocialPost: '#2FA69A',
189
+ Campaign: '#3FB8A0',
190
+ BrandDeal: '#57C2AE',
191
+ Recommendation: '#74CDBB',
192
+
193
+ // CRM people (terracotta, beside Person and Customer).
194
+ Contact: '#C97E52',
195
+ Lead: '#A85E38',
196
+ Position: '#96522E',
197
+ Audience: '#E0B088',
198
+
199
+ // Places / transport / retail (forest-emerald, apart from knowledge moss).
200
+ PostalAddress: '#2E8B57',
201
+ Room: '#3A9B63',
202
+ StoreLocation: '#46A96F',
203
+ Vehicle: '#54B77B',
204
+ Route: '#62C187',
205
+ Stop: '#2A7A4C',
206
+ Shipment: '#6FCB93',
207
+ Reservation: '#7ED4A0',
208
+ TableReservation: '#8CDBAC',
209
+ Menu: '#1F6B41',
210
+ MenuItem: '#98E0B6',
211
+ Product: '#34925E',
212
+ StockMovement: '#4CA574',
213
+
214
+ // Work / delivery (indigo, apart from the strategy indigo band).
215
+ Milestone: '#4A5FA8',
216
+ Deliverable: '#6274C0',
217
+ TimeEntry: '#8189CE',
218
+ Engagement: '#9AA0DA',
219
+ Case: '#3B4E92',
220
+ Meeting: '#5566B4',
221
+
222
+ // Idea (amber).
223
+ Idea: '#E0A82E',
224
+
225
+ // Web analytics (sky-cyan, apart from the research teal).
226
+ Session: '#2AA7C4',
227
+ PageView: '#3DB4CE',
228
+ Click: '#55C0D6',
229
+ ScrollMilestone: '#70CCDE',
230
+ TimelineEvent: '#1E93B0',
231
+ AnonVisitor: '#88D6E6',
232
+ AEOAudit: '#46B0CB',
233
+
234
+ // WhatsApp transport (brand green).
235
+ WhatsAppConversation: '#128C7E',
236
+ WhatsAppMessage: '#1FA894',
237
+
238
+ // Cloudflare / chain infra (grey-blue).
239
+ CloudflareHostname: '#5B6E82',
240
+ CloudflareTunnel: '#6E8296',
241
+ Chain: '#47586A',
242
+ ChainLink: '#8496AA',
243
+
244
+ // Voice (violet).
245
+ VoiceProfile: '#9B59B6',
246
+ VoiceEdit: '#B07CC9',
159
247
  }
160
248
 
161
249
  export const FALLBACK_LABEL_COLOUR = '#94A3B8'
@@ -251,6 +339,18 @@ export function shapeForLabel(label: string): GraphNodeShape {
251
339
  return SHAPE_BY_LABEL[label] ?? FALLBACK_LABEL_SHAPE
252
340
  }
253
341
 
342
+ /**
343
+ * Node labels written by code with no schema node-type-table row and no
344
+ * typed-edge declaration. They still render as graph nodes, so they must carry
345
+ * a colour + glyph like any other node type. The reconciliation gate in
346
+ * __tests__/icons.test.ts folds this set into the node-label universe. Add a
347
+ * label here when the runtime `op=icon-fallback` census names one that no
348
+ * schema table or typed edge declares.
349
+ */
350
+ export const CODE_EMITTED_NODE_LABELS: ReadonlySet<string> = Object.freeze(
351
+ new Set(['FileArtifact']),
352
+ ) as ReadonlySet<string>
353
+
254
354
  // ---------------------------------------------------------------------------
255
355
  // Icon registry — a third visual channel alongside colour + shape.
256
356
  //
@@ -325,6 +425,67 @@ const LABEL_ICONS: Record<string, string> = {
325
425
  Finding: '<path d="M15 14c.2-1 .7-1.7 1.5-2.5 1-.9 1.5-2.2 1.5-3.5A6 6 0 0 0 6 8c0 1 .2 2.2 1.5 3.5.7.7 1.3 1.5 1.5 2.5"></path><path d="M9 18h6"></path><path d="M10 22h4"></path>',
326
426
  Hypothesis: '<path d="M14 2v6a2 2 0 0 0 .245.96l5.51 10.08A2 2 0 0 1 18 22H6a2 2 0 0 1-1.755-2.96l5.51-10.08A2 2 0 0 0 10 8V2"></path><path d="M6.453 15h11.094"></path><path d="M8.5 2h7"></path>',
327
427
  CashEntry: '<rect width="20" height="12" x="2" y="6" rx="2"></rect><circle cx="12" cy="12" r="2"></circle><path d="M6 12h.01"></path><path d="M18 12h.01"></path>',
428
+ // --- Task 1965: glyphs for the 60 previously-uncovered node labels ---
429
+ InvoiceLine: '<path d="M13 16H8" /><path d="M14 8H8" /><path d="M16 12H8" /><path d="M4 3a1 1 0 0 1 1-1 1.3 1.3 0 0 1 .7.2l.933.6a1.3 1.3 0 0 0 1.4 0l.934-.6a1.3 1.3 0 0 1 1.4 0l.933.6a1.3 1.3 0 0 0 1.4 0l.933-.6a1.3 1.3 0 0 1 1.4 0l.934.6a1.3 1.3 0 0 0 1.4 0l.933-.6A1.3 1.3 0 0 1 19 2a1 1 0 0 1 1 1v18a1 1 0 0 1-1 1 1.3 1.3 0 0 1-.7-.2l-.933-.6a1.3 1.3 0 0 0-1.4 0l-.934.6a1.3 1.3 0 0 1-1.4 0l-.933-.6a1.3 1.3 0 0 0-1.4 0l-.933.6a1.3 1.3 0 0 1-1.4 0l-.934-.6a1.3 1.3 0 0 0-1.4 0l-.933.6a1.3 1.3 0 0 1-.7.2 1 1 0 0 1-1-1z" />',
430
+ InvoicePayment: '<path d="M11 15h2a2 2 0 1 0 0-4h-3c-.6 0-1.1.2-1.4.6L3 17" /><path d="m7 21 1.6-1.4c.3-.4.8-.6 1.4-.6h4c1.1 0 2.1-.4 2.8-1.2l4.6-4.4a2 2 0 0 0-2.75-2.91l-4.2 3.9" /><path d="m2 16 6 6" /><circle cx="16" cy="9" r="2.9" /><circle cx="6" cy="5" r="3" />',
431
+ QuoteLine: '<path d="M3 5h.01" /><path d="M3 12h.01" /><path d="M3 19h.01" /><path d="M8 5h13" /><path d="M8 12h13" /><path d="M8 19h13" />',
432
+ PurchaseOrderLine: '<path d="M11 5h10" /><path d="M11 12h10" /><path d="M11 19h10" /><path d="M4 4h1v5" /><path d="M4 9h2" /><path d="M6.5 20H3.4c0-1 2.6-1.925 2.6-3.5a1.5 1.5 0 0 0-2.6-1.02" />',
433
+ JobCost: '<rect width="20" height="12" x="2" y="6" rx="2" /><circle cx="12" cy="12" r="2" /><path d="M6 12h.01M18 12h.01" />',
434
+ Credit: '<path d="M3.85 8.62a4 4 0 0 1 4.78-4.77 4 4 0 0 1 6.74 0 4 4 0 0 1 4.78 4.78 4 4 0 0 1 0 6.74 4 4 0 0 1-4.77 4.78 4 4 0 0 1-6.75 0 4 4 0 0 1-4.78-4.77 4 4 0 0 1 0-6.76Z" /><path d="M16 8h-6a2 2 0 1 0 0 4h4a2 2 0 1 1 0 4H8" /><path d="M12 18V6" />',
435
+ Retainer: '<path d="M19 7V4a1 1 0 0 0-1-1H5a2 2 0 0 0 0 4h15a1 1 0 0 1 1 1v4h-3a2 2 0 0 0 0 4h3a1 1 0 0 0 1-1v-2a1 1 0 0 0-1-1" /><path d="M3 5v14a2 2 0 0 0 2 2h15a1 1 0 0 0 1-1v-4" />',
436
+ Sale: '<path d="M12 17V7" /><path d="M16 8h-6a2 2 0 0 0 0 4h4a2 2 0 0 1 0 4H8" /><path d="M4 3a1 1 0 0 1 1-1 1.3 1.3 0 0 1 .7.2l.933.6a1.3 1.3 0 0 0 1.4 0l.934-.6a1.3 1.3 0 0 1 1.4 0l.933.6a1.3 1.3 0 0 0 1.4 0l.933-.6a1.3 1.3 0 0 1 1.4 0l.934.6a1.3 1.3 0 0 0 1.4 0l.933-.6A1.3 1.3 0 0 1 19 2a1 1 0 0 1 1 1v18a1 1 0 0 1-1 1 1.3 1.3 0 0 1-.7-.2l-.933-.6a1.3 1.3 0 0 0-1.4 0l-.934.6a1.3 1.3 0 0 1-1.4 0l-.933-.6a1.3 1.3 0 0 0-1.4 0l-.933.6a1.3 1.3 0 0 1-1.4 0l-.934-.6a1.3 1.3 0 0 0-1.4 0l-.933.6a1.3 1.3 0 0 1-.7.2 1 1 0 0 1-1-1z" />',
437
+ Valuation: '<path d="M12 3v18" /><path d="m19 8 3 8a5 5 0 0 1-6 0zV7" /><path d="M3 7h1a17 17 0 0 0 8-2 17 17 0 0 0 8 2h1" /><path d="m5 8 3 8a5 5 0 0 1-6 0zV7" /><path d="M7 21h10" />',
438
+ Order: '<path d="M16 10a4 4 0 0 1-8 0" /><path d="M3.103 6.034h17.794" /><path d="M3.4 5.467a2 2 0 0 0-.4 1.2V20a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6.667a2 2 0 0 0-.4-1.2l-2-2.667A2 2 0 0 0 17 2H7a2 2 0 0 0-1.6.8z" />',
439
+ DiningOrder: '<path d="M3 2v7c0 1.1.9 2 2 2h4a2 2 0 0 0 2-2V2" /><path d="M7 2v20" /><path d="M21 15V2a5 5 0 0 0-5 5v6c0 1.1.9 2 2 2h3Zm0 0v7" />',
440
+ Content: '<path d="M15 18h-5" /><path d="M18 14h-8" /><path d="M4 22h16a2 2 0 0 0 2-2V4a2 2 0 0 0-2-2H8a2 2 0 0 0-2 2v16a2 2 0 0 1-4 0v-9a2 2 0 0 1 2-2h2" /><rect width="8" height="4" x="10" y="6" rx="1" />',
441
+ Report: '<rect width="8" height="4" x="8" y="2" rx="1" ry="1" /><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2" /><path d="M12 11h4" /><path d="M12 16h4" /><path d="M8 11h.01" /><path d="M8 16h.01" />',
442
+ Page: '<path d="M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z" /><path d="M14 2v5a1 1 0 0 0 1 1h5" />',
443
+ Post: '<path d="M12 3H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7" /><path d="M18.375 2.625a1 1 0 0 1 3 3l-9.013 9.014a2 2 0 0 1-.853.505l-2.873.84a.5.5 0 0 1-.62-.62l.84-2.873a2 2 0 0 1 .506-.852z" />',
444
+ SocialPost: '<path d="M11 6a13 13 0 0 0 8.4-2.8A1 1 0 0 1 21 4v12a1 1 0 0 1-1.6.8A13 13 0 0 0 11 14H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2z" /><path d="M6 14a12 12 0 0 0 2.4 7.2 2 2 0 0 0 3.2-2.4A8 8 0 0 1 10 14" /><path d="M8 6v8" />',
445
+ VariationNote: '<path d="M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z" /><path d="M9 10h6" /><path d="M12 13V7" /><path d="M9 17h6" />',
446
+ Contact: '<path d="M16 2v2" /><path d="M17.915 22a6 6 0 0 0-12 0" /><path d="M8 2v2" /><circle cx="12" cy="12" r="4" /><rect x="3" y="4" width="18" height="18" rx="2" />',
447
+ Lead: '<path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2" /><circle cx="9" cy="7" r="4" /><line x1="19" x2="19" y1="8" y2="14" /><line x1="22" x2="16" y1="11" y2="11" />',
448
+ Position: '<path d="M16 20V4a2 2 0 0 0-2-2h-4a2 2 0 0 0-2 2v16" /><rect width="20" height="14" x="2" y="6" rx="2" />',
449
+ Audience: '<path d="M18 21a8 8 0 0 0-16 0" /><circle cx="10" cy="8" r="5" /><path d="M22 20c0-3.37-2-6.5-4-8a5 5 0 0 0-.45-8.3" />',
450
+ PostalAddress: '<path d="M18 8c0 3.613-3.869 7.429-5.393 8.795a1 1 0 0 1-1.214 0C9.87 15.429 6 11.613 6 8a6 6 0 0 1 12 0" /><circle cx="12" cy="8" r="2" /><path d="M8.714 14h-3.71a1 1 0 0 0-.948.683l-2.004 6A1 1 0 0 0 3 22h18a1 1 0 0 0 .948-1.316l-2-6a1 1 0 0 0-.949-.684h-3.712" />',
451
+ Room: '<path d="M11 20H2" /><path d="M11 4.562v16.157a1 1 0 0 0 1.242.97L19 20V5.562a2 2 0 0 0-1.515-1.94l-4-1A2 2 0 0 0 11 4.561z" /><path d="M11 4H8a2 2 0 0 0-2 2v14" /><path d="M14 12h.01" /><path d="M22 20h-3" />',
452
+ StoreLocation: '<path d="M15 21v-5a1 1 0 0 0-1-1h-4a1 1 0 0 0-1 1v5" /><path d="M17.774 10.31a1.12 1.12 0 0 0-1.549 0 2.5 2.5 0 0 1-3.451 0 1.12 1.12 0 0 0-1.548 0 2.5 2.5 0 0 1-3.452 0 1.12 1.12 0 0 0-1.549 0 2.5 2.5 0 0 1-3.77-3.248l2.889-4.184A2 2 0 0 1 7 2h10a2 2 0 0 1 1.653.873l2.895 4.192a2.5 2.5 0 0 1-3.774 3.244" /><path d="M4 10.95V19a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-8.05" />',
453
+ Vehicle: '<path d="M19 17h2c.6 0 1-.4 1-1v-3c0-.9-.7-1.7-1.5-1.9C18.7 10.6 16 10 16 10s-1.3-1.4-2.2-2.3c-.5-.4-1.1-.7-1.8-.7H5c-.6 0-1.1.4-1.4.9l-1.4 2.9A3.7 3.7 0 0 0 2 12v4c0 .6.4 1 1 1h2" /><circle cx="7" cy="17" r="2" /><path d="M9 17h6" /><circle cx="17" cy="17" r="2" />',
454
+ Route: '<circle cx="6" cy="19" r="3" /><path d="M9 19h8.5a3.5 3.5 0 0 0 0-7h-11a3.5 3.5 0 0 1 0-7H15" /><circle cx="18" cy="5" r="3" />',
455
+ Stop: '<path d="M20 10c0 4.993-5.539 10.193-7.399 11.799a1 1 0 0 1-1.202 0C9.539 20.193 4 14.993 4 10a8 8 0 0 1 16 0" /><circle cx="12" cy="10" r="3" />',
456
+ Shipment: '<path d="M11 21.73a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73z" /><path d="M12 22V12" /><polyline points="3.29 7 12 12 20.71 7" /><path d="m7.5 4.27 9 5.15" />',
457
+ Reservation: '<path d="M8 2v4" /><path d="M16 2v4" /><rect width="18" height="18" x="3" y="4" rx="2" /><path d="M3 10h18" /><path d="m9 16 2 2 4-4" />',
458
+ TableReservation: '<path d="m16 2-2.3 2.3a3 3 0 0 0 0 4.2l1.8 1.8a3 3 0 0 0 4.2 0L22 8" /><path d="M15 15 3.3 3.3a4.2 4.2 0 0 0 0 6l7.3 7.3c.7.7 2 .7 2.8 0L15 15Zm0 0 7 7" /><path d="m2.1 21.8 6.4-6.3" /><path d="m19 5-7 7" />',
459
+ Menu: '<path d="M12 5v16" /><path d="M20.001 19A2 2 0 0022 17V5a2 2 0 00-1.999-2L16 3.002A5 5 0 0012 5a5 5 0 00-4-2H4a2 2 0 00-2 2v12a2 2 0 001.999 2H8a5 5 0 014 2 5 5 0 014-2z" />',
460
+ MenuItem: '<path d="M7 21h10" /><path d="M12 21a9 9 0 0 0 9-9H3a9 9 0 0 0 9 9Z" /><path d="M11.38 12a2.4 2.4 0 0 1-.4-4.77 2.4 2.4 0 0 1 3.2-2.77 2.4 2.4 0 0 1 3.47-.63 2.4 2.4 0 0 1 3.37 3.37 2.4 2.4 0 0 1-1.1 3.7 2.51 2.51 0 0 1 .03 1.1" /><path d="m13 12 4-4" /><path d="M10.9 7.25A3.99 3.99 0 0 0 4 10c0 .73.2 1.41.54 2" />',
461
+ Product: '<path d="M21 8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16Z" /><path d="m3.3 7 8.7 5 8.7-5" /><path d="M12 22V12" />',
462
+ StockMovement: '<path d="m16 3 4 4-4 4" /><path d="M20 7H4" /><path d="m8 21-4-4 4-4" /><path d="M4 17h16" />',
463
+ Milestone: '<path d="M4 22V4a1 1 0 0 1 .4-.8A6 6 0 0 1 8 2c3 0 5 2 7.333 2q2 0 3.067-.8A1 1 0 0 1 20 4v10a1 1 0 0 1-.4.8A6 6 0 0 1 16 16c-3 0-5-2-8-2a6 6 0 0 0-4 1.528" />',
464
+ Deliverable: '<path d="M12 22V12" /><path d="m16 17 2 2 4-4" /><path d="M21 11.127V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.729l7 4a2 2 0 0 0 2 .001l1.32-.753" /><path d="M3.29 7 12 12l8.71-5" /><path d="m7.5 4.27 8.997 5.148" />',
465
+ TimeEntry: '<line x1="10" x2="14" y1="2" y2="2" /><line x1="12" x2="15" y1="14" y2="11" /><circle cx="12" cy="14" r="8" />',
466
+ Engagement: '<path d="m11 17 2 2a1 1 0 1 0 3-3" /><path d="m14 14 2.5 2.5a1 1 0 1 0 3-3l-3.88-3.88a3 3 0 0 0-4.24 0l-.88.88a1 1 0 1 1-3-3l2.81-2.81a5.79 5.79 0 0 1 7.06-.87l.47.28a2 2 0 0 0 1.42.25L21 4" /><path d="m21 3 1 11h-2" /><path d="M3 3 2 14l6.5 6.5a1 1 0 1 0 3-3" /><path d="M3 4h8" />',
467
+ Case: '<path d="M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z" />',
468
+ Meeting: '<path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2" /><path d="M16 3.128a4 4 0 0 1 0 7.744" /><path d="M22 21v-2a4 4 0 0 0-3-3.87" /><circle cx="9" cy="7" r="4" />',
469
+ Idea: '<path d="M15 14c.2-1 .7-1.7 1.5-2.5 1-.9 1.5-2.2 1.5-3.5A6 6 0 0 0 6 8c0 1 .2 2.2 1.5 3.5.7.7 1.3 1.5 1.5 2.5" /><path d="M9 18h6" /><path d="M10 22h4" />',
470
+ Campaign: '<path d="M12 15v5s3.03-.55 4-2c1.08-1.62 0-5 0-5" /><path d="M4.5 16.5c-1.5 1.26-2 5-2 5s3.74-.5 5-2c.71-.84.7-2.13-.09-2.91a2.18 2.18 0 0 0-2.91-.09" /><path d="M9 12a22 22 0 0 1 2-3.95A12.88 12.88 0 0 1 22 2c0 2.72-.78 7.5-6 11a22.4 22.4 0 0 1-4 2z" /><path d="M9 12H4s.55-3.03 2-4c1.62-1.08 5 .05 5 .05" />',
471
+ BrandDeal: '<path d="M3.85 8.62a4 4 0 0 1 4.78-4.77 4 4 0 0 1 6.74 0 4 4 0 0 1 4.78 4.78 4 4 0 0 1 0 6.74 4 4 0 0 1-4.77 4.78 4 4 0 0 1-6.75 0 4 4 0 0 1-4.78-4.77 4 4 0 0 1 0-6.76Z" /><path d="m9 12 2 2 4-4" />',
472
+ Recommendation: '<path d="M15 5.88 14 10h5.83a2 2 0 0 1 1.92 2.56l-2.33 8A2 2 0 0 1 17.5 22H4a2 2 0 0 1-2-2v-8a2 2 0 0 1 2-2h2.76a2 2 0 0 0 1.79-1.11L12 2a3.13 3.13 0 0 1 3 3.88Z" /><path d="M7 10v12" />',
473
+ Session: '<rect width="20" height="14" x="2" y="3" rx="2" /><line x1="8" x2="16" y1="21" y2="21" /><line x1="12" x2="12" y1="17" y2="21" />',
474
+ PageView: '<path d="M2.062 12.348a1 1 0 0 1 0-.696 10.75 10.75 0 0 1 19.876 0 1 1 0 0 1 0 .696 10.75 10.75 0 0 1-19.876 0" /><circle cx="12" cy="12" r="3" />',
475
+ Click: '<path d="M14 4.1 12 6" /><path d="m5.1 8-2.9-.8" /><path d="m6 12-1.9 2" /><path d="M7.2 2.2 8 5.1" /><path d="M9.037 9.69a.498.498 0 0 1 .653-.653l11 4.5a.5.5 0 0 1-.074.949l-4.349 1.041a1 1 0 0 0-.74.739l-1.04 4.35a.5.5 0 0 1-.95.074z" />',
476
+ ScrollMilestone: '<path d="m7 6 5 5 5-5" /><path d="m7 13 5 5 5-5" />',
477
+ TimelineEvent: '<path d="M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8" /><path d="M3 3v5h5" /><path d="M12 7v5l4 2" />',
478
+ AnonVisitor: '<path d="M9 10h.01" /><path d="M15 10h.01" /><path d="M12 2a8 8 0 0 0-8 8v12l3-3 2.5 2.5L12 19l2.5 2.5L17 19l3 3V10a8 8 0 0 0-8-8z" />',
479
+ AEOAudit: '<path d="m8 11 2 2 4-4" /><circle cx="11" cy="11" r="8" /><path d="m21 21-4.3-4.3" />',
480
+ WhatsAppConversation: '<path d="M2.992 16.342a2 2 0 0 1 .094 1.167l-1.065 3.29a1 1 0 0 0 1.236 1.168l3.413-.998a2 2 0 0 1 1.099.092 10 10 0 1 0-4.777-4.719" />',
481
+ WhatsAppMessage: '<path d="M22 17a2 2 0 0 1-2 2H6.828a2 2 0 0 0-1.414.586l-2.202 2.202A.71.71 0 0 1 2 21.286V5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2z" /><path d="M7 11h10" /><path d="M7 15h6" /><path d="M7 7h8" />',
482
+ CloudflareHostname: '<circle cx="12" cy="12" r="10" /><path d="M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20" /><path d="M2 12h20" />',
483
+ CloudflareTunnel: '<rect x="16" y="16" width="6" height="6" rx="1" /><rect x="2" y="16" width="6" height="6" rx="1" /><rect x="9" y="2" width="6" height="6" rx="1" /><path d="M5 16v-3a1 1 0 0 1 1-1h12a1 1 0 0 1 1 1v3" /><path d="M12 12V8" />',
484
+ Chain: '<path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" /><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" />',
485
+ ChainLink: '<path d="M9 17H7A5 5 0 0 1 7 7h2" /><path d="M15 7h2a5 5 0 1 1 0 10h-2" /><line x1="8" x2="16" y1="12" y2="12" />',
486
+ FileArtifact: '<path d="M13.659 22H18a2 2 0 0 0 2-2V8a2.4 2.4 0 0 0-.706-1.706l-3.588-3.588A2.4 2.4 0 0 0 14 2H6a2 2 0 0 0-2 2v11.5" /><path d="M14 2v5a1 1 0 0 0 1 1h5" /><path d="M8 12v-1" /><path d="M8 18v-2" /><path d="M8 7V6" /><circle cx="8" cy="20" r="2" />',
487
+ VoiceProfile: '<path d="M12 19v3" /><path d="M19 10v2a7 7 0 0 1-14 0v-2" /><rect x="9" y="2" width="6" height="13" rx="3" />',
488
+ VoiceEdit: '<path d="M2 10v3" /><path d="M6 6v11" /><path d="M10 3v18" /><path d="M14 8v7" /><path d="M18 5v13" /><path d="M22 10v3" />',
328
489
  }
329
490
 
330
491
  /**
@@ -184,6 +184,7 @@ Tools are available via the `admin` MCP server.
184
184
  | Investigate a bug or unexpected behaviour | User asks to debug, fix a bug, find the root cause, or investigate an error | `skills/investigate/SKILL.md` |
185
185
  | Capture work as a task before implementing | User asks to "task it", create a task, or describes work that should be a task file before a sprint | `skills/task/SKILL.md` |
186
186
  | Run an insight pass on this session | Operator asks to run an insight pass, review this session, capture what we learned, or do a retrospective on the conversation | `skills/insight/SKILL.md` |
187
+ | Summarise the last turn | User asks to summarise, recap, or give the "in short" version of what the agent just said or asked | `skills/summarise/SKILL.md` |
187
188
 
188
189
  ## Hooks
189
190
 
@@ -28,6 +28,21 @@ Load `references/agent-pattern.md`. It is the canonical shape: the two kinds of
28
28
  deny-by-default tool rule, model-choice guidance, and the three-part contract body. Use it
29
29
  throughout. Do not paraphrase the pattern from memory; the reference is the source.
30
30
 
31
+ ## Step 0: Check for a deliverable catalogue
32
+
33
+ When the operator asks what can be built here, rather than naming a specific agent, first look for
34
+ a shipped suggestion catalogue. A premium bundle can ship a `DELIVERABLES.md` at its root listing
35
+ deliverables it supports beyond the agents it already ships. Get the enabled bundles from
36
+ `capabilities-here`, then for each one read `$PLATFORM_ROOT/../premium-plugins/<bundle>/DELIVERABLES.md`
37
+ if it exists, the bundle root where its `BUNDLE.md` and `agents/` live. Offer the operator the entries you find, grouped by bundle. If
38
+ no enabled bundle ships one, skip this step and go to Step 1.
39
+
40
+ The catalogue is a suggestion list, not a shipped roster. Each entry is authored bespoke for this
41
+ account when the operator picks it, because a generic version carries none of the customer's own
42
+ determinism. On a pick, run Steps 1 through 7 to author the skill and agent into the account's own
43
+ plugins tree, and name the agent for the customer's actual deliverable rather than the catalogue's
44
+ ontology label, exactly as the catalogue instructs. The ontology name is grounding, not a file name.
45
+
31
46
  ## Step 1: Name the deliverable
32
47
 
33
48
  Ask: **"What is the one thing this agent hands back?"**
@@ -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:8be6993be76afa65716e56cd2dcb17c87f6588109d64ca81e38db74af1d4d48c
4
+ content-hash: sha256:f78dfe81d9832fd7197627e1cf770c730bb2d16b4b640f1a47558e86acfe0db8
5
5
  brand: maxy-code
6
6
  product-name: Maxy
7
7
  ---
@@ -3045,6 +3045,37 @@ routines exist for the account is the account-scope-miss signature; the logged
3045
3045
  `cron=` on `outcome=ok` confirms the persisted expression without opening the
3046
3046
  graph.
3047
3047
 
3048
+ ### Skills
3049
+
3050
+ The Skills surface (`/skills`) is the admin view of the account's operator-
3051
+ authored skills — the `SKILL.md` files the store-skill tool writes under
3052
+ `<accountDir>/plugins/<plugin>/skills/<skill>/` (the durable per-account plugin
3053
+ root, never the immutable `PLATFORM_ROOT/plugins` tree). It renders one card per
3054
+ skill (name, owning plugin, frontmatter description, reference count); a card
3055
+ opens a **read-only** detail modal showing the frontmatter, the SKILL.md body,
3056
+ and the reference filenames. The page is read-only by design — skills are
3057
+ authored and edited through the chat interface — with one exception: a **delete**
3058
+ behind an explicit confirm step in the modal. A confirmed delete removes the
3059
+ skill directory, and the whole plugin directory when that was its last skill.
3060
+
3061
+ The route (`server/routes/admin/skills.ts`) walks only the account tree, so
3062
+ shipped platform skills never appear. Plugin and skill path params are kebab-case
3063
+ validated (`/^[a-z0-9][a-z0-9-]*$/`) — the traversal guard; anything else 400s.
3064
+
3065
+ | Route | Behaviour |
3066
+ |-------|-----------|
3067
+ | `GET /api/admin/skills` | Enumerates `plugins/*/skills/*/SKILL.md`, returns `{plugin, skill, description, refCount}` per skill, sorted plugin then skill. A per-entry read error logs `op=list-skip` rather than silently shortening the list. |
3068
+ | `GET /api/admin/skills/:plugin/:skill` | One skill's parsed frontmatter (`name`, `description`, `publicEmbed`), the SKILL.md body, and the reference filenames. Unknown skill 404s. |
3069
+ | `DELETE /api/admin/skills/:plugin/:skill` | Removes the skill dir; removes the plugin dir when its `skills/` empties. Returns `{ok, skillDirGone, pluginDirRemoved}` measured after the rm. |
3070
+
3071
+ Observability rides `[admin:skills]`: `op=list accountId=<8> plugins=<n>
3072
+ skills=<n>` (the surface's own heartbeat — `skills=0` against a populated
3073
+ `plugins/*/skills/` is the broken-walk signature), `op=get plugin=<…> skill=<…>
3074
+ found=<bool>`, and `op=delete plugin=<…> skill=<…> outcome=ok
3075
+ skillDirGone=<bool> pluginDirRemoved=<bool>` (`skillDirGone=false` with
3076
+ `outcome=ok` is a named failure) / `outcome=rejected reason=<invalid-name|
3077
+ not-found|rm-error>`.
3078
+
3048
3079
  ### Graph
3049
3080
 
3050
3081
  | Mount | Purpose |
@@ -3056,6 +3087,25 @@ graph.
3056
3087
  | `/graph-labels-in-graph` | Distinct label list for the filter dropdown. |
3057
3088
  | `/graph-default-view` | Account-scoped saved view (zoom, focal id, filters). |
3058
3089
 
3090
+ **Node visual encoding.** Every node type that can appear on the canvas renders
3091
+ a coloured disc with a white Lucide glyph, sized by incident-edge count, with a
3092
+ matching legend swatch. Colour, shape, and glyph are three per-label registries
3093
+ in `platform/lib/graph-style/src/index.ts`. Coverage is reconciled against the
3094
+ true node-label universe, not the colour registry itself: the standing test
3095
+ `__tests__/icons.test.ts` assembles the universe from the `Neo4j Label` column
3096
+ of every `schema-*.md` node-type table, the `sourceLabel`/`targetLabel` set in
3097
+ `typed-edge-schema.ts`, and the `CODE_EMITTED_NODE_LABELS` list, then fails the
3098
+ build if any member lacks a colour or glyph. Labels that only ever stack on a
3099
+ base node (`ADDITIONAL_BASE_LABELS`) inherit the base node's colour and are
3100
+ excluded. A node type carries a coloured glyph even when its text caption is its
3101
+ bare type: the glyph and the caption are independent channels (caption coverage
3102
+ is the separate `caption-coverage.test.ts` gate). At runtime, a per-session
3103
+ `op=icon-fallback` census (emitted from `app/graph/page.tsx` across the initial
3104
+ render and every live-update poll) names any label that reached the canvas with
3105
+ no glyph; an empty census is the healthy signal, a non-empty one means a
3106
+ code-emitted label that no static source declares and should be added to
3107
+ `CODE_EMITTED_NODE_LABELS`.
3108
+
3059
3109
  ### Artefacts and files
3060
3110
 
3061
3111
  | Mount | Purpose |
@@ -4304,7 +4354,7 @@ Standing rules the operator gives over a channel are `Preference` nodes. Two mec
4304
4354
 
4305
4355
  **Observability.** `[preference-write] op=reconcile … mode=<reinforce|update|contradict|new> similarity=…` — a stream of `mode=new` for subjects that already exist is the duplicate-minting signature. `[preference-inject] op=inject channel=<wa|tg|web> injected=<N> owner=<8> source=<owner|no-owner|owner-error|fetch-error>` — injection selects only the account owner's admin preferences, so `source=owner injected=0` is a genuinely empty owner, `source=no-owner` is an account with no seeded owner (injects nothing, a missed owner seeding), and `injected=0 source=owner` on an account whose owner holds rules is the write-only regression re-appearing. `[preference-inject] op=reconcile accountId=… stored=… injected=…` — the standing reconciliation audit (claude-session-manager, 5-minute tick) flags any account whose stored active count diverges from what injection surfaces; injection now surfaces every stored active preference verbatim, so `stored == injected` is the healthy signal and any gap is a dropped-row regression (a reintroduced cap, filter, or truncation). A clean pass logs `op=reconcile drifted=0`, the no-event detector.
4306
4356
 
4307
- **Consult-before-send gate.** Injection (above) puts the owner's active rules in front of every channel turn, but the growing layer-2 `Preference` set is read on demand with `profile-read`, a per-turn judgement the agent can skip, the miss that dropped a customer signature on a delivered document. `hooks/preference-consult-gate.sh` (admin plugin) is the deliverable-boundary enforcement: a PreToolUse gate on customer-facing document deliverables (`browser-pdf-save`/`SendUserFile` to `memory/users/<phone>/documents/`, and email/Outlook sends carrying attachments) that blocks (exit 2) when no `profile-read` ran after the last user message in the transcript. It fires on documents only, never on casual replies. **Observability:** `[preference-gate] op=allow tool=<name> consulted=true` on an allowed document deliverable, `op=bypass tool=<name> detail=no profile-read this turn` on a block; a stream of `op=bypass` is the un-consulted-send signature. The advisory companion `hooks/preference-consult-directive.sh` injects the two-layer architecture reminder every turn (`[pref-wrapper] op=inject`). The placement half, promoting finished deliverables out of `output/` scratch, is counted by the `data-manager` reconcile audit (`scratch-refs`, `stranded`); making that audit run on a standing periodic cadence is a filed follow-up.
4357
+ **Consult-before-send gate.** Injection (above) puts the owner's active rules in front of every channel turn, but the growing layer-2 `Preference` set is read on demand with `profile-read`, a per-turn judgement the agent can skip, the miss that dropped a customer signature on a delivered document. `hooks/preference-consult-gate.sh` (admin plugin) is the deliverable-boundary enforcement: a PreToolUse gate on customer-facing document deliverables (`browser-pdf-save`/`SendUserFile` to `memory/users/<phone>/documents/`, and email/Outlook sends carrying attachments) that blocks (exit 2) when no `profile-read` ran after the last user message in the transcript. It fires on documents only, never on casual replies. **Observability:** `[preference-gate] op=allow tool=<name> consulted=true` on an allowed document deliverable, `op=bypass tool=<name> detail=no profile-read this turn` on a block; a stream of `op=bypass` is the un-consulted-send signature. The advisory companion `hooks/preference-consult-directive.sh` injects the two-layer architecture reminder every turn (`[pref-wrapper] op=inject`). The placement half, promoting finished deliverables out of `output/` scratch, is counted by the `data-manager` reconcile audit (`scratch-refs`, `stranded`); making that audit run on a standing periodic cadence is a filed follow-up. The inbound mirror is the `uploads/` intake inbox: an uploaded file lands at `uploads/<attachmentId>/` and is promoted to its canonical entity bucket when its graph node is created, so a persisted node reference resolving into `uploads/` is intake drift, counted by the same reconcile audit as `stranded-intake`. Unlike `output/`, `uploads/` is never rebuilt by a tool, so the `data-manager` may promote a file out of it. Beyond placement and intake, the same audit counts intra-folder sprawl inside each operator-data entity folder from disk structure alone: superseded version copies (`<base>.ext` and `<base>-vN.ext` are one deliverable, all but the newest counted `versioned`), format twins (a source `.html`/`.md` render kept beside its delivered `.pdf` of the same basename, counted `format-twins`), and subfolders nested under `projects/<name>/` or `contacts/<name>/` that the flat rule forbids (counted `stray-subdir`). These are read-only counts; removal or flattening happens only on a `data-manager` stewardship dispatch, each paired with a `database-operator` reference update. The reconcile output contract is `unreachable=N broken-refs=M scratch-refs=P stranded=Q stranded-intake=R versioned=S format-twins=T stray-subdir=U`.
4308
4358
 
4309
4359
  ---
4310
4360
 
@@ -0,0 +1,37 @@
1
+ ---
2
+ name: summarise
3
+ description: Use when the user types /summarise, or asks for a summary, recap, or "in short" version of what the agent just said or asked. Restates the agent's own immediately preceding turn under the plainly rules: precise, concise, plain English, every claim already evidenced in that turn. When that turn concluded an investigation, the summary carries exactly two things: the diagnosis and the proposed resolution.
4
+ ---
5
+
6
+ # summarise
7
+
8
+ Invoked by the admin agent directly.
9
+
10
+ The subject is the agent's own last turn, not the codebase, not the conversation as a whole, and not the user's message. Whatever was just said or asked gets restated so a reader who skipped the turn ends up with the same working understanding, in a fraction of the words.
11
+
12
+ ## What success looks like
13
+
14
+ The summary is precise, concise, plain English, with every claim already evidenced in the turn being summarised. It also satisfies the shipped `plainly` skill (`skills/plainly/SKILL.md`): spoken register, no acronyms, no formulas, no AI tells, no em-dashes. Read `plainly` before writing; do not summarise from memory of what it says.
15
+
16
+ Three sentences is the ceiling for an ordinary turn. If the turn ended in a question, the summary states the question and what turns on the answer, and nothing else.
17
+
18
+ ## Investigations
19
+
20
+ When the preceding turn concluded an investigation, the summary has exactly two parts, in this order:
21
+
22
+ 1. **Diagnosis.** What is actually wrong, and the evidence that establishes it. Name the cause, not the layer where the symptom surfaced.
23
+ 2. **Proposed resolution.** What would fix that cause, and its scope boundary.
24
+
25
+ Both parts are mandatory. A diagnosis with no proposed resolution is an unfinished summary; a proposed resolution with no diagnosis is speculation wearing a summary's clothes. Neither part introduces a finding, a fix, or a caveat that was absent from the turn being summarised. If the investigation reached a diagnosis but no resolution, say that in the resolution slot rather than inventing one.
26
+
27
+ ## Constraints
28
+
29
+ - **No new content.** The summary is a compression, never an extension. Anything not in the turn being summarised does not belong here.
30
+ - **Drop the scaffolding, keep the load.** File paths, tool names, and step-by-step narration go unless the reader needs them to act. The outcome stays.
31
+ - **No hedging.** The original turn either established a claim or it did not. Do not soften a confirmed finding into "appears to", and do not harden a speculation into a fact.
32
+ - **No follow-up offers.** End on the summary.
33
+
34
+ ## When NOT to use
35
+
36
+ - The user wants the summary of a document, a file, or someone else's text: that is ordinary reading work, and the `plainly` skill fits better.
37
+ - The last turn was already one or two plain sentences: there is nothing to compress, so say so and stop.
@@ -9,6 +9,13 @@ Invoked by the admin agent directly.
9
9
 
10
10
  This is the platform's release timeline, newest first. Each entry shows the date it shipped and the version it shipped in, so you can tell the operator how current their install is. To compare, read the installed version from `capabilities-here` and match it against the versions below. Keep answers high level and in plain English; this is a summary, not a full commit log.
11
11
 
12
+ ## 2026-07-25 (0.1.502)
13
+
14
+ - Uploading a file from the phone or desktop app now shows as a proper thumbnail in chat, not a raw file path.
15
+ - Switching accounts now shows a loading screen and lands you on the dashboard instead of a blank page.
16
+ - Uploaded files are automatically filed into the right folder instead of sitting in a temporary holding area.
17
+ - Searching your files is more reliable and no longer times out on larger accounts.
18
+
12
19
  ## 2026-07-21 (0.1.482)
13
20
 
14
21
  - WhatsApp now runs a separate connection for each client account, so one client's messages never mix with another's.
@@ -118,6 +118,37 @@ routines exist for the account is the account-scope-miss signature; the logged
118
118
  `cron=` on `outcome=ok` confirms the persisted expression without opening the
119
119
  graph.
120
120
 
121
+ ### Skills
122
+
123
+ The Skills surface (`/skills`) is the admin view of the account's operator-
124
+ authored skills — the `SKILL.md` files the store-skill tool writes under
125
+ `<accountDir>/plugins/<plugin>/skills/<skill>/` (the durable per-account plugin
126
+ root, never the immutable `PLATFORM_ROOT/plugins` tree). It renders one card per
127
+ skill (name, owning plugin, frontmatter description, reference count); a card
128
+ opens a **read-only** detail modal showing the frontmatter, the SKILL.md body,
129
+ and the reference filenames. The page is read-only by design — skills are
130
+ authored and edited through the chat interface — with one exception: a **delete**
131
+ behind an explicit confirm step in the modal. A confirmed delete removes the
132
+ skill directory, and the whole plugin directory when that was its last skill.
133
+
134
+ The route (`server/routes/admin/skills.ts`) walks only the account tree, so
135
+ shipped platform skills never appear. Plugin and skill path params are kebab-case
136
+ validated (`/^[a-z0-9][a-z0-9-]*$/`) — the traversal guard; anything else 400s.
137
+
138
+ | Route | Behaviour |
139
+ |-------|-----------|
140
+ | `GET /api/admin/skills` | Enumerates `plugins/*/skills/*/SKILL.md`, returns `{plugin, skill, description, refCount}` per skill, sorted plugin then skill. A per-entry read error logs `op=list-skip` rather than silently shortening the list. |
141
+ | `GET /api/admin/skills/:plugin/:skill` | One skill's parsed frontmatter (`name`, `description`, `publicEmbed`), the SKILL.md body, and the reference filenames. Unknown skill 404s. |
142
+ | `DELETE /api/admin/skills/:plugin/:skill` | Removes the skill dir; removes the plugin dir when its `skills/` empties. Returns `{ok, skillDirGone, pluginDirRemoved}` measured after the rm. |
143
+
144
+ Observability rides `[admin:skills]`: `op=list accountId=<8> plugins=<n>
145
+ skills=<n>` (the surface's own heartbeat — `skills=0` against a populated
146
+ `plugins/*/skills/` is the broken-walk signature), `op=get plugin=<…> skill=<…>
147
+ found=<bool>`, and `op=delete plugin=<…> skill=<…> outcome=ok
148
+ skillDirGone=<bool> pluginDirRemoved=<bool>` (`skillDirGone=false` with
149
+ `outcome=ok` is a named failure) / `outcome=rejected reason=<invalid-name|
150
+ not-found|rm-error>`.
151
+
121
152
  ### Graph
122
153
 
123
154
  | Mount | Purpose |
@@ -129,6 +160,25 @@ graph.
129
160
  | `/graph-labels-in-graph` | Distinct label list for the filter dropdown. |
130
161
  | `/graph-default-view` | Account-scoped saved view (zoom, focal id, filters). |
131
162
 
163
+ **Node visual encoding.** Every node type that can appear on the canvas renders
164
+ a coloured disc with a white Lucide glyph, sized by incident-edge count, with a
165
+ matching legend swatch. Colour, shape, and glyph are three per-label registries
166
+ in `platform/lib/graph-style/src/index.ts`. Coverage is reconciled against the
167
+ true node-label universe, not the colour registry itself: the standing test
168
+ `__tests__/icons.test.ts` assembles the universe from the `Neo4j Label` column
169
+ of every `schema-*.md` node-type table, the `sourceLabel`/`targetLabel` set in
170
+ `typed-edge-schema.ts`, and the `CODE_EMITTED_NODE_LABELS` list, then fails the
171
+ build if any member lacks a colour or glyph. Labels that only ever stack on a
172
+ base node (`ADDITIONAL_BASE_LABELS`) inherit the base node's colour and are
173
+ excluded. A node type carries a coloured glyph even when its text caption is its
174
+ bare type: the glyph and the caption are independent channels (caption coverage
175
+ is the separate `caption-coverage.test.ts` gate). At runtime, a per-session
176
+ `op=icon-fallback` census (emitted from `app/graph/page.tsx` across the initial
177
+ render and every live-update poll) names any label that reached the canvas with
178
+ no glyph; an empty census is the healthy signal, a non-empty one means a
179
+ code-emitted label that no static source declares and should be added to
180
+ `CODE_EMITTED_NODE_LABELS`.
181
+
132
182
  ### Artefacts and files
133
183
 
134
184
  | Mount | Purpose |
@@ -408,7 +408,7 @@ Standing rules the operator gives over a channel are `Preference` nodes. Two mec
408
408
 
409
409
  **Observability.** `[preference-write] op=reconcile … mode=<reinforce|update|contradict|new> similarity=…` — a stream of `mode=new` for subjects that already exist is the duplicate-minting signature. `[preference-inject] op=inject channel=<wa|tg|web> injected=<N> owner=<8> source=<owner|no-owner|owner-error|fetch-error>` — injection selects only the account owner's admin preferences, so `source=owner injected=0` is a genuinely empty owner, `source=no-owner` is an account with no seeded owner (injects nothing, a missed owner seeding), and `injected=0 source=owner` on an account whose owner holds rules is the write-only regression re-appearing. `[preference-inject] op=reconcile accountId=… stored=… injected=…` — the standing reconciliation audit (claude-session-manager, 5-minute tick) flags any account whose stored active count diverges from what injection surfaces; injection now surfaces every stored active preference verbatim, so `stored == injected` is the healthy signal and any gap is a dropped-row regression (a reintroduced cap, filter, or truncation). A clean pass logs `op=reconcile drifted=0`, the no-event detector.
410
410
 
411
- **Consult-before-send gate.** Injection (above) puts the owner's active rules in front of every channel turn, but the growing layer-2 `Preference` set is read on demand with `profile-read`, a per-turn judgement the agent can skip, the miss that dropped a customer signature on a delivered document. `hooks/preference-consult-gate.sh` (admin plugin) is the deliverable-boundary enforcement: a PreToolUse gate on customer-facing document deliverables (`browser-pdf-save`/`SendUserFile` to `memory/users/<phone>/documents/`, and email/Outlook sends carrying attachments) that blocks (exit 2) when no `profile-read` ran after the last user message in the transcript. It fires on documents only, never on casual replies. **Observability:** `[preference-gate] op=allow tool=<name> consulted=true` on an allowed document deliverable, `op=bypass tool=<name> detail=no profile-read this turn` on a block; a stream of `op=bypass` is the un-consulted-send signature. The advisory companion `hooks/preference-consult-directive.sh` injects the two-layer architecture reminder every turn (`[pref-wrapper] op=inject`). The placement half, promoting finished deliverables out of `output/` scratch, is counted by the `data-manager` reconcile audit (`scratch-refs`, `stranded`); making that audit run on a standing periodic cadence is a filed follow-up.
411
+ **Consult-before-send gate.** Injection (above) puts the owner's active rules in front of every channel turn, but the growing layer-2 `Preference` set is read on demand with `profile-read`, a per-turn judgement the agent can skip, the miss that dropped a customer signature on a delivered document. `hooks/preference-consult-gate.sh` (admin plugin) is the deliverable-boundary enforcement: a PreToolUse gate on customer-facing document deliverables (`browser-pdf-save`/`SendUserFile` to `memory/users/<phone>/documents/`, and email/Outlook sends carrying attachments) that blocks (exit 2) when no `profile-read` ran after the last user message in the transcript. It fires on documents only, never on casual replies. **Observability:** `[preference-gate] op=allow tool=<name> consulted=true` on an allowed document deliverable, `op=bypass tool=<name> detail=no profile-read this turn` on a block; a stream of `op=bypass` is the un-consulted-send signature. The advisory companion `hooks/preference-consult-directive.sh` injects the two-layer architecture reminder every turn (`[pref-wrapper] op=inject`). The placement half, promoting finished deliverables out of `output/` scratch, is counted by the `data-manager` reconcile audit (`scratch-refs`, `stranded`); making that audit run on a standing periodic cadence is a filed follow-up. The inbound mirror is the `uploads/` intake inbox: an uploaded file lands at `uploads/<attachmentId>/` and is promoted to its canonical entity bucket when its graph node is created, so a persisted node reference resolving into `uploads/` is intake drift, counted by the same reconcile audit as `stranded-intake`. Unlike `output/`, `uploads/` is never rebuilt by a tool, so the `data-manager` may promote a file out of it. Beyond placement and intake, the same audit counts intra-folder sprawl inside each operator-data entity folder from disk structure alone: superseded version copies (`<base>.ext` and `<base>-vN.ext` are one deliverable, all but the newest counted `versioned`), format twins (a source `.html`/`.md` render kept beside its delivered `.pdf` of the same basename, counted `format-twins`), and subfolders nested under `projects/<name>/` or `contacts/<name>/` that the flat rule forbids (counted `stray-subdir`). These are read-only counts; removal or flattening happens only on a `data-manager` stewardship dispatch, each paired with a `database-operator` reference update. The reconcile output contract is `unreachable=N broken-refs=M scratch-refs=P stranded=Q stranded-intake=R versioned=S format-twins=T stray-subdir=U`.
412
412
 
413
413
  ---
414
414
 
@@ -0,0 +1,7 @@
1
+ # summarise skill
2
+
3
+ A workflow skill bundled in the admin plugin on every {{productName}} install.
4
+
5
+ **`summarise`** restates the agent's own immediately-preceding turn under the plainly rules: precise, concise, plain English, every claim already evidenced in the turn. Three-sentence ceiling for an ordinary turn. When that turn concluded an investigation, the summary carries exactly two parts in order: the diagnosis (the cause, with its evidence) and the proposed resolution (or an explicit note that none was reached). Triggers: "/summarise", "summarise", "recap", "in short".
6
+
7
+ It never adds content, never hedges, and never ends on a follow-up offer. For summarising someone else's document or text, `plainly` fits better; a turn already one or two plain sentences needs no compression.
@@ -26,6 +26,32 @@ the client data portal, and case-sensitive matching. The write guard blocks a ba
26
26
  name as you create it, and the standing reconcile counts any that predate the
27
27
  rule. Renaming an existing bad name goes through the `data-manager` specialist.
28
28
 
29
+ ## Intra-folder hygiene
30
+
31
+ The `projects/<name>/` and `contacts/<name>/` entity folders hold one current
32
+ copy of each deliverable, in one canonical format, with no subtree. Three rules
33
+ keep them that way; the standing reconcile counts any that predate them, and
34
+ `data-manager` clears them on a stewardship dispatch. (`documents/` is exempt: it
35
+ may hold one document-folder deep per the buckets above, so these rules do not
36
+ apply there.)
37
+
38
+ - **Current version only.** `<base>.ext` and `<base>-vN.ext` (for example
39
+ `charlotte-deck.pdf` and `charlotte-deck-v2.pdf` through
40
+ `charlotte-deck-v6.pdf`) are versions of one deliverable, not separate files.
41
+ Keep the highest-numbered version (the unsuffixed base is the original, so any
42
+ `-vN` supersedes it); the superseded copies are removed through `data-manager`.
43
+ - **One canonical format.** A delivered `.pdf` and its source render sharing the
44
+ same basename (`charlotte-deck.html` beside `charlotte-deck.pdf`,
45
+ `invoice-rbt-2026-005.html` beside `invoice-rbt-2026-005.pdf`) are one
46
+ deliverable in two formats. The pdf is the deliverable; the `.html` or `.md`
47
+ render that produced it is not a second one and is removed through
48
+ `data-manager`.
49
+ - **No subfolder.** No folder nests under `projects/<name>/` or
50
+ `contacts/<name>/`; files live directly inside the entity folder. This is the
51
+ flat rule from the buckets above, restated as a standing invariant: the write
52
+ guard blocks the depth as you create it, and the reconcile counts any subtree
53
+ that predates the guard or was created off-path.
54
+
29
55
  ## Tool-owned (off-limits — never reorganise)
30
56
 
31
57
  Finished deliverables must be promoted out of `output/` into `documents/` or
@@ -33,8 +59,17 @@ Finished deliverables must be promoted out of `output/` into `documents/` or
33
59
  last resort, and a graph reference must never point into it (the next tool write
34
60
  rebuilds the tree and the reference dangles).
35
61
 
62
+ `uploads/` is a temporary intake inbox, not rebuilt scratch. A file lands at
63
+ `uploads/<attachmentId>/` on upload and is promoted to its canonical entity
64
+ bucket when its graph node is created: a Person or Organization file to
65
+ `contacts/<name>/`, a Project file to `projects/<name>/`, anything else to
66
+ `documents/`. A persisted graph reference must never point into `uploads/`.
67
+ Unlike `output/`, each upload writes its own `uploads/<attachmentId>/` dir and no
68
+ tool walks or rebuilds the subtree, so promoting a file out of it through the
69
+ `data-manager` specialist is safe.
70
+
36
71
  The internal structure of these is owned by the writing tool and is recreated on
37
- the next write: `url-get/`, `output/`, `generated/`, `extracted/`, `uploads/`,
72
+ the next write: `url-get/`, `output/`, `generated/`, `extracted/`,
38
73
  any published/served tree (`sites/`, `public/`), `agents/`, `specialists/`, and
39
74
  platform-internal areas (`cache/`, `secrets/`, `state/`, `logs/`, `tmp/`).
40
75
  `.quarantine/` is a historical store. An earlier version of the schema reconcile
@@ -14,6 +14,8 @@ You are the steward of this account's data directory. Admin dispatches you throu
14
14
  - The account data directory is organised consistently with the graph ontology. Before the first move of a session, Read `platform/plugins/memory/references/schema-base.md`; when `brand.json#vertical` is set, also Read `platform/plugins/memory/references/schema-<vertical>.md`. Directory structure and file placement should make sense to someone navigating by the graph: files that belong to a Project, Person, or document node live where the node's references say they live.
15
15
  - Every operator file is reachable from a graph node, and every graph file-reference resolves to a real path. `memory-search` is how you resolve which nodes reference a file — search for the filename and the path before deciding anything about it.
16
16
  - A file is never moved without the referencing nodes being updated in the same pass. Moves and reference updates are paired operations: for each move, dispatch `database-operator` via the Task tool with a brief naming the node (elementId from your `memory-search`), the property holding the old path, and the new path. You have no graph-write tools by design — every graph mutation flows through database-operator.
17
+ - A file in the `uploads/` intake inbox whose graph node exists is promoted to its canonical bucket (Person or Organization to `contacts/<name>/`, Project to `projects/<name>/`, else `documents/`), paired with a `database-operator` `sourcePath` update in the same pass, exactly as any other move. `uploads/` is a temporary landing area, not a tool-owned tree, so moving a file out of it is not a reorganisation of a system-convention directory.
18
+ - Superseded versions and redundant format twins are removed, and a subfolder nested under `projects/<name>/` or `contacts/<name>/` is flattened or re-filed, when a stewardship brief asks for it. Each removal or move pairs with a `database-operator` reference update in the same pass, exactly as any other move: the surviving current render keeps the node reference, and a removed file's node is repointed or its reference dropped. Removal is destructive, so it happens only on an explicit stewardship dispatch, never in the read-only reconcile audit.
17
19
 
18
20
  ## Off-limits: system-convention directories
19
21
 
@@ -21,13 +23,13 @@ Directories whose layout is owned by tools are not yours to reorganise — a too
21
23
 
22
24
  ## The reconcile audit brief
23
25
 
24
- When admin's brief names a reconcile audit, the pass is read-only: walk the operator-file areas of the account directory, resolve each file against the graph via `memory-search`, and count (a) files unreachable from any graph node, (b) graph file-references that do not resolve to a real path, (c) graph file-references that resolve into a tool-owned scratch dir (`output/`, `generated/`, `extracted/`, `url-get/`), which the next tool write rebuilds so the reference is fragile by construction, and (d) deliverables a node references that resolve only under a scratch dir and were never promoted to `documents/` or `projects/`. Move nothing, dispatch nothing. The audit exists so a rising count is visible before anyone asks for a cleanup.
26
+ When admin's brief names a reconcile audit, the pass is read-only: walk the operator-file areas of the account directory, resolve each file against the graph via `memory-search`, and count (a) files unreachable from any graph node, (b) graph file-references that do not resolve to a real path, (c) graph file-references that resolve into a tool-owned scratch dir (`output/`, `generated/`, `extracted/`, `url-get/`), which the next tool write rebuilds so the reference is fragile by construction, (d) deliverables a node references that resolve only under a scratch dir and were never promoted to `documents/` or `projects/`, and (e) graph file-references that resolve into the `uploads/` intake inbox, which is a temporary landing area a file should have been promoted out of when its node was created. The walk now includes `uploads/`; a file under `uploads/` that no node references counts under (a) unreachable. Then, per operator-data entity folder (`projects/<name>/`, `contacts/<name>/`), count three intra-folder drifts from disk structure alone, no graph resolution needed: (f) files in a version group, where `<base>.ext` and `<base>-vN.ext` share a basename stem and extension and all but the newest are superseded, counting every superseded copy as `versioned`; (g) format twins, a source-render file (`.html`, `.md`) whose delivered `.pdf` twin of the same basename sits in the same folder, counting each such render as `format-twins`; (h) stray subfolders nested under an entity folder, which the flat rule forbids, counting each subfolder as `stray-subdir`. Move nothing, dispatch nothing. The audit exists so a rising count is visible before anyone asks for a cleanup.
25
27
 
26
28
  When the vertical's `schema-<vertical>.md` declares a **published / served tree** with a Filesystem ↔ graph section (e.g. the per-listing site target with a URL ↔ path rule and an artefact → graph-reference table), the audit also walks that tree. The graph references it by hosted URL, not local path, so resolution runs through the section's URL ↔ path rule: for each served file, derive its hosted URL and check that some node references it; for each node URL **that matches the section's hosted-URL prefix**, derive its local path and check the file exists. Count served files matching no node reference in `unreachable` and prefix-matching node URLs resolving to no served file in `broken-refs`. Node URLs the rule cannot resolve — externally-hosted URLs (a CDN image, a third-party portal link) carry no served-tree prefix — are outside this audit and are never counted as broken. Honour the section's "served but intentionally unreferenced (tool / collateral)" list — those classes carry no reference by design and are never orphans, the same exclusion you already apply to the `url-get/` cache. This walk is still read-only: the served tree is a publish write target (off-limits to moves, above); the audit only counts drift, it never reorganises.
27
29
 
28
30
  ## Output contract
29
31
 
30
- End every dispatch with a one-line machine-greppable summary as the last line of your reply. For stewardship work: `moves=N ref-updates=M skipped=<reason|none>`. For the reconcile audit: `unreachable=N broken-refs=M scratch-refs=P stranded=Q`. If a brief is ambiguous about whether a directory is operator data or a system convention, name the ambiguity in your reply instead of guessing.
32
+ End every dispatch with a one-line machine-greppable summary as the last line of your reply. For stewardship work: `moves=N ref-updates=M skipped=<reason|none>`. For the reconcile audit: `unreachable=N broken-refs=M scratch-refs=P stranded=Q stranded-intake=R versioned=S format-twins=T stray-subdir=U`. If a brief is ambiguous about whether a directory is operator data or a system convention, name the ambiguity in your reply instead of guessing.
31
33
 
32
34
  ## Review gates
33
35
 
@@ -5,12 +5,12 @@
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
6
  <title>Activity — Maxy</title>
7
7
  <link rel="icon" href="/favicon.ico">
8
- <script type="module" crossorigin src="/assets/activity-CKUYOLd2.js"></script>
9
- <link rel="modulepreload" crossorigin href="/assets/chunk-fQC6bEKA.js">
10
- <link rel="modulepreload" crossorigin href="/assets/useSubAccountSwitcher-Bc7XHr0L.js">
11
- <link rel="modulepreload" crossorigin href="/assets/AdminShell-BFVVkWFC.js">
12
- <link rel="modulepreload" crossorigin href="/assets/triangle-alert-DumCWC08.js">
13
- <link rel="stylesheet" crossorigin href="/assets/useSubAccountSwitcher-DPHQJO7E.css">
8
+ <script type="module" crossorigin src="/assets/activity-DZFYDHNA.js"></script>
9
+ <link rel="modulepreload" crossorigin href="/assets/chunk-CCr-iYLO.js">
10
+ <link rel="modulepreload" crossorigin href="/assets/useSubAccountSwitcher-iQGqNJhI.js">
11
+ <link rel="modulepreload" crossorigin href="/assets/AdminShell-Gq8DU_ig.js">
12
+ <link rel="modulepreload" crossorigin href="/assets/triangle-alert-k8ulav-N.js">
13
+ <link rel="stylesheet" crossorigin href="/assets/useSubAccountSwitcher-fvBunpOC.css">
14
14
  <link rel="stylesheet" href="/brand-defaults.css">
15
15
  </head>
16
16
  <body>