@rubytech/create-maxy-code 0.1.488 → 0.1.490
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.
- package/package.json +1 -1
- package/payload/platform/docs/superpowers/plans/2026-07-22-task-1899-reconcile-report-only.md +748 -0
- package/payload/platform/docs/superpowers/plans/2026-07-22-task-1910-two-way-portal-exchange.md +1716 -0
- package/payload/platform/docs/superpowers/specs/2026-07-22-task-1899-reconcile-report-only-design.md +121 -0
- package/payload/platform/docs/superpowers/specs/2026-07-22-task-1910-two-way-portal-exchange-design.md +103 -0
- package/payload/platform/lib/graph-mcp/dist/__tests__/schema-cypher-parser.test.js +51 -0
- package/payload/platform/lib/graph-mcp/dist/__tests__/schema-cypher-parser.test.js.map +1 -1
- package/payload/platform/lib/graph-mcp/dist/schema-cypher-parser.d.ts +2 -2
- package/payload/platform/lib/graph-mcp/dist/schema-cypher-parser.d.ts.map +1 -1
- package/payload/platform/lib/graph-mcp/dist/schema-cypher-parser.js +20 -4
- package/payload/platform/lib/graph-mcp/dist/schema-cypher-parser.js.map +1 -1
- package/payload/platform/lib/graph-mcp/src/__tests__/schema-cypher-parser.test.ts +55 -0
- package/payload/platform/lib/graph-mcp/src/schema-cypher-parser.ts +19 -4
- package/payload/platform/lib/graph-style/dist/index.d.ts +24 -0
- package/payload/platform/lib/graph-style/dist/index.d.ts.map +1 -1
- package/payload/platform/lib/graph-style/dist/index.js +25 -1
- package/payload/platform/lib/graph-style/dist/index.js.map +1 -1
- package/payload/platform/lib/graph-style/src/__tests__/additional-base-labels.test.ts +22 -0
- package/payload/platform/lib/graph-style/src/index.ts +27 -0
- package/payload/platform/lib/storage-broker/dist/__tests__/audit.test.js +15 -0
- package/payload/platform/lib/storage-broker/dist/__tests__/audit.test.js.map +1 -1
- package/payload/platform/lib/storage-broker/dist/audit.d.ts +34 -1
- package/payload/platform/lib/storage-broker/dist/audit.d.ts.map +1 -1
- package/payload/platform/lib/storage-broker/dist/audit.js +28 -4
- package/payload/platform/lib/storage-broker/dist/audit.js.map +1 -1
- package/payload/platform/lib/storage-broker/dist/cf-exec.d.ts +13 -0
- package/payload/platform/lib/storage-broker/dist/cf-exec.d.ts.map +1 -1
- package/payload/platform/lib/storage-broker/dist/cf-exec.js +22 -0
- package/payload/platform/lib/storage-broker/dist/cf-exec.js.map +1 -1
- package/payload/platform/lib/storage-broker/src/__tests__/audit.test.ts +21 -0
- package/payload/platform/lib/storage-broker/src/audit.ts +67 -3
- package/payload/platform/lib/storage-broker/src/cf-exec.ts +48 -0
- package/payload/platform/neo4j/schema.cypher +105 -0
- package/payload/platform/plugins/admin/skills/agent-builder/references/agent-pattern.md +34 -5
- package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +8 -2
- package/payload/platform/plugins/cloudflare/PLUGIN.md +22 -2
- package/payload/platform/plugins/cloudflare/bin/portal-index-push.mjs +161 -41
- package/payload/platform/plugins/cloudflare/mcp/__tests__/portal-delete-withdraw.test.ts +95 -0
- package/payload/platform/plugins/cloudflare/mcp/__tests__/portal-directory-listing.test.ts +75 -13
- package/payload/platform/plugins/cloudflare/mcp/__tests__/portal-index-push.test.ts +216 -16
- package/payload/platform/plugins/cloudflare/mcp/__tests__/portal-indexed-download.test.ts +38 -2
- package/payload/platform/plugins/cloudflare/mcp/__tests__/portal-moved-download.test.ts +83 -0
- package/payload/platform/plugins/cloudflare/mcp/__tests__/portal-schema-shape.test.ts +48 -0
- package/payload/platform/plugins/cloudflare/mcp/__tests__/portal-sign.test.ts +62 -0
- package/payload/platform/plugins/cloudflare/mcp/__tests__/portal-target.test.ts +103 -0
- package/payload/platform/plugins/cloudflare/mcp/__tests__/portal-upload-multipart.test.ts +211 -0
- package/payload/platform/plugins/cloudflare/mcp/__tests__/portal-upload-target.test.ts +168 -0
- package/payload/platform/plugins/cloudflare/mcp/__tests__/portal-uploads-listing.test.ts +138 -0
- package/payload/platform/plugins/cloudflare/mcp/__tests__/template-config.test.ts +179 -0
- package/payload/platform/plugins/cloudflare/mcp/__tests__/upload-route.test.ts +14 -13
- package/payload/platform/plugins/cloudflare/skills/data-portal/SKILL.md +87 -13
- package/payload/platform/plugins/cloudflare/skills/data-portal/schema.sql +87 -9
- package/payload/platform/plugins/cloudflare/skills/data-portal/template/functions/api/_lib/portal-sign.mjs +74 -0
- package/payload/platform/plugins/cloudflare/skills/data-portal/template/functions/api/_lib/target.ts +91 -0
- package/payload/platform/plugins/cloudflare/skills/data-portal/template/functions/api/_lib/types.ts +23 -0
- package/payload/platform/plugins/cloudflare/skills/data-portal/template/functions/api/delete.ts +45 -0
- package/payload/platform/plugins/cloudflare/skills/data-portal/template/functions/api/download.ts +72 -5
- package/payload/platform/plugins/cloudflare/skills/data-portal/template/functions/api/files.ts +80 -13
- package/payload/platform/plugins/cloudflare/skills/data-portal/template/functions/api/upload.ts +358 -16
- package/payload/platform/plugins/cloudflare/skills/data-portal/template/index.html +12 -1
- package/payload/platform/plugins/cloudflare/skills/data-portal/template/portal.css +45 -0
- package/payload/platform/plugins/cloudflare/skills/data-portal/template/portal.js +258 -21
- package/payload/platform/plugins/docs/references/calendar-booking.md +5 -1
- package/payload/platform/plugins/docs/references/internals.md +2 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/schema-cypher-drift.test.js +61 -31
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/schema-cypher-drift.test.js.map +1 -1
- package/payload/platform/plugins/scheduling/PLUGIN.md +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/index.js +2 -2
- package/payload/platform/plugins/scheduling/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/booking-reconcile.test.js +70 -5
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/booking-reconcile.test.js.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/getUserTimezone.test.js +31 -6
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/getUserTimezone.test.js.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/lib/booking-reconcile.d.ts +31 -4
- package/payload/platform/plugins/scheduling/mcp/dist/lib/booking-reconcile.d.ts.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/lib/booking-reconcile.js +18 -7
- package/payload/platform/plugins/scheduling/mcp/dist/lib/booking-reconcile.js.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/lib/neo4j.d.ts +15 -6
- package/payload/platform/plugins/scheduling/mcp/dist/lib/neo4j.d.ts.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/lib/neo4j.js +24 -11
- package/payload/platform/plugins/scheduling/mcp/dist/lib/neo4j.js.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/check-due-events.js +5 -122
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/check-due-events.js.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/reconcile-bookings.js +84 -5
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/reconcile-bookings.js.map +1 -1
- package/payload/platform/scripts/__tests__/quarantine-restore.test.sh +143 -0
- package/payload/platform/scripts/quarantine-restore.mjs +175 -0
- package/payload/platform/services/claude-session-manager/dist/account-dir-schema-reconcile.d.ts +24 -7
- package/payload/platform/services/claude-session-manager/dist/account-dir-schema-reconcile.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/account-dir-schema-reconcile.js +52 -74
- package/payload/platform/services/claude-session-manager/dist/account-dir-schema-reconcile.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/index.js +11 -8
- package/payload/platform/services/claude-session-manager/dist/index.js.map +1 -1
- package/payload/platform/services/whatsapp-channel/dist/notification.d.ts +32 -0
- package/payload/platform/services/whatsapp-channel/dist/notification.d.ts.map +1 -1
- package/payload/platform/services/whatsapp-channel/dist/notification.js +32 -2
- package/payload/platform/services/whatsapp-channel/dist/notification.js.map +1 -1
- package/payload/platform/services/whatsapp-channel/dist/targets.d.ts +6 -1
- package/payload/platform/services/whatsapp-channel/dist/targets.d.ts.map +1 -1
- package/payload/platform/services/whatsapp-channel/dist/targets.js +6 -0
- package/payload/platform/services/whatsapp-channel/dist/targets.js.map +1 -1
- package/payload/platform/templates/account-schema/SCHEMA.md +7 -4
- package/payload/platform/templates/agents/admin/IDENTITY.md +1 -1
- package/payload/platform/templates/agents/passive/IDENTITY.md +6 -0
- package/payload/platform/templates/agents/public/IDENTITY.md +6 -0
- package/payload/platform/templates/specialists/agents/citation-auditor.md +6 -0
- package/payload/platform/templates/specialists/agents/coding-assistant.md +6 -0
- package/payload/platform/templates/specialists/agents/compiled-truth-rewriter.md +6 -0
- package/payload/platform/templates/specialists/agents/content-producer.md +6 -0
- package/payload/platform/templates/specialists/agents/data-manager.md +6 -0
- package/payload/platform/templates/specialists/agents/database-operator.md +6 -0
- package/payload/platform/templates/specialists/agents/librarian.md +6 -0
- package/payload/platform/templates/specialists/agents/personal-assistant.md +6 -0
- package/payload/platform/templates/specialists/agents/project-manager.md +6 -0
- package/payload/platform/templates/specialists/agents/public-session-reviewer.md +6 -0
- package/payload/platform/templates/specialists/agents/research-assistant.md +7 -1
- package/payload/platform/templates/specialists/agents/typed-edge-classifier.md +6 -0
- package/payload/premium-plugins/writer-craft/agents/writer-craft--manuscript-reviewer.md +6 -0
- package/payload/server/{chunk-REWXLAPC.js → chunk-KZFE3MOY.js} +52 -4
- package/payload/server/public/activity.html +5 -6
- package/payload/server/public/assets/AdminLoginScreens-DgVFsHtd.js +1 -0
- package/payload/server/public/assets/AdminShell-BwNqwb4k.js +2 -0
- package/payload/server/public/assets/activity-2DL2DwLr.js +1 -0
- package/payload/server/public/assets/admin-COe0dT2U.js +1 -0
- package/payload/server/public/assets/{arc-D-QzyndF.js → arc-CFMJyMm-.js} +1 -1
- package/payload/server/public/assets/architecture-YZFGNWBL-Cypt5qHL.js +1 -0
- package/payload/server/public/assets/{architectureDiagram-Q4EWVU46-W7f0Us4S.js → architectureDiagram-Q4EWVU46-CiSSgdL8.js} +1 -1
- package/payload/server/public/assets/{blockDiagram-DXYQGD6D-CfPS4FXg.js → blockDiagram-DXYQGD6D-_sXtIW5c.js} +1 -1
- package/payload/server/public/assets/{bot-DnuyRsLS.js → bot-CpBuDQlF.js} +1 -1
- package/payload/server/public/assets/{browser-q4EFqKvR.js → browser-CSApuZrv.js} +1 -1
- package/payload/server/public/assets/{c4Diagram-AHTNJAMY-Dq60cLVI.js → c4Diagram-AHTNJAMY-Bh7OAMXs.js} +1 -1
- package/payload/server/public/assets/{calendar-C-q9kWmA.js → calendar-DlvwZnS7.js} +1 -1
- package/payload/server/public/assets/channel-BMyXE4zc.js +1 -0
- package/payload/server/public/assets/chat-DWvYv9jq.js +1 -0
- package/payload/server/public/assets/chevron-down-Dnuuw6Q2.js +1 -0
- package/payload/server/public/assets/chevron-left-CC4zmWOr.js +1 -0
- package/payload/server/public/assets/chevron-right-CG_lHTx_.js +1 -0
- package/payload/server/public/assets/{chunk-2KRD3SAO-CXDjuDN0.js → chunk-2KRD3SAO-D4fktAwW.js} +1 -1
- package/payload/server/public/assets/{chunk-336JU56O-926ox0tj.js → chunk-336JU56O-CcQoKH2G.js} +2 -2
- package/payload/server/public/assets/chunk-426QAEUC-Xu6JKP4d.js +1 -0
- package/payload/server/public/assets/{chunk-4BX2VUAB-C2nrPCfN.js → chunk-4BX2VUAB-C13Wz3zS.js} +1 -1
- package/payload/server/public/assets/{chunk-4TB4RGXK-CuW3FOR6.js → chunk-4TB4RGXK-BL48D7gN.js} +1 -1
- package/payload/server/public/assets/{chunk-55IACEB6-B4_-In90.js → chunk-55IACEB6-TmOKdy8a.js} +1 -1
- package/payload/server/public/assets/{chunk-5FUZZQ4R-DnYGr9CP.js → chunk-5FUZZQ4R-DZ_jSF6N.js} +1 -1
- package/payload/server/public/assets/{chunk-5PVQY5BW-BYCOi9bf.js → chunk-5PVQY5BW-De4G1vfV.js} +1 -1
- package/payload/server/public/assets/{chunk-67CJDMHE-CBxsC4JP.js → chunk-67CJDMHE-Cif9Uu5C.js} +1 -1
- package/payload/server/public/assets/{chunk-7N4EOEYR-CAACaqFr.js → chunk-7N4EOEYR-WjTHCXnd.js} +1 -1
- package/payload/server/public/assets/{chunk-AA7GKIK3-BoamZdS6.js → chunk-AA7GKIK3-CwCfdQmb.js} +1 -1
- package/payload/server/public/assets/{chunk-BSJP7CBP-CHk82eFy.js → chunk-BSJP7CBP-9A67yXwT.js} +1 -1
- package/payload/server/public/assets/{chunk-CIAEETIT-DdeW9zxl.js → chunk-CIAEETIT-DPMiWSBi.js} +1 -1
- package/payload/server/public/assets/{chunk-EDXVE4YY-DJqSRv2w.js → chunk-EDXVE4YY-C2f_Qvx-.js} +1 -1
- package/payload/server/public/assets/{chunk-ENJZ2VHE-BRi4BwM5.js → chunk-ENJZ2VHE-B-Q9EbSQ.js} +1 -1
- package/payload/server/public/assets/{chunk-FMBD7UC4-OJp2CtUE.js → chunk-FMBD7UC4-Bmt_ZRlV.js} +1 -1
- package/payload/server/public/assets/{chunk-FOC6F5B3-BUAQ6spG.js → chunk-FOC6F5B3-DfGJGxpX.js} +1 -1
- package/payload/server/public/assets/{chunk-ICPOFSXX-DeRggD-Q.js → chunk-ICPOFSXX-Wyym1Q8K.js} +2 -2
- package/payload/server/public/assets/{chunk-K5T4RW27-EHiXZ8Py.js → chunk-K5T4RW27-DMpoaqw2.js} +1 -1
- package/payload/server/public/assets/{chunk-KGLVRYIC-tOajPcpn.js → chunk-KGLVRYIC-DYUFgri2.js} +1 -1
- package/payload/server/public/assets/{chunk-LIHQZDEY-uwO6oHIU.js → chunk-LIHQZDEY-x3LmeKmx.js} +1 -1
- package/payload/server/public/assets/{chunk-ORNJ4GCN-Dpa1_BCA.js → chunk-ORNJ4GCN-BeuNAWkW.js} +1 -1
- package/payload/server/public/assets/{chunk-OYMX7WX6-ChdDqaQL.js → chunk-OYMX7WX6-BUWVAfQA.js} +1 -1
- package/payload/server/public/assets/chunk-QZHKN3VN-7_kS3RBy.js +1 -0
- package/payload/server/public/assets/{chunk-U2HBQHQK-5JgWosPv.js → chunk-U2HBQHQK-DKS5adQo.js} +1 -1
- package/payload/server/public/assets/{chunk-X2U36JSP-BGJqysB2.js → chunk-X2U36JSP-CT_UM4IN.js} +1 -1
- package/payload/server/public/assets/{chunk-XPW4576I-GERE2ekx.js → chunk-XPW4576I-ByFnN4JO.js} +1 -1
- package/payload/server/public/assets/{chunk-YZCP3GAM-BskqX1V5.js → chunk-YZCP3GAM-DCetC20J.js} +1 -1
- package/payload/server/public/assets/{chunk-ZZ45TVLE-DG1iY-L9.js → chunk-ZZ45TVLE-Bh1QIv8N.js} +1 -1
- package/payload/server/public/assets/classDiagram-6PBFFD2Q-DJqbNcuC.js +1 -0
- package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-CGglqfQA.js +1 -0
- package/payload/server/public/assets/clone-BiTMTzp_.js +1 -0
- package/payload/server/public/assets/{cose-bilkent-S5V4N54A-C8Rl4ykh.js → cose-bilkent-S5V4N54A-DgLEaApT.js} +1 -1
- package/payload/server/public/assets/{dagre-KV5264BT-C0YrJPF3.js → dagre-KV5264BT-BfkUPrEV.js} +1 -1
- package/payload/server/public/assets/{dagre-DTlv-FFy.js → dagre-unhMIyVo.js} +1 -1
- package/payload/server/public/assets/data-CJbC_cVy.js +1 -0
- package/payload/server/public/assets/{diagram-5BDNPKRD-Dfk5oepI.js → diagram-5BDNPKRD-BjjwcTD9.js} +1 -1
- package/payload/server/public/assets/{diagram-G4DWMVQ6-QRj55DiB.js → diagram-G4DWMVQ6-BbgWtVSS.js} +1 -1
- package/payload/server/public/assets/{diagram-MMDJMWI5-BAPKsohU.js → diagram-MMDJMWI5-DyAnkWGy.js} +1 -1
- package/payload/server/public/assets/{diagram-TYMM5635-C3iZXPkC.js → diagram-TYMM5635-SAMMoLxq.js} +1 -1
- package/payload/server/public/assets/{erDiagram-SMLLAGMA-DIbI7g_5.js → erDiagram-SMLLAGMA-Cvo-5-fD.js} +1 -1
- package/payload/server/public/assets/{flatten-DlAJeN0L.js → flatten-BXzaHgtL.js} +1 -1
- package/payload/server/public/assets/{flowDiagram-DWJPFMVM-l3eJJr8R.js → flowDiagram-DWJPFMVM-y3o2Ycld.js} +1 -1
- package/payload/server/public/assets/{ganttDiagram-T4ZO3ILL-aaEzHxIq.js → ganttDiagram-T4ZO3ILL-BEm_CQaK.js} +1 -1
- package/payload/server/public/assets/gitGraph-7Q5UKJZL-C_-XjxLd.js +1 -0
- package/payload/server/public/assets/{gitGraphDiagram-UUTBAWPF-Di-1eg_m.js → gitGraphDiagram-UUTBAWPF-CqmOxS7F.js} +1 -1
- package/payload/server/public/assets/{graph-CNgt7ygi.js → graph-kaxS9a_7.js} +2 -2
- package/payload/server/public/assets/graph-labels-GgejRtgV.js +1 -0
- package/payload/server/public/assets/{graphlib-D5awC2yp.js → graphlib-_HfbQcBf.js} +1 -1
- package/payload/server/public/assets/info-OMHHGYJF-BH8yDCfL.js +1 -0
- package/payload/server/public/assets/infoDiagram-42DDH7IO-DyDXyJ6U.js +2 -0
- package/payload/server/public/assets/{isEmpty-Dh1dF-tM.js → isEmpty-uB1dEu_Z.js} +1 -1
- package/payload/server/public/assets/{ishikawaDiagram-UXIWVN3A-CLOd_cNf.js → ishikawaDiagram-UXIWVN3A-C_d0vcZC.js} +1 -1
- package/payload/server/public/assets/{journeyDiagram-VCZTEJTY-cGyfW6Tg.js → journeyDiagram-VCZTEJTY-DsjRBqlW.js} +1 -1
- package/payload/server/public/assets/{kanban-definition-6JOO6SKY-CRTKWPlh.js → kanban-definition-6JOO6SKY-BwtK6LS-.js} +1 -1
- package/payload/server/public/assets/{line-B8Brm0TD.js → line-szBCFOLJ.js} +1 -1
- package/payload/server/public/assets/{linear-BHnSKXBe.js → linear-X3O7lwK3.js} +1 -1
- package/payload/server/public/assets/{maximize-2-k3Fu5hdP.js → maximize-2-CPq3nwZA.js} +1 -1
- package/payload/server/public/assets/{mermaid-parser.core-BtNpdQct.js → mermaid-parser.core-BavvMAhU.js} +2 -2
- package/payload/server/public/assets/{mermaid.core-BY87anQZ.js → mermaid.core-Br2F9Hdm.js} +3 -3
- package/payload/server/public/assets/{mindmap-definition-QFDTVHPH-rUSv9qN1.js → mindmap-definition-QFDTVHPH-4qlwXoJ-.js} +1 -1
- package/payload/server/public/assets/operator-CldXXilj.js +1 -0
- package/payload/server/public/assets/{ordinal-Bnl5GL-x.js → ordinal-CZY9laxg.js} +1 -1
- package/payload/server/public/assets/packet-4T2RLAQJ-Bajcns4r.js +1 -0
- package/payload/server/public/assets/{page-Bvx0A2jB.js → page-BE6DkJP9.js} +1 -1
- package/payload/server/public/assets/{page-DhwPG5Zf.js → page-BIwdWXnv.js} +3 -3
- package/payload/server/public/assets/{pdf-render-CrpAi4Ba.js → pdf-render-H884L3EI.js} +1 -1
- package/payload/server/public/assets/pie-ZZUOXDRM-D7hjM5w1.js +1 -0
- package/payload/server/public/assets/{pieDiagram-DEJITSTG-Cjn9IP-C.js → pieDiagram-DEJITSTG-BbXryIg0.js} +1 -1
- package/payload/server/public/assets/{public-BJ5Oo2Fw.js → public-BKQEQGPE.js} +1 -1
- package/payload/server/public/assets/{quadrantDiagram-34T5L4WZ-BkSHkSsn.js → quadrantDiagram-34T5L4WZ-BHLjcvh3.js} +1 -1
- package/payload/server/public/assets/radar-PYXPWWZC-B46d5x7a.js +1 -0
- package/payload/server/public/assets/{reduce-Bp5oNw1n.js → reduce-BGQzAenn.js} +1 -1
- package/payload/server/public/assets/{requirementDiagram-MS252O5E-hLwgyB-F.js → requirementDiagram-MS252O5E-BXaVO0lk.js} +1 -1
- package/payload/server/public/assets/{rotate-ccw-D-ov5jM4.js → rotate-ccw-DieZzl10.js} +1 -1
- package/payload/server/public/assets/{sankeyDiagram-XADWPNL6-D6Dl1RIa.js → sankeyDiagram-XADWPNL6-CmN1s17Q.js} +1 -1
- package/payload/server/public/assets/{sequenceDiagram-FGHM5R23-j0QBqkbL.js → sequenceDiagram-FGHM5R23-DC08e5NY.js} +1 -1
- package/payload/server/public/assets/{stateDiagram-FHFEXIEX-TvEBNsuV.js → stateDiagram-FHFEXIEX-DnoQMuOW.js} +1 -1
- package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-CrstH34p.js +1 -0
- package/payload/server/public/assets/{tasks-DHMxTZVD.js → tasks-DYyCGz8W.js} +1 -1
- package/payload/server/public/assets/{time-entry-format-EmV5lz6o.js → time-entry-format-CC_9jFuR.js} +1 -1
- package/payload/server/public/assets/{timeline-definition-GMOUNBTQ-BfivOP-j.js → timeline-definition-GMOUNBTQ-eTn3uRaH.js} +1 -1
- package/payload/server/public/assets/treeView-SZITEDCU-3XQh5xQ1.js +1 -0
- package/payload/server/public/assets/treemap-W4RFUUIX-DwR-_l5z.js +1 -0
- package/payload/server/public/assets/{triangle-alert-CivyPfAL.js → triangle-alert-7mQfCQWP.js} +1 -1
- package/payload/server/public/assets/{useCopyFeedback-hbnPS-r9.js → useCopyFeedback-os7N0yCx.js} +1 -1
- package/payload/server/public/assets/useSubAccountSwitcher-Bgfqig1V.css +1 -0
- package/payload/server/public/assets/{useVoiceRecorder-QN9_GqB_.js → useVoiceRecorder-ZFwt5i6d.js} +2 -2
- package/payload/server/public/assets/{vennDiagram-DHZGUBPP-Bvfw9aFJ.js → vennDiagram-DHZGUBPP-DNx6o_ZQ.js} +1 -1
- package/payload/server/public/assets/wardley-RL74JXVD-DZp9oCGv.js +1 -0
- package/payload/server/public/assets/{wardleyDiagram-NUSXRM2D-C9h04G6b.js → wardleyDiagram-NUSXRM2D-BZOchqF3.js} +1 -1
- package/payload/server/public/assets/{xychartDiagram-5P7HB3ND-BMvi2m2q.js → xychartDiagram-5P7HB3ND-ZBEGH6ZI.js} +1 -1
- package/payload/server/public/browser.html +4 -4
- package/payload/server/public/calendar.html +6 -6
- package/payload/server/public/chat.html +14 -14
- package/payload/server/public/data.html +11 -11
- package/payload/server/public/graph.html +10 -11
- package/payload/server/public/index.html +16 -17
- package/payload/server/public/operator.html +15 -16
- package/payload/server/public/public.html +14 -14
- package/payload/server/public/tasks.html +6 -6
- package/payload/server/server.js +1836 -998
- package/payload/server/{src-JJ6PHO5I.js → src-UYDFMWF4.js} +1 -1
- package/payload/server/public/assets/AdminLoginScreens-Dic_fPt6.js +0 -1
- package/payload/server/public/assets/AdminShell-JN7rZ8aG.js +0 -2
- package/payload/server/public/assets/Checkbox-CB74kINA.js +0 -1
- package/payload/server/public/assets/activity-DbfpHg3i.js +0 -1
- package/payload/server/public/assets/admin-CThbXaYH.js +0 -1
- package/payload/server/public/assets/architecture-YZFGNWBL-CGvD4r9F.js +0 -1
- package/payload/server/public/assets/channel-Bpg-ADC0.js +0 -1
- package/payload/server/public/assets/chat-DcefIV9q.js +0 -1
- package/payload/server/public/assets/chevron-down-Blma_CKl.js +0 -1
- package/payload/server/public/assets/chevron-left-xeYDxFiC.js +0 -1
- package/payload/server/public/assets/chevron-right-BonbtjqJ.js +0 -1
- package/payload/server/public/assets/chunk-426QAEUC-1poG7Nd3.js +0 -1
- package/payload/server/public/assets/chunk-QZHKN3VN-GN1tIO8J.js +0 -1
- package/payload/server/public/assets/classDiagram-6PBFFD2Q-DMGVq8Jv.js +0 -1
- package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-DfddT8UV.js +0 -1
- package/payload/server/public/assets/clone-Cc9vFIEk.js +0 -1
- package/payload/server/public/assets/data-D5dp6nj6.js +0 -1
- package/payload/server/public/assets/gitGraph-7Q5UKJZL-DtjXE0UI.js +0 -1
- package/payload/server/public/assets/graph-labels-DA48HrRR.js +0 -1
- package/payload/server/public/assets/info-OMHHGYJF-CmArhGYf.js +0 -1
- package/payload/server/public/assets/infoDiagram-42DDH7IO-utWz7oZy.js +0 -2
- package/payload/server/public/assets/operator-8NVpNLeD.js +0 -1
- package/payload/server/public/assets/packet-4T2RLAQJ-BTfK0Zw4.js +0 -1
- package/payload/server/public/assets/pie-ZZUOXDRM-BvtPeMQY.js +0 -1
- package/payload/server/public/assets/radar-PYXPWWZC-D2UXvQgn.js +0 -1
- package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-Ur4eM6rb.js +0 -1
- package/payload/server/public/assets/treeView-SZITEDCU-0dE7FvM2.js +0 -1
- package/payload/server/public/assets/treemap-W4RFUUIX-BrwlXpr5.js +0 -1
- package/payload/server/public/assets/useSubAccountSwitcher-kwhG-aJj.css +0 -1
- package/payload/server/public/assets/wardley-RL74JXVD-CO9HdfJ-.js +0 -1
- /package/payload/server/public/assets/{_baseFor-BBkmqPEk.js → _baseFor-CMWvz0uA.js} +0 -0
- /package/payload/server/public/assets/{array-Bjtb2C_5.js → array-Cny_NT6T.js} +0 -0
- /package/payload/server/public/assets/{cytoscape.esm-B1dQ664k.js → cytoscape.esm-De0CCmB2.js} +0 -0
- /package/payload/server/public/assets/{defaultLocale-iwYA7ne8.js → defaultLocale-BahRht8U.js} +0 -0
- /package/payload/server/public/assets/{dist-CLmrZbY7.js → dist-C9D_xz81.js} +0 -0
- /package/payload/server/public/assets/{init-CxnBAgcf.js → init-DoIvjoJ_.js} +0 -0
- /package/payload/server/public/assets/{katex-DojHoLIr.js → katex-Bfn1OZEl.js} +0 -0
- /package/payload/server/public/assets/{path-JZiojYzQ.js → path-CTezU8L5.js} +0 -0
- /package/payload/server/public/assets/{preload-helper-CT1Z6Pdu.js → preload-helper-D_sPnjeL.js} +0 -0
- /package/payload/server/public/assets/{rough.esm-D5NinLFK.js → rough.esm-BJo4pvXu.js} +0 -0
- /package/payload/server/public/assets/{src-DQ0MSD6q.js → src-BTKk8KAG.js} +0 -0
- /package/payload/server/public/assets/{useSubAccountSwitcher-DotTrvX3.js → useSubAccountSwitcher-Q941cZYV.js} +0 -0
|
@@ -151,6 +151,14 @@ export interface DataPortalRow {
|
|
|
151
151
|
objectKey: string;
|
|
152
152
|
ingested: number;
|
|
153
153
|
uploadedAt: string;
|
|
154
|
+
/** Where the bytes landed on the device, set only after a verified write
|
|
155
|
+
* (Task 1910). Its presence IS the proof the move happened, which is what
|
|
156
|
+
* separates a healthy moved row from a phantom. Absent on a portal whose
|
|
157
|
+
* hand-run ALTER has not happened yet, which reads as "never moved". */
|
|
158
|
+
devicePath?: string;
|
|
159
|
+
/** 1 when the client has withdrawn an already-moved file and the device has
|
|
160
|
+
* not yet removed it. */
|
|
161
|
+
deleted?: number;
|
|
154
162
|
}
|
|
155
163
|
|
|
156
164
|
export interface DataPortalReconcile {
|
|
@@ -167,6 +175,31 @@ export interface DataPortalReconcile {
|
|
|
167
175
|
/** Null when nothing is uningested, or when no uningested row carries a
|
|
168
176
|
* parseable timestamp. Rendered `na` by the caller. */
|
|
169
177
|
oldestUningestedHrs: number | null;
|
|
178
|
+
/** Every row at `ingested = 0`, including rows whose `uploadedAt` does not
|
|
179
|
+
* parse. Deliberately NOT the size of the age sample below: that sample
|
|
180
|
+
* excludes unparseable timestamps, so a backlog made entirely of them would
|
|
181
|
+
* report a null age beside a zero count and read as an empty queue. */
|
|
182
|
+
uningestedRows: number;
|
|
183
|
+
/** Objects still in R2 whose row says the bytes already reached the device
|
|
184
|
+
* (Task 1910). The move deletes the object after the claim commits, so a
|
|
185
|
+
* survivor is a leak. It emits nothing at request time — the pull has
|
|
186
|
+
* already returned and no later request touches the key — which is why it
|
|
187
|
+
* is counted here rather than logged somewhere. */
|
|
188
|
+
unmovedObjects: string[];
|
|
189
|
+
/** Withdrawals the device has not carried out yet. Healthy is 0; a number
|
|
190
|
+
* that persists across cycles is a withdrawal that will never complete. */
|
|
191
|
+
deletePendingRows: number;
|
|
192
|
+
/** Age of the oldest pending withdrawal, so a stuck one is dateable rather
|
|
193
|
+
* than merely present. Null when none are pending. */
|
|
194
|
+
oldestDeletePendingHrs: number | null;
|
|
195
|
+
/** Files routed into an exposed folder that the published index does not
|
|
196
|
+
* carry. The client uploaded into a folder, the file landed, and the tree
|
|
197
|
+
* still does not show it.
|
|
198
|
+
*
|
|
199
|
+
* NULL when the index was not supplied, never 0: an unread index is "not
|
|
200
|
+
* measured", and rendering it as zero would let a failed read present as a
|
|
201
|
+
* clean bill of health. */
|
|
202
|
+
unpublishedRouted: number | null;
|
|
170
203
|
}
|
|
171
204
|
|
|
172
205
|
/** Pure over two lists and an injected clock, so the age arithmetic is
|
|
@@ -179,16 +212,24 @@ export function reconcileDataPortal(
|
|
|
179
212
|
objects: DataPortalObject[],
|
|
180
213
|
rows: DataPortalRow[],
|
|
181
214
|
nowMs: number,
|
|
215
|
+
publishedPaths?: Set<string>,
|
|
182
216
|
): DataPortalReconcile {
|
|
183
217
|
const objectKeys = new Set(objects.map((o) => o.key));
|
|
184
218
|
const rowKeys = new Set(rows.map((r) => r.objectKey));
|
|
185
219
|
|
|
220
|
+
// A moved row's bytes are deliberately NOT in R2, so it is neither a phantom
|
|
221
|
+
// nor an orphan — it is the healthy end state of the exchange. Splitting the
|
|
222
|
+
// row set here is what keeps the pre-existing counts meaning what they meant
|
|
223
|
+
// before the move existed.
|
|
224
|
+
const moved = rows.filter((r) => Boolean(r.devicePath));
|
|
225
|
+
const movedKeys = new Set(moved.map((r) => r.objectKey));
|
|
226
|
+
|
|
186
227
|
// Date.parse of a truthy non-ISO value is NaN, which slips past a `??` guard
|
|
187
228
|
// and renders as `oldestUningestedHrs=NaN` — the defect the availability
|
|
188
229
|
// loop's snapshotAgeMs already had to fix. Unparseable rows are excluded from
|
|
189
230
|
// the minimum; if that leaves none, the answer is null, never NaN.
|
|
190
|
-
const
|
|
191
|
-
|
|
231
|
+
const uningested = rows.filter((r) => r.ingested === 0);
|
|
232
|
+
const uningestedMs = uningested
|
|
192
233
|
.map((r) => Date.parse(r.uploadedAt))
|
|
193
234
|
.filter((ms) => !Number.isNaN(ms));
|
|
194
235
|
|
|
@@ -201,11 +242,34 @@ export function reconcileDataPortal(
|
|
|
201
242
|
// escape the caller's per-side catches and cost every later account its line.
|
|
202
243
|
const oldestMs = uningestedMs.reduce((min, ms) => (ms < min ? ms : min), Infinity);
|
|
203
244
|
|
|
245
|
+
const pendingDeletes = rows.filter((r) => r.deleted === 1);
|
|
246
|
+
const pendingDeleteMs = pendingDeletes
|
|
247
|
+
.map((r) => Date.parse(r.uploadedAt))
|
|
248
|
+
.filter((ms) => !Number.isNaN(ms));
|
|
249
|
+
// Reduced rather than spread, for the same stack-overflow reason as above.
|
|
250
|
+
const oldestPendingMs = pendingDeleteMs.reduce((min, ms) => (ms < min ? ms : min), Infinity);
|
|
251
|
+
|
|
204
252
|
return {
|
|
205
253
|
distinctObjects: objectKeys.size,
|
|
206
254
|
distinctRows: rowKeys.size,
|
|
207
255
|
orphanObjects: [...objectKeys].filter((k) => !rowKeys.has(k)),
|
|
208
|
-
|
|
256
|
+
// A moved row is excluded: its object is SUPPOSED to be gone, so counting
|
|
257
|
+
// it would manufacture a defect on every healthy exchange.
|
|
258
|
+
phantomRows: [...rowKeys].filter((k) => !objectKeys.has(k) && !movedKeys.has(k)),
|
|
209
259
|
oldestUningestedHrs: uningestedMs.length === 0 ? null : (nowMs - oldestMs) / 3_600_000,
|
|
260
|
+
uningestedRows: uningested.length,
|
|
261
|
+
unmovedObjects: [...movedKeys].filter((k) => objectKeys.has(k)),
|
|
262
|
+
deletePendingRows: pendingDeletes.length,
|
|
263
|
+
oldestDeletePendingHrs:
|
|
264
|
+
pendingDeleteMs.length === 0 ? null : (nowMs - oldestPendingMs) / 3_600_000,
|
|
265
|
+
unpublishedRouted:
|
|
266
|
+
publishedPaths === undefined
|
|
267
|
+
? null
|
|
268
|
+
: moved.filter(
|
|
269
|
+
(r) =>
|
|
270
|
+
// uploads/ is never published by design, so only a file routed
|
|
271
|
+
// into an exposed folder can be "unpublished".
|
|
272
|
+
!r.devicePath!.startsWith("uploads/") && !publishedPaths.has(r.devicePath!),
|
|
273
|
+
).length,
|
|
210
274
|
};
|
|
211
275
|
}
|
|
@@ -53,6 +53,19 @@ export interface CfExec {
|
|
|
53
53
|
d1List(): Promise<{ name: string; uuid: string }[]>;
|
|
54
54
|
d1Create(name: string): Promise<{ uuid: string }>;
|
|
55
55
|
d1Query(name: string, sql: string): Promise<unknown>;
|
|
56
|
+
/**
|
|
57
|
+
* One statement with BOUND parameters, over the D1 HTTP API (Task 1910).
|
|
58
|
+
*
|
|
59
|
+
* `d1Query` above takes raw SQL and spawns wrangler per statement, so every
|
|
60
|
+
* caller must interpolate its values. The portal pull writes client-supplied
|
|
61
|
+
* filenames and paths, which must never be concatenated into SQL — a UUID
|
|
62
|
+
* gate on one field is not a substitute for binding.
|
|
63
|
+
*
|
|
64
|
+
* This is deliberately NOT the convergence of the three D1 clients (Task
|
|
65
|
+
* 1843); it is one bound path for the callers that need one, added where the
|
|
66
|
+
* house credential already lives so no second credential path appears.
|
|
67
|
+
*/
|
|
68
|
+
d1Exec(name: string, sql: string, params?: unknown[]): Promise<unknown>;
|
|
56
69
|
r2List(): Promise<{ name: string }[]>;
|
|
57
70
|
r2Create(name: string): Promise<void>;
|
|
58
71
|
r2ObjectList(bucket: string, prefix?: string): Promise<R2Object[]>;
|
|
@@ -278,6 +291,41 @@ export function makeCfExec(
|
|
|
278
291
|
);
|
|
279
292
|
return parseJson(stdout, "d1 execute");
|
|
280
293
|
},
|
|
294
|
+
async d1Exec(name, sql, params = []) {
|
|
295
|
+
// The database is addressed by uuid, so the name is resolved first. An
|
|
296
|
+
// exact name match, never "the first result": `?name=` is a filter whose
|
|
297
|
+
// matching rule is undocumented, and taking position on trust would run
|
|
298
|
+
// these statements against a near-named database.
|
|
299
|
+
const listUrl =
|
|
300
|
+
`https://api.cloudflare.com/client/v4/accounts/${cred.accountId}` +
|
|
301
|
+
`/d1/database?name=${encodeURIComponent(name)}`;
|
|
302
|
+
const { parsed: listed } = await cfApiJson(
|
|
303
|
+
fetchFn,
|
|
304
|
+
listUrl,
|
|
305
|
+
{ method: "GET", headers: { Authorization: `Bearer ${cred.apiToken}` } },
|
|
306
|
+
"d1 database lookup",
|
|
307
|
+
);
|
|
308
|
+
const hit = ((listed as { result?: { name?: string; uuid?: string }[] }).result ?? []).find(
|
|
309
|
+
(d) => d.name === name,
|
|
310
|
+
);
|
|
311
|
+
if (!hit || typeof hit.uuid !== "string") {
|
|
312
|
+
throw new Error(`storage-broker: no D1 database named "${name}"`);
|
|
313
|
+
}
|
|
314
|
+
const { parsed } = await cfApiJson(
|
|
315
|
+
fetchFn,
|
|
316
|
+
`https://api.cloudflare.com/client/v4/accounts/${cred.accountId}/d1/database/${hit.uuid}/query`,
|
|
317
|
+
{
|
|
318
|
+
method: "POST",
|
|
319
|
+
headers: {
|
|
320
|
+
Authorization: `Bearer ${cred.apiToken}`,
|
|
321
|
+
"Content-Type": "application/json",
|
|
322
|
+
},
|
|
323
|
+
body: JSON.stringify({ sql, params }),
|
|
324
|
+
},
|
|
325
|
+
"d1 query",
|
|
326
|
+
);
|
|
327
|
+
return (parsed as { result?: unknown }).result;
|
|
328
|
+
},
|
|
281
329
|
async r2List() {
|
|
282
330
|
// `wrangler r2 bucket list` has no --json flag and emits human prose, so
|
|
283
331
|
// it can neither be parsed nor honour the "never grep a CLI's prose"
|
|
@@ -2228,3 +2228,108 @@ OPTIONS {
|
|
|
2228
2228
|
`vector.similarity_function`: 'cosine'
|
|
2229
2229
|
}
|
|
2230
2230
|
};
|
|
2231
|
+
|
|
2232
|
+
// ---------------------------------------------------------------------------
|
|
2233
|
+
// Vertical node-type declarations (Task 1893).
|
|
2234
|
+
//
|
|
2235
|
+
// Every label below is declared in a `## Node Types` / `## Additional Node
|
|
2236
|
+
// Types` table of a schema-*.md ontology doc but had no line here, so the
|
|
2237
|
+
// schema-doc drift guard could not see it and the first memory-write against a
|
|
2238
|
+
// fresh graph would hit an unregistered label. The guard was blind to them
|
|
2239
|
+
// because it matched only the backticked `:Label` prose form.
|
|
2240
|
+
//
|
|
2241
|
+
// An accountId index, not a uniqueness constraint. Only schema-construction.md
|
|
2242
|
+
// and schema-knowledge-work.md carry an `## Idempotency contracts` section, so
|
|
2243
|
+
// no natural key is agreed for any label here, and several have none that would
|
|
2244
|
+
// be correct: two :StockMovement rows on one day are legitimately distinct, as
|
|
2245
|
+
// are two :Stop rows on one route. accountId is the one property every one of
|
|
2246
|
+
// them requires, and an index can never reject a legitimate write.
|
|
2247
|
+
// ---------------------------------------------------------------------------
|
|
2248
|
+
|
|
2249
|
+
// schema-base.md
|
|
2250
|
+
//
|
|
2251
|
+
// :`Order` and :`Case` are backtick-escaped because ORDER and CASE are Cypher
|
|
2252
|
+
// reserved words. Escaping a label is always legal whether or not the word is
|
|
2253
|
+
// reserved, and seed-neo4j.sh pipes this whole file through cypher-shell under
|
|
2254
|
+
// `set -euo pipefail`, so one unparseable statement aborts the entire install
|
|
2255
|
+
// seed. They are the only two of the 130 declared labels that collide.
|
|
2256
|
+
CREATE INDEX order_account IF NOT EXISTS
|
|
2257
|
+
FOR (n:`Order`) ON (n.accountId);
|
|
2258
|
+
|
|
2259
|
+
CREATE INDEX lead_account IF NOT EXISTS
|
|
2260
|
+
FOR (n:Lead) ON (n.accountId);
|
|
2261
|
+
|
|
2262
|
+
CREATE INDEX case_account IF NOT EXISTS
|
|
2263
|
+
FOR (n:`Case`) ON (n.accountId);
|
|
2264
|
+
|
|
2265
|
+
CREATE INDEX whatsapp_conversation_account IF NOT EXISTS
|
|
2266
|
+
FOR (n:WhatsAppConversation) ON (n.accountId);
|
|
2267
|
+
|
|
2268
|
+
CREATE INDEX whatsapp_message_account IF NOT EXISTS
|
|
2269
|
+
FOR (n:WhatsAppMessage) ON (n.accountId);
|
|
2270
|
+
|
|
2271
|
+
// schema-creator.md
|
|
2272
|
+
CREATE INDEX content_account IF NOT EXISTS
|
|
2273
|
+
FOR (n:Content) ON (n.accountId);
|
|
2274
|
+
|
|
2275
|
+
CREATE INDEX campaign_account IF NOT EXISTS
|
|
2276
|
+
FOR (n:Campaign) ON (n.accountId);
|
|
2277
|
+
|
|
2278
|
+
CREATE INDEX audience_account IF NOT EXISTS
|
|
2279
|
+
FOR (n:Audience) ON (n.accountId);
|
|
2280
|
+
|
|
2281
|
+
CREATE INDEX brand_deal_account IF NOT EXISTS
|
|
2282
|
+
FOR (n:BrandDeal) ON (n.accountId);
|
|
2283
|
+
|
|
2284
|
+
// schema-food-beverage.md
|
|
2285
|
+
CREATE INDEX table_reservation_account IF NOT EXISTS
|
|
2286
|
+
FOR (n:TableReservation) ON (n.accountId);
|
|
2287
|
+
|
|
2288
|
+
CREATE INDEX menu_item_account IF NOT EXISTS
|
|
2289
|
+
FOR (n:MenuItem) ON (n.accountId);
|
|
2290
|
+
|
|
2291
|
+
CREATE INDEX menu_account IF NOT EXISTS
|
|
2292
|
+
FOR (n:Menu) ON (n.accountId);
|
|
2293
|
+
|
|
2294
|
+
CREATE INDEX dining_order_account IF NOT EXISTS
|
|
2295
|
+
FOR (n:DiningOrder) ON (n.accountId);
|
|
2296
|
+
|
|
2297
|
+
// schema-hospitality.md
|
|
2298
|
+
CREATE INDEX reservation_account IF NOT EXISTS
|
|
2299
|
+
FOR (n:Reservation) ON (n.accountId);
|
|
2300
|
+
|
|
2301
|
+
CREATE INDEX room_account IF NOT EXISTS
|
|
2302
|
+
FOR (n:Room) ON (n.accountId);
|
|
2303
|
+
|
|
2304
|
+
// schema-logistics.md
|
|
2305
|
+
CREATE INDEX shipment_account IF NOT EXISTS
|
|
2306
|
+
FOR (n:Shipment) ON (n.accountId);
|
|
2307
|
+
|
|
2308
|
+
CREATE INDEX vehicle_account IF NOT EXISTS
|
|
2309
|
+
FOR (n:Vehicle) ON (n.accountId);
|
|
2310
|
+
|
|
2311
|
+
CREATE INDEX route_account IF NOT EXISTS
|
|
2312
|
+
FOR (n:Route) ON (n.accountId);
|
|
2313
|
+
|
|
2314
|
+
CREATE INDEX stop_account IF NOT EXISTS
|
|
2315
|
+
FOR (n:Stop) ON (n.accountId);
|
|
2316
|
+
|
|
2317
|
+
// schema-professional-services.md
|
|
2318
|
+
CREATE INDEX engagement_account IF NOT EXISTS
|
|
2319
|
+
FOR (n:Engagement) ON (n.accountId);
|
|
2320
|
+
|
|
2321
|
+
CREATE INDEX retainer_account IF NOT EXISTS
|
|
2322
|
+
FOR (n:Retainer) ON (n.accountId);
|
|
2323
|
+
|
|
2324
|
+
CREATE INDEX deliverable_account IF NOT EXISTS
|
|
2325
|
+
FOR (n:Deliverable) ON (n.accountId);
|
|
2326
|
+
|
|
2327
|
+
// schema-retail.md
|
|
2328
|
+
CREATE INDEX product_account IF NOT EXISTS
|
|
2329
|
+
FOR (n:Product) ON (n.accountId);
|
|
2330
|
+
|
|
2331
|
+
CREATE INDEX stock_movement_account IF NOT EXISTS
|
|
2332
|
+
FOR (n:StockMovement) ON (n.accountId);
|
|
2333
|
+
|
|
2334
|
+
CREATE INDEX store_location_account IF NOT EXISTS
|
|
2335
|
+
FOR (n:StoreLocation) ON (n.accountId);
|
|
@@ -59,10 +59,10 @@ named. Derive the allowlist from the skills the agent owns: read each skill, lis
|
|
|
59
59
|
calls, take the union, and stop there. A vertical agent never inherits a horizontal agent's
|
|
60
60
|
whole surface. Do not add a tool "in case"; an unused tool is a boundary you gave away.
|
|
61
61
|
|
|
62
|
-
## The body: identity, then the
|
|
62
|
+
## The body: identity, then the contract sections
|
|
63
63
|
|
|
64
64
|
After the frontmatter the body opens with the agent's doctrine and identity (who it is, what it
|
|
65
|
-
owns, and the standing git-write boundary the roster shares), then the
|
|
65
|
+
owns, and the standing git-write boundary the roster shares), then the contract sections
|
|
66
66
|
in this exact order and with these exact headings:
|
|
67
67
|
|
|
68
68
|
### Output contract
|
|
@@ -88,6 +88,35 @@ it:
|
|
|
88
88
|
A headless agent with no human-in-the-loop step (a classifier, a rewriter) carries the
|
|
89
89
|
untrusted-input line alone; review gates do not apply where there is no operator checkpoint.
|
|
90
90
|
|
|
91
|
+
### Grounding
|
|
92
|
+
|
|
93
|
+
Heading `## Grounding`, last section in the file. Two sentences, the same wording in every agent
|
|
94
|
+
so a reviewer and a test can grep them, exactly as the untrusted-input line is. Copy them
|
|
95
|
+
character for character:
|
|
96
|
+
|
|
97
|
+
> Every factual claim you make carries a source you can name and, when it is time sensitive, the date you observed it; a fact you cannot source, or a date you cannot see, is a gap you flag, never one you supply from training recall.
|
|
98
|
+
|
|
99
|
+
> The only source for an attribute of a named person, the pronoun you use for them included, is that person's record; an attribute you cannot read there is one you leave out, never one you assume.
|
|
100
|
+
|
|
101
|
+
They live in their own section rather than inside the Output contract because they are
|
|
102
|
+
behavioural rules, not part of any deliverable. Several agents fix an exact machine-parseable
|
|
103
|
+
reply shape in their Output contract, and prose inside that section would contradict it.
|
|
104
|
+
|
|
105
|
+
The second sentence is worded so that an agent holding no read tool satisfies it by leaving the
|
|
106
|
+
attribute out. It never tells such an agent to attempt a lookup its own prompt forbids. That is
|
|
107
|
+
why it names the record as the only source rather than instructing a read.
|
|
108
|
+
|
|
109
|
+
Two further clauses bind the Output contract itself. **Recency**, universal: a time-sensitive
|
|
110
|
+
claim carries the date it was observed, and a date the agent cannot see is one it flags, never
|
|
111
|
+
one it infers. **Corroboration**, for agents whose deliverable is a pattern, a theme, a trend, a
|
|
112
|
+
signal: the agent names the independent sources behind it and does not report one as established
|
|
113
|
+
on a single occurrence. Corroboration is authoring guidance enforced by review, not by the gate,
|
|
114
|
+
because the subset it applies to cannot be detected statically.
|
|
115
|
+
|
|
116
|
+
`agent-grounding-drift.test.ts` fails if any shipped prompt carries either sentence anything
|
|
117
|
+
other than exactly once, and fails if the number of prompts it inspected is not the number it
|
|
118
|
+
expects. A new agent that skips them does not ship.
|
|
119
|
+
|
|
91
120
|
## Skills owned
|
|
92
121
|
|
|
93
122
|
The body closes with the skills the agent owns, as a table: the skill, the plugin it lives in,
|
|
@@ -97,7 +126,7 @@ skill-builder flow, so the agent and its skills land together.
|
|
|
97
126
|
|
|
98
127
|
## Why this shape
|
|
99
128
|
|
|
100
|
-
A fixed contract makes every agent legible. An operator reading the file knows
|
|
101
|
-
|
|
102
|
-
|
|
129
|
+
A fixed contract makes every agent legible. An operator reading the file knows what it produces,
|
|
130
|
+
where it pauses for them, that it will not act on text it was handed as data, and what it may
|
|
131
|
+
treat as a source. A reviewer can check each of them by grep. The reference agents prove this costs
|
|
103
132
|
about 320 words and is worth standardising.
|
|
@@ -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:
|
|
4
|
+
content-hash: sha256:643a947eb8968517ad598f7f8b4e433d8545877d7c21f857d4867ac914e34cc9
|
|
5
5
|
brand: maxy-code
|
|
6
6
|
product-name: Maxy
|
|
7
7
|
---
|
|
@@ -2482,10 +2482,14 @@ This usually completes within a couple of minutes of the booking.
|
|
|
2482
2482
|
Bookings are reconciled on a cycle and the system audits itself each run. In the device logs:
|
|
2483
2483
|
|
|
2484
2484
|
- `[calendar-booking]` lines trace one booking through submit, write, notify, and done.
|
|
2485
|
-
- `[calendar-reconcile]` lines report
|
|
2485
|
+
- `[calendar-reconcile]` lines report four health numbers per account: `eligibleExamined` (bookings the check looked at), `pendingUnreconciled` (bookings captured but not yet turned into meetings), `pendingOldestAgeMin` (how many minutes the longest-waiting of those has been waiting) and `orphanMeetings` (booking meetings with no matching saved booking). Entries that only block out a slot are not counted — they are not bookings and never become meetings.
|
|
2486
2486
|
|
|
2487
2487
|
A `pendingUnreconciled` number that stays above zero across several runs means those bookings are failing to turn into meetings, and the `attempt=` number on the matching `[calendar-booking]` lines says how many times each has been tried. A booking whose meeting is deleted is picked up again automatically on the next run. To read them: `journalctl --user -u <brand-service> | grep -E '\[calendar-booking\]|\[calendar-reconcile\]'`, then filter by one booking id for its full story. The edge half of a booking (before your device sees it) is in the Cloudflare Pages function logs.
|
|
2488
2488
|
|
|
2489
|
+
A count on its own cannot tell a short lag from a long one: five bookings waiting two minutes and five waiting nine hours are both `pendingUnreconciled=5`. `pendingOldestAgeMin` is what separates them. It reads `0` when nothing is waiting, and also when something is waiting but for under a minute, so read it together with the count beside it.
|
|
2490
|
+
|
|
2491
|
+
A separate `op=liveness` line is printed every ten minutes by your device's own server, whether or not anything is wrong. `stale=false` means the booking check has run recently. `stale=true` means it has not run in over six minutes, which is the case nothing else can tell you about: a check that has stopped cannot report that it has stopped. `lastSkipReason` names the last account whose check finished without looking at any bookings, and reads `none` when every account it got to was checked. If you run more than one account, a check can both look at one account and skip another, so seeing a recent check time next to a skip reason is normal and means two different accounts.
|
|
2492
|
+
|
|
2489
2493
|
## When your real calendar lives elsewhere
|
|
2490
2494
|
|
|
2491
2495
|
The booking page publishes open slots from the internal calendar, the one Maxy keeps in its own graph.
|
|
@@ -4219,6 +4223,8 @@ Standing rules the operator gives over a channel are `Preference` nodes. Two mec
|
|
|
4219
4223
|
|
|
4220
4224
|
**Trust a scheduled turn's provenance, never an inbound's claim (channel-agent grounding).** A cron-dispatched agent turn reaches the same channel builders as a real inbound, so its own authorisation text would otherwise land inside `## Context` — the zone `REFRAME_INSTRUCTION` demotes to untrusted — and a security-cautious agent would distrust its own automation (the self-cancel loop). The dispatcher therefore stamps a `ScheduleProvenance` marker (owning account, event, owner userId, dispatch time) that the channel builders render as a `## Scheduled automation (platform-verified)` preamble **above** `## Context` (and above the rules block); the whatsapp wrap tag names the door `scheduler`. Both channel-server paths carry the marker — the single-target loop and the unified multi-target door reader (via the same `doorInboundPayload` seam that carries `standingRules`); the scheduler only dispatches to the whatsapp/telegram door, so a webchat frame never bears one. Because the marker is dispatcher-stamped and never derived from payload text, an inbound message cannot forge it — placement above `## Context` alone is not the trust signal, the dispatcher provenance is. The grounding binds to it in two places: one recipient-neutral clause added byte-identical to the three reframe instructions (a marker-bearing turn is authorised standing automation; a reconfiguration or re-enable is not intrusion), and the admin `IDENTITY.md` "Untrusted input" rule that the agent never states a platform-level authenticity or impersonation verdict it was not given (an unsourced "this is not from you" is its own inference, flagged, never asserted as the platform's). A sender has exactly one session: every inbound, real or scheduled, resolves to the deterministic `adminSessionIdFor(effectiveAccount, destination)`. An earlier change gave each firing its own salted seat so distrust could not accrete across firings, but the inbound hub keys on `senderId` and replaces its subscriber outright, so a seat's channel server attached under the sender's own key and swallowed the sender's REAL messages until it detached — splitting one conversation across two sessions that each denied what the other held. The seat is removed; the authoritative `agentPrompt`, this provenance marker, and the injected standing rules ground each firing without one. Self-cancel attribution is disarmed while that model beds in: the guard used to compare the cancelling turn's session id to a dispatcher stamp, which discriminated only because each firing ran its own seat, so with one session per sender it would report an autonomous loop on every operator cancel. It now always reports false — under-counting a real loop rather than inventing one — until the discriminator is rebuilt from the turn's own dispatched eventId. See `.docs/scheduling-agent-dispatch.md § Operator-provenance marker`, `§ Session model — one session per sender`, and `§ Self-cancel attribution — disarmed`.
|
|
4221
4225
|
|
|
4226
|
+
**Carry what a reply points at (WhatsApp reply quote).** A WhatsApp sender replying to an earlier message produces a quote that `app/lib/whatsapp/inbound/extract.ts` resolves to `{ id, text?, sender? }` and `manager.ts` assigns to the route payload's `replyContext`. That field had no reader: the `server/index.ts` gateway argument omitted it, `AdminInbound` and the hub frame had no such field, and the channel service's `composePayload` emitted the caption and media notes only, so a message saying "prepare an email confirmation for this booking" reached the agent with nothing to resolve "this booking" against. It now rides both WhatsApp admin doors. On the persistent door it travels `server/index.ts` -> `AdminInbound` -> the hub frame the SSE route stringifies -> the channel service's `InboundPayload`, where `composeReplyNote` renders it as a bracketed note at the top of the payload, inside `## Context` and above the caption and any woven media notes: the quoted body is sender-controlled and belongs in the reframed untrusted zone, never in the platform preamble. Both channel-server inbound paths carry it, through the same `doorInboundPayload` seam that carries `standingRules` and the schedule provenance marker: the single-target SSE loop parses the whole frame, and the unified multi-target door reader rebuilds the payload field by field, so a field that seam omits is dropped on multi-target sessions alone. That seam is guarded by a test which reads the `InboundPayload` declaration and the rebuild function rather than a fixture, because the pre-existing byte-identity guard compared one hardcoded payload and so stayed green for every field added after it was written. On the passive account-manager door there is no channel server and no `## Context` frame, so the one-shot's whole initial prompt is read at face value; `composePassivePrompt` therefore puts the BARE sentence above the requirement and never the quoted body. The body arrives from the sender's own client and is never checked against the message store, so quoting it in a platform-voiced bracket on the door with no untrusted zone would let a modified client put words the agent never said into a `work-create` filing. The empty-text drop gate still tests the requirement alone, so a quote never rescues an inbound carrying no requirement of its own. The quoted body is capped at `QUOTED_TEXT_CAP` (500 characters, `…` appended when cut) because it is unbounded sender-controlled input, and the cut lands on whole characters so a body whose 500th boundary falls inside a surrogate pair cannot emit a lone surrogate at a position the sender chose. Only the channel service renders a body, so the cap lives there alone; platform/ui's `app/lib/whatsapp/reply-quote.ts` holds just the bare sentence for the passive door, and both packages hardcode that sentence in their own tests so the two doors cannot drift apart on the wording (the packages never cross-import, the same arrangement `REFRAME_INSTRUCTION` and `MediaRef` use). Telegram and webchat extract no quote at all, so neither carries one. `payloadBytes` measures the whole inner payload, so a reply carrying a quote produces a visibly larger `[reframe] op=applied … payloadBytes=<n>` than the same text without one, and a bare-text-length `payloadBytes` on an inbound whose store row has a non-null `quotedId` is the signature of this defect returning. Because the field is optional at every hop, dropping it from either `server/index.ts` argument would be silent (tsc stays green, and no unit test executes that wiring file), so the `check-reply-context-threaded` build gate asserts both argument sites.
|
|
4227
|
+
|
|
4222
4228
|
**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-audit] op=drift accountId=… storedActive=… injectedActive=… duplicateClusters=…` — the standing reconciliation audit (claude-session-manager, 5-minute tick) flags any account whose store holds more active rows than injection would surface, the no-event detector for drift.
|
|
4223
4229
|
|
|
4224
4230
|
---
|
|
@@ -73,11 +73,11 @@ The setup-done claim only fires when `curl -I https://<hostname>` issued from ou
|
|
|
73
73
|
|
|
74
74
|
The portal shows a sub-account's deliverable folders alongside what the client uploaded. Which folders those are is never a list in the code: it is derived per account from that account's own `SCHEMA.md` — the buckets in its `<!-- ontology-buckets -->` region plus `output`, intersected with its `allowed-top-level` block. A brand on a different vertical exposes different folders with no code change. `bin/schema-exposed-dirs.mjs` is the only place that decision is made. An operator can add non-ontology folders (a workflow folder such as `inbound-invoices`) by listing them in `exposeFolders` in the account's `data-portal.json`; each must match `[A-Za-z0-9_-]+` and is never a system folder (`agents`, `secrets`, `specialists`, `state`, `memory`, `cache`, `logs`, `tmp`) or dot-prefixed. Those extras union on top of the schema-derived set and, unlike ontology buckets, are not intersected with `allowed-top-level`; empty or absent behaves as before.
|
|
75
75
|
|
|
76
|
-
`bin/portal-index-push.mjs` walks those folders and full-replaces the account's rows in the portal's D1 `directory` table each cycle. Metadata only — names, sizes, times — so listing survives the device being offline, and a deleted or renamed file converges on the next cycle without a tombstone. The scheduling heartbeat runs it hourly for every account carrying a `data-portal.json`; it is also runnable standalone, which is how it stays operable while the heartbeat spawn path is unproven on a device.
|
|
76
|
+
`bin/portal-index-push.mjs` walks those folders and full-replaces the account's rows in the portal's D1 `directory` table each cycle. Metadata only — names, sizes, times — so listing survives the device being offline, and a deleted or renamed file converges on the next cycle without a tombstone. Every walked folder gets a row of its own marked `isDir`, not only the files inside it, so an exposed folder holding nothing at any depth still renders and opens; a folder whose `readdir` fails publishes nothing, so the tree never offers one the device does not have. `isDir` is a later column and `IF NOT EXISTS` adds nothing to an existing table, so a portal that predates it needs one hand-run `ALTER TABLE directory ADD COLUMN isDir INTEGER NOT NULL DEFAULT 0`. The scheduling heartbeat runs it hourly for every account carrying a `data-portal.json`; it is also runnable standalone, which is how it stays operable while the heartbeat spawn path is unproven on a device.
|
|
77
77
|
|
|
78
78
|
Opening a file reaches the install: the portal probes it, then mints an `/api/portal/fetch` link signed with the account's `PORTAL_FETCH_SECRET` and valid for five minutes. The install verifies the signature **and independently re-derives the exposed folder set**, so a correctly signed link for a non-exposed path is still refused. That second check is the security boundary, not the signature.
|
|
79
79
|
|
|
80
|
-
Exposure is narrowed twice. The install re-derives the account's exposed set from `SCHEMA.md` on every request and refuses anything outside it, which is the account-isolation boundary. On top of that, a person's `people.folders` grant (written at enrolment, empty means the full set) narrows them to named top-level folders inside that set. The grant lives only in the portal's D1, so a portal compromise widens a person back to the account's exposed set and never beyond it. `op=list` carries `granted=<n|full>` and an out-of-grant download logs `op=fetch-through result=not-granted`, distinct from `result=absent`.
|
|
80
|
+
Exposure is narrowed twice. The install re-derives the account's exposed set from `SCHEMA.md` on every request and refuses anything outside it, which is the account-isolation boundary. On top of that, a person's `people.folders` grant (written at enrolment, empty means the full set) narrows them to named top-level folders inside that set. The grant lives only in the portal's D1, so a portal compromise widens a person back to the account's exposed set and never beyond it. `op=list` carries `granted=<n|full>` and an out-of-grant download logs `op=fetch-through result=not-granted`, distinct from `result=absent`. A download naming a folder logs `result=is-directory` and mints no link, distinct from both.
|
|
81
81
|
|
|
82
82
|
What to watch, on the device:
|
|
83
83
|
|
|
@@ -94,6 +94,26 @@ What to watch, on the device:
|
|
|
94
94
|
|
|
95
95
|
`op=audit` is the standing check, and it is the only one that speaks when the push does **not**. It runs in-process inside the heartbeat rather than as a spawn, and reads only local state, so it cannot be blinded by the same failure it is watching for. It fires hourly even when everything is healthy — an audit that goes quiet on success is indistinguishable from an audit that stopped running. `result=never-ran` on an account with a `data-portal.json` means no cycle has ever succeeded for it; `result=stale` with `ageMin` means cycles have stopped. Its input is written only after `op=verify`, so a stale entry means the push stopped succeeding rather than merely stopped logging.
|
|
96
96
|
|
|
97
|
+
## Data-portal upload pull
|
|
98
|
+
|
|
99
|
+
The index push publishes what the device holds; the pull is the other direction. An upload lands in R2 under the person's prefix with one `manifest` row at `ingested = 0`, and the `portal-uploads-pull` loop in the admin server carries it onto the device every minute. It does not live in this plugin: it sits beside `runDataPortalAudit` in `platform/ui/server/portal-uploads-pull.ts`, because that audit already resolves the house Cloudflare credential and each account's portal database and bucket, and a second enumeration or credential path is how the two would drift.
|
|
100
|
+
|
|
101
|
+
Per pending row the order is fetch, write, verify, then claim. The object is fetched by `objectKey` through the storage broker, written to a dot-prefixed `.uploads-tmp/` partial (invisible to the index walk, so a partial can never be published as a deliverable), renamed into `uploads/<ownerName>/<filename>`, and only then is `ingested` set to 1 — and only if the written size equals `manifest.size`. A row claimed before the bytes land is a lost file carrying a flag that says it arrived. Anything that fails leaves the row at 0 and it is retried next cycle. `ownerName` and `filename` must each be a single path segment, checked on the device rather than trusted from the portal's own gate, and `fileId` must be a UUID before it reaches a statement, because `d1Query` takes raw SQL and binds nothing.
|
|
102
|
+
|
|
103
|
+
What to watch, on the device:
|
|
104
|
+
|
|
105
|
+
```
|
|
106
|
+
[portal-pull] op=claim account=<id> fileId=<uuid> bytes=<n> result=written|fetch-failed|write-failed|verify-failed|claim-failed|bad-owner|bad-filename|bad-fileid
|
|
107
|
+
[portal-pull] op=done account=<id> pulled=<n> failed=<n>
|
|
108
|
+
[portal-pull] op=malformed account=<id> rows=<n>
|
|
109
|
+
[portal-pull] op=failed account=<id> err="<message>"
|
|
110
|
+
[data-portal-audit] account=<id> … oldestUningestedHrs=<n|na> pendingRows=<n|na>
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
`[data-portal-audit]` is the standing signal, printed every cycle including at `pendingRows=0`, so a quiet line reads as healthy rather than as a stopped pull. When a client says a file never arrived: a `pendingRows` that stays non-zero with a rising `oldestUningestedHrs` means the pull runs and cannot finish, so read `result=` on `op=claim`. `pendingRows=na` means the manifest read itself failed, a different fault. No `[data-portal-audit]` line at all means the audit loop is not running, a third. Both counts are computed from the manifest alone, so they survive R2 being unreachable — which is exactly when a stalled pull matters most. `result=claim-failed` is the one outcome that leaves a file on disk with its row unclaimed; the next cycle re-fetches and overwrites the same path, which is deliberate.
|
|
114
|
+
|
|
115
|
+
Log lines carry counts and ids only. An object key is `<ownerId>/<filename>`, a real person's filename, and the brand journal is not where those belong.
|
|
116
|
+
|
|
97
117
|
On the portal side `[data-portal] op=list` carries `account=` and `op=fetch-through` carries the outcome; on the install `[portal-fetch]` logs `op=request` then exactly one of `op=served`, `op=denied`, `op=miss`. A portal `fetch-through` with no matching install `op=request` means the request never arrived, rather than having been refused. `[portal-fetch] op=no-secret account=<id>` is an unconfigured portal, which the `op=denied reason=secret` line alone would not distinguish from a bad signature.
|
|
98
118
|
|
|
99
119
|
## Discipline
|