@cgh567/agent 2.4.2 → 2.4.4

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 (157) hide show
  1. package/agents/business/talisman-ceo.md +183 -0
  2. package/agents/business/talisman-comms.md +257 -0
  3. package/agents/business/talisman-cto.md +153 -0
  4. package/agents/business/talisman-finance.md +246 -0
  5. package/agents/business/talisman-marketing.md +240 -0
  6. package/agents/business/talisman-sales.md +242 -0
  7. package/agents/business/talisman-support.md +236 -0
  8. package/bin/helios-rpc.js +19 -0
  9. package/daemon/adapters/helios-rpc-adapter.js +5 -12
  10. package/daemon/adapters/tui_wakeup.js +8 -0
  11. package/daemon/context-enrichment.js +27 -0
  12. package/daemon/daemon-manager.js +1 -1
  13. package/daemon/db/email-infrastructure-migrate.js +192 -0
  14. package/daemon/db/hbo-core-migrate.js +189 -0
  15. package/daemon/helios-api.js +863 -64
  16. package/daemon/helios-company-daemon.js +233 -33
  17. package/daemon/lib/blast-radius-analyzer.js +75 -0
  18. package/daemon/lib/domain-bootstrap-orchestrator.js +267 -0
  19. package/daemon/lib/forensic-log.js +113 -0
  20. package/daemon/lib/goal-research-pipeline.js +644 -0
  21. package/daemon/lib/harada/cascade-judge.js +84 -1
  22. package/daemon/lib/harada/cascade-research-dispatcher.js +282 -0
  23. package/daemon/lib/harada/pillar-dispatcher.js +23 -2
  24. package/daemon/lib/hbo-bridge.js +74 -6
  25. package/daemon/lib/headroom-middleware.js +129 -0
  26. package/daemon/lib/headroom-proxy-manager.js +309 -0
  27. package/daemon/lib/hed-engine.js +25 -0
  28. package/daemon/lib/intelligence/department-page-generator.js +46 -1
  29. package/daemon/lib/interpretation-engine.js +92 -0
  30. package/daemon/lib/mental-model-cache.js +96 -0
  31. package/daemon/lib/project-factory.js +47 -0
  32. package/daemon/lib/session-log-reader.js +93 -0
  33. package/daemon/lib/standard-work-bootstrap.js +87 -1
  34. package/daemon/lib/task-completion-processor.js +23 -0
  35. package/daemon/lib/wizard-engine.js +57 -6
  36. package/daemon/package.json +2 -1
  37. package/daemon/routes/agents.js +51 -6
  38. package/daemon/routes/channels.js +116 -2
  39. package/daemon/routes/crm.js +85 -0
  40. package/daemon/routes/dashboard.js +62 -16
  41. package/daemon/routes/dept.js +10 -1
  42. package/daemon/routes/email-triage.js +19 -10
  43. package/daemon/routes/hbo.js +618 -58
  44. package/daemon/routes/hed.js +133 -0
  45. package/daemon/routes/inbox.js +397 -8
  46. package/daemon/routes/project.js +580 -66
  47. package/daemon/routes/routines.js +14 -0
  48. package/daemon/routes/tasks.js +15 -1
  49. package/daemon/schema-apply.js +174 -0
  50. package/daemon/schema-definitions.js +433 -0
  51. package/daemon/schema-migrations-hbo.js +20 -0
  52. package/daemon/schema-migrations-hed.js +18 -0
  53. package/daemon/schema-migrations-proj.js +153 -0
  54. package/extensions/__tests__/codebase-index.test.ts +73 -0
  55. package/extensions/__tests__/extension-command-registration.test.ts +35 -0
  56. package/extensions/__tests__/git-push-guard.test.ts +68 -0
  57. package/extensions/context-compaction.ts +104 -76
  58. package/extensions/cortex/__tests__/cortex-core.test.ts +100 -0
  59. package/extensions/cortex/wal-replay.ts +91 -0
  60. package/extensions/email/actions/draft-response.ts +21 -1
  61. package/extensions/email/auth/accounts.ts +5 -11
  62. package/extensions/email/auth/inbox-dog.ts +5 -2
  63. package/extensions/email/backfill.ts +20 -13
  64. package/extensions/email/providers/gmail.ts +164 -0
  65. package/extensions/email/providers/google-calendar.ts +34 -5
  66. package/extensions/helios-browser/__tests__/browser-routing.test.ts +57 -0
  67. package/extensions/helios-browser/backends/playwright.ts +3 -1
  68. package/extensions/helios-governance/__tests__/governance-gates.test.ts +40 -0
  69. package/extensions/helios-governance/__tests__/tournament-consumer.test.js +66 -0
  70. package/extensions/hema-dispatch-v3/headroom-compress.ts +103 -0
  71. package/extensions/hema-dispatch-v3/index.ts +46 -72
  72. package/extensions/interview/__tests__/server.test.ts +117 -0
  73. package/extensions/lib/helios-root.cjs +46 -0
  74. package/extensions/subagent-mesh/__tests__/handlers.test.ts +98 -0
  75. package/extensions/warm-tick/warm-tick-maintenance.ts +164 -0
  76. package/lib/__tests__/bulk-ingest.live.test.ts +66 -0
  77. package/lib/__tests__/crash-fixes.test.ts +49 -0
  78. package/lib/__tests__/hbo-core-store.test.js +238 -0
  79. package/lib/__tests__/maintenance-mission-wiring.test.ts +35 -0
  80. package/lib/broker/__tests__/jit-subscription.test.js +44 -1
  81. package/lib/broker/__tests__/lifecycle-channels.test.js +25 -1
  82. package/lib/compression/__tests__/ccr-store.test.js +138 -0
  83. package/lib/compression/__tests__/pipeline.test.js +280 -0
  84. package/lib/compression/__tests__/smart-crusher.test.js +242 -0
  85. package/lib/compression/dist/server.js +34 -1
  86. package/lib/compression/dist/start-server.js +77 -0
  87. package/lib/event-bus.mts +1 -1
  88. package/lib/graph/learning/headroom-learn-bridge.js +175 -0
  89. package/lib/graph-availability.js +62 -0
  90. package/lib/hbo-core-store.compiled.js +834 -0
  91. package/lib/hbo-core-store.js +124 -0
  92. package/lib/hbo-core-store.ts +979 -0
  93. package/lib/mission-loop/__tests__/research-handler.test.ts +143 -0
  94. package/lib/skill-sync.js +6 -1
  95. package/lib/startup-integrity.js +9 -2
  96. package/lib/triage-core/__tests__/classifier-fixture.test.ts +254 -0
  97. package/lib/triage-core/__tests__/classifier-post-norm.test.ts +1 -1
  98. package/lib/triage-core/__tests__/classifier.test.ts +45 -7
  99. package/lib/triage-core/__tests__/correction-detector.test.ts +36 -0
  100. package/lib/triage-core/__tests__/d6-dunbar-boost.test.ts +5 -5
  101. package/lib/triage-core/__tests__/orchestrator-pipeline.test.ts +107 -0
  102. package/lib/triage-core/__tests__/orchestrator.test.ts +113 -1
  103. package/lib/triage-core/__tests__/signals.test.ts +357 -0
  104. package/lib/triage-core/__tests__/sql-parity.test.ts +216 -0
  105. package/lib/triage-core/backfill-cost-estimator.ts +91 -0
  106. package/lib/triage-core/backfill-orchestrator.ts +119 -0
  107. package/lib/triage-core/classifier.ts +41 -8
  108. package/lib/triage-core/cos/cross-channel-escalation.ts +2 -3
  109. package/lib/triage-core/cos/response-debt.ts +2 -2
  110. package/lib/triage-core/graph/__tests__/batch-persistence.test.ts +283 -0
  111. package/lib/triage-core/graph/batch-persistence.ts +66 -2
  112. package/lib/triage-core/graph/betweenness-worker.js +75 -0
  113. package/lib/triage-core/graph/graph-rank-sql.ts +67 -0
  114. package/lib/triage-core/graph/persistence.ts +1 -1
  115. package/lib/triage-core/graph/schema-v2.ts +2 -0
  116. package/lib/triage-core/graph/schema.cypher +11 -0
  117. package/lib/triage-core/graph/triage-query.ts +1 -1
  118. package/lib/triage-core/learning.ts +15 -20
  119. package/lib/triage-core/mental-model/bedrock-config.ts +78 -0
  120. package/lib/triage-core/mental-model/cos-integration.ts +1 -1
  121. package/lib/triage-core/mental-model/entity-extractor.ts +51 -4
  122. package/lib/triage-core/mental-model/identity-resolver.ts +5 -5
  123. package/lib/triage-core/mental-model/key-facts.ts +1 -2
  124. package/lib/triage-core/mental-model/model-assembler-sql.ts +200 -0
  125. package/lib/triage-core/mental-model/model-assembler.ts +16 -3
  126. package/lib/triage-core/orchestrator.ts +8 -15
  127. package/lib/triage-core/scheduled-sends.ts +39 -2
  128. package/lib/triage-core/signals/comms-style.ts +1 -1
  129. package/lib/triage-core/signals/cross-channel-escalation.ts +2 -2
  130. package/lib/triage-core/signals/favee-type.ts +6 -1
  131. package/lib/triage-core/signals/goal-relevance.ts +31 -2
  132. package/lib/triage-core/signals/personal-importance.ts +1 -1
  133. package/lib/triage-core/signals/referral-chain.ts +0 -1
  134. package/lib/triage-core/signals/relationship-decay.ts +4 -0
  135. package/lib/triage-core/signals/relationship-health.ts +6 -1
  136. package/lib/triage-core/signals/trajectory-signal.ts +38 -3
  137. package/lib/triage-core/tournament-runner.js +11 -1
  138. package/lib/triage-core/triage-llm-factory.ts +110 -0
  139. package/lib/triage-core/triage-local-llm.ts +145 -0
  140. package/lib/triage-core/triage-sql-store.ts +337 -0
  141. package/lib/triage-core/types.ts +2 -2
  142. package/lib/unified-graph.atomic.test.ts +52 -0
  143. package/lib/unified-graph.failure-categories.test.ts +55 -0
  144. package/package.json +18 -7
  145. package/prebuilds/darwin-arm64/better_sqlite3.node +0 -0
  146. package/prebuilds/linux-x64/better_sqlite3.node +0 -0
  147. package/prebuilds/win32-x64/better_sqlite3.node +0 -0
  148. package/skills/helios-bookkeeping/SKILL.md +321 -0
  149. package/skills/helios-briefer/SKILL.md +44 -0
  150. package/skills/helios-client-relations/SKILL.md +322 -0
  151. package/skills/helios-personal-triager/SKILL.md +45 -0
  152. package/skills/helios-recruitment/SKILL.md +317 -0
  153. package/skills/helios-relationship-nudger/SKILL.md +77 -0
  154. package/skills/helios-researcher/SKILL.md +44 -0
  155. package/skills/helios-scheduler/SKILL.md +58 -0
  156. package/skills/helios-tax-analyst/SKILL.md +280 -0
  157. package/lib/triage-core/orchestrator.ts.bak-r005-r006-r008 +0 -1823
@@ -0,0 +1,433 @@
1
+ 'use strict';
2
+ /**
3
+ * schema-definitions.js — Declarative Memgraph schema for the Helios daemon.
4
+ *
5
+ * This is the single source of truth for every index and uniqueness constraint
6
+ * in the graph. It replaces the seven legacy migration files (schema-migrations*.js)
7
+ * which accumulated duplicates, syntax inconsistencies, and uncovered labels.
8
+ *
9
+ * Applied via schema-apply.js using Memgraph's MAGE schema.assert() procedure,
10
+ * which converges the live graph to exactly this definition on every startup:
11
+ * • Creates anything missing
12
+ * • Keeps anything already correct
13
+ * • Logs every action (Created / Kept / Dropped) for auditability
14
+ *
15
+ * ── Index map format ──────────────────────────────────────────────────────────
16
+ * indexes: { Label: ['prop1', 'prop2', ...] }
17
+ * Empty string '' → label-only index (fastest for existence checks)
18
+ * Single string → label+property index
19
+ * Strings in the same array are individual single-property indexes on that label.
20
+ * Composite indexes (multiple props together) go in compositeIndexes.
21
+ *
22
+ * ── Unique constraint format ──────────────────────────────────────────────────
23
+ * uniqueConstraints: { Label: [['prop'], ['propA', 'propB'], ...] }
24
+ * Each inner array is one unique constraint.
25
+ * Single-element → standard unique on one property.
26
+ * Multi-element → composite unique constraint.
27
+ *
28
+ * ── Notes ─────────────────────────────────────────────────────────────────────
29
+ * schema.assert() takes (indices_map, unique_constraints, existence_constraints, drop_existing).
30
+ * For indices_map each label maps to a list; '' = label-only, 'prop' = label+property.
31
+ * For unique_constraints each label maps to a list-of-lists for composite support.
32
+ */
33
+
34
+ // ── Indexes ───────────────────────────────────────────────────────────────────
35
+ // Each key is a node label.
36
+ // Each value is an array of property names to index individually on that label.
37
+ // Use '' (empty string) to create a label-only index alongside property indexes.
38
+
39
+ const INDEXES = {
40
+
41
+ // ── Core task / agent ──────────────────────────────────────────────────────
42
+ Task: [
43
+ 'companyId', 'status', 'assigneeAgentId', 'checkoutRunId', 'workspaceId',
44
+ 'originKind', 'hboTaskId', 'hboGoalId', 'outputConfidence',
45
+ 'cascade_exempt', 'startedAt', 'completedAt', 'andoning',
46
+ 'workType', 'workCategory', 'pdsaProcessed', 'progressPropagated', 'taskType',
47
+ 'pillarId', // set by pillar-dispatcher.js; used by PDSACycle pillar lookup
48
+ ],
49
+ BusinessAgent: [
50
+ 'companyId', 'role', 'status', 'adapterType', 'pendingApprovalId', 'agentKey',
51
+ ],
52
+ Comment: ['taskId', 'authorType'],
53
+ Workspace: ['agentId', 'runId'],
54
+ Transcript: ['runId', 'agentId'],
55
+
56
+ // ── Scheduling / routines ──────────────────────────────────────────────────
57
+ Routine: ['companyId', 'agentId', 'status', 'nextRunAt', 'latestRevisionId'],
58
+ RoutineTrigger: ['routineId', 'publicId'],
59
+ RoutineRevision: ['routineId'],
60
+ RoutineRun: ['routineId', 'linkedTaskId'],
61
+ Heartbeat: ['agentId', 'status', 'scheduledAt'],
62
+ SchedulerState: ['companyId'],
63
+
64
+ // ── Approvals / actions ────────────────────────────────────────────────────
65
+ Approval: ['companyId', 'status', 'type', 'requestedBy', 'pillarId'],
66
+ HeartbeatRun: ['agentId', 'taskId', 'status', 'companyId'],
67
+
68
+ // ── HBO goal hierarchy ─────────────────────────────────────────────────────
69
+ CompanyGoal: ['companyId', 'status', 'ownerAgentId'],
70
+ BusinessGoal: ['companyId', 'status', 'parentGoalId', 'createdAt'],
71
+ GoalNode: ['companyId', 'status'],
72
+
73
+ // ── Hoshin Kanri cascade ───────────────────────────────────────────────────
74
+ GoalPillar: [
75
+ 'companyId', 'goalId', 'openWindowRate', 'consecutiveZeroWeeks',
76
+ 'department', 'l2ReviewStatus', 'assignedRole', 'l2ReviewCycles',
77
+ ],
78
+ ActionCell: [
79
+ 'pillarId', 'goalId', 'companyId', 'status', 'assigneeAgentId',
80
+ 'l3ReviewStatus', 'l3ReviewCycles',
81
+ ],
82
+ GoalResearchBrief: ['companyId', 'expiresAt'],
83
+ StandardWorkDocument: ['companyId', 'agentId', 'taskOriginKind', 'version'],
84
+ StandardWorkDeviation: [
85
+ 'taskId', 'agentId', 'standardWorkId', 'status', 'detectedAt',
86
+ ],
87
+ OKRSeed: ['companyId', 'goalId'],
88
+
89
+ // ── Project intelligence ───────────────────────────────────────────────────
90
+ HeliosProject: ['companyId', 'pillarId', 'status', 'createdAt'],
91
+ ProjectDocument: ['projectId', 'updatedAt', 'companyId'], // companyId gap filled
92
+ ProjectQuestion: ['projectId', 'status'],
93
+ DocumentEdit: ['documentId', 'createdAt', 'companyId'], // companyId gap filled
94
+ PendingDocumentUpdate:['documentId', 'status', 'createdAt'],
95
+ DepartmentPage: ['companyId', 'department', 'lastGeneratedAt'],
96
+ PlanChangeProposal: ['companyId', 'targetId', 'status', 'scope', 'createdAt'],
97
+ // ── Department living document (Notion-feel editor) ────────────────────────
98
+ DepartmentDocument: ['companyId', 'department', 'updatedAt'],
99
+ DeptPendingDocumentUpdate:['companyId', 'deptSlug', 'status'],
100
+ DeptPlanChangeProposal: ['companyId', 'deptSlug', 'status'],
101
+
102
+ // ── Company beliefs (previously missing entirely) ──────────────────────────
103
+ CompanyBelief: [
104
+ 'companyId', 'department', 'templateKey', 'isConfirmed',
105
+ ],
106
+ BeliefObservation: ['companyId', 'beliefId'],
107
+ BudgetPolicy: ['companyId'], // high-frequency budget enforcement queries
108
+
109
+ // ── Anomaly / signals ──────────────────────────────────────────────────────
110
+ AnomalySignal: [
111
+ 'companyId', 'agentId', 'taskId', 'tier', 'signalType',
112
+ 'severity', 'status', 'detectedAt', 'source',
113
+ ],
114
+ AndonAlert: ['companyId', 'agentId', 'resolvedAt'],
115
+ AndonSignal: ['companyId'], // legacy label — kept until re-label migration clears it
116
+ RecurringPattern: ['companyId', 'signalType', 'agentId', 'status'],
117
+ KaizenProposal: ['companyId', 'status', 'ownerAgentId', 'sourceType'],
118
+ CascadeReviewAlert: ['companyId', 'agentId', 'actioned', 'triggeredAt'],
119
+
120
+ // ── PDSA / learning ───────────────────────────────────────────────────────
121
+ PDSACycle: ['companyId', 'taskId', 'agentId', 'actDecision', 'pillarId'],
122
+ HillChart: ['taskId', 'companyId', 'phase', 'updatedAt'],
123
+ KnowledgeAsset:['companyId', 'status', 'confidence', 'lastAppliedAt', 'applicableTaskTypes'],
124
+ HanseiRecord: ['companyId', 'taskId', 'agentId'],
125
+
126
+ // ── OKRs / cascade integrity ───────────────────────────────────────────────
127
+ QuarterlyOKR: [
128
+ 'companyId', 'status', 'quarter', 'assigneeAgentId', 'catchballStatus', 'grade',
129
+ ],
130
+ OKRFeedback: ['okrId', 'fromAgentId', 'status'],
131
+ ObstacleRecord: ['companyId', 'linkedOKRId', 'agentId', 'status'],
132
+ CatchballDisagreement: ['okrId', 'fromAgentId', 'objectionType'],
133
+ BeliefCeilingAssessment: ['okrId', 'agentId'],
134
+ SacrificeDeclaration: ['agentId', 'okrId', 'sacrificeVerified'],
135
+ PersonalCascade: ['companyId', 'agentId', 'integrityScore'],
136
+ CascadeIntegrityScore: ['companyId', 'agentId', 'calculatedAt', 'overallScore'],
137
+ SelfProposedOKR: ['companyId', 'agentId', 'status', 'selfAnalysisId'],
138
+ GoalDecompositionDraft: ['companyId', 'agentId', 'sourceAnalysisId'],
139
+
140
+ // ── Agent capability / coaching ───────────────────────────────────────────
141
+ CapabilityProfile: ['companyId', 'agentId', 'overallScore'],
142
+ CapabilityGap: ['companyId', 'agentId', 'dimension', 'status'],
143
+ FutureStateCommitment: ['companyId', 'agentId', 'gapClosureRate'],
144
+ EmpiricalCapabilityAssessment: ['companyId', 'agentId'],
145
+ AgentSelfAnalysis: ['companyId', 'agentId', 'triggerType', 'isComplete'],
146
+ AgentCollabRequest: ['companyId', 'fromAgentId', 'toAgentId', 'status'],
147
+ CoachingContract: ['companyId', 'agentId', 'coachAgentId', 'status'],
148
+ KataCoachingSession: ['companyId', 'agentId', 'weekOf', 'sessionStatus'],
149
+ MirrorObservation: ['companyId', 'agentId', 'patternType', 'status'],
150
+ PatternResponse: ['mirrorObservationId', 'agentId'],
151
+ MasteryMoment: ['companyId', 'agentId', 'okrId', 'status'],
152
+ MasteryReflection: ['masteryMomentId', 'agentId', 'specificityScore'],
153
+ MasteryTimeline: ['companyId', 'agentId'],
154
+ CharacterDimensionProfile: ['agentId'],
155
+ WeeklyReview: ['companyId', 'weekOf'],
156
+ DailyRoutineCard: ['companyId', 'agentId', 'date'],
157
+
158
+ // ── Agent mastery ──────────────────────────────────────────────────────────
159
+ MonthlyPredictionValidation: ['companyId', 'agentId', 'month'],
160
+
161
+ // ── Cost & budget ──────────────────────────────────────────────────────────
162
+ CostEvent: ['companyId', 'agentId', 'createdAt', 'model', 'biller'],
163
+ BusinessCostEvent: ['companyId', 'agentId', 'createdAt', 'businessTaskId'],
164
+ BudgetIncident: ['companyId', 'status', 'policyId'],
165
+ ActivityEvent: ['companyId', 'action', 'createdAt'],
166
+ TaskEnrichment: ['companyId', 'taskId', 'createdAt', 'workType', 'originKind'],
167
+
168
+ // ── CRM / outreach ────────────────────────────────────────────────────────
169
+ Lead: ['companyId', 'status', 'score', 'personId', 'businessId'],
170
+ Opportunity: ['companyId', 'stage', 'accountId', 'ownerId'],
171
+ Account: ['companyId', 'domain'],
172
+ Contact: ['companyId', 'accountId', 'email'],
173
+ ICPScore: ['companyId', 'accountId'],
174
+ Signal: ['companyId', 'type', 'accountId', 'createdAt'],
175
+ Sequence: ['companyId', 'status'],
176
+ Campaign: ['companyId', 'status'],
177
+ Deal: ['companyId', 'stage', 'ownerId'],
178
+ BusinessTask: ['companyId', 'status', 'assigneeId', 'goalId', 'dueDate', 'createdAt'],
179
+ CRMContact: ['companyId', 'personId', 'email', 'dunbarLayer', 'needsFollowUp'],
180
+ ActiveSequence: ['companyId', 'status'],
181
+
182
+ // ── People / social graph ─────────────────────────────────────────────────
183
+ Person: ['dunbarLayer', 'name', 'healthStatus', 'relationshipStrength', 'communityId'],
184
+ Identity: ['handle', 'platform'],
185
+ Organization: ['name', 'domain'],
186
+ FAVEESnapshot: ['personId', 'week'],
187
+ Conversation: ['platform', 'threadId'],
188
+ Episode: ['rawId', 'receivedAt', 'platform', 'sessionId'],
189
+ Topic: ['status', 'name'],
190
+ Question: ['status'],
191
+ Commitment: ['status', 'dueDate'],
192
+
193
+ // ── Email / triage ────────────────────────────────────────────────────────
194
+ Email: ['processedAt', 'address', 'triagePriority'],
195
+ EmailTriageResult: ['processedAt'],
196
+ TriageDecision: ['userOverride'],
197
+
198
+ // ── Auth / session (Harada) ───────────────────────────────────────────────
199
+ User: ['email', 'createdAt'],
200
+ Session: ['userId', 'expiresAt', 'createdAt'],
201
+ HaradaMap: ['userId', 'createdAt', 'updatedAt'],
202
+
203
+ // ── Strategy / OKR ───────────────────────────────────────────────────────
204
+ Strategy: ['companyId', 'goalId'],
205
+ SystemAim: ['companyId', 'industry'],
206
+ Department: ['companyId'], // gap filled — missing companyId index
207
+
208
+ // ── Domain config ─────────────────────────────────────────────────────────
209
+ CompanySkillConfig: ['domain'],
210
+ PillarRoutingConfig: ['companyId'],
211
+ SkillDef: ['name', 'contentHash'],
212
+ CompanySkill: ['key'],
213
+ // KnowledgeAsset indexes defined earlier (line ~117) with full property list
214
+
215
+ // ── HITL / interaction loop ───────────────────────────────────────────────
216
+ HITLInteraction: [
217
+ 'companyId', 'taskId', 'agentId', 'status', 'kind',
218
+ 'batchId', 'idempotencyKey', 'createdAt', 'resolvedAt', 'continuationPolicy',
219
+ ],
220
+ HITLQuestion: ['interactionId', 'companyId', 'kind'],
221
+ HITLAnswer: ['interactionId', 'companyId', 'createdAt'],
222
+ AgentReadySignal: ['companyId', 'agentId', 'status', 'createdAt', 'sourceType'],
223
+
224
+ // ── HED (Helios Engineering Daemon) ──────────────────────────────────────
225
+ HEDOperation: ['hedId', 'status', 'wave', 'assignedAgentRole'],
226
+ HEDReviewFinding: ['opId', 'verdict'],
227
+ BlastRadiusEvent: ['opId', 'severity'],
228
+
229
+ // ── Misc (previously uncovered — gaps filled) ────────────────────────────
230
+ Hansei: ['agentId'], // written via MERGE in routes/sensei.js
231
+ ApprovalComment: ['taskId', 'approvalId'], // written via CREATE
232
+ ProcessMetric: ['companyId', 'agentId', 'metricName'],
233
+ ControlChartSignal: ['metricId', 'agentId', 'status'],
234
+
235
+ // ── Outreach / property / realty ─────────────────────────────────────────
236
+ Outreach: ['sentAt', 'replied'],
237
+ Property: ['name', 'city'],
238
+ ManagementCompany: ['name', 'domain', 'lifecycleStage', 'icpTier'],
239
+ };
240
+
241
+ // ── Composite indexes (multi-property together) ───────────────────────────────
242
+ // schema.assert() handles single-property indexes via the INDEXES map above.
243
+ // Composite indexes must be created via separate Cypher statements because
244
+ // schema.assert() only creates single-property label indexes.
245
+ // These are applied in schema-apply.js via direct mgQuery calls.
246
+
247
+ const COMPOSITE_INDEXES = [
248
+ { label: 'Task', props: ['companyId', 'status'] },
249
+ { label: 'KnowledgeAsset', props: ['companyId', 'status'] },
250
+ { label: 'CompanySkill', props: ['companyId', 'key'] },
251
+ { label: 'PillarRoutingConfig', props: ['companyId', 'pillarName'] },
252
+ { label: 'HITLInteraction', props: ['taskId', 'status'] },
253
+ { label: 'HITLInteraction', props: ['companyId', 'status'] },
254
+ { label: 'HITLInteraction', props: ['companyId', 'idempotencyKey'] },
255
+ { label: 'HITLInteraction', props: ['companyId', 'kind', 'status'] },
256
+ { label: 'AgentReadySignal', props: ['agentId', 'status', 'sourceType'] },
257
+ // M6: Approval queries in decisions timeline and approvals list both filter by companyId+pillarId
258
+ // and companyId+status respectively. Single-prop indexes require sequential scans at scale.
259
+ { label: 'Approval', props: ['companyId', 'pillarId'] },
260
+ { label: 'Approval', props: ['companyId', 'status'] },
261
+ // PDSACycle decisions timeline filters by companyId+pillarId
262
+ { label: 'PDSACycle', props: ['companyId', 'pillarId'] },
263
+ ];
264
+
265
+ // ── Unique constraints ────────────────────────────────────────────────────────
266
+ // schema.assert() takes a map of label → list-of-lists.
267
+ // Each inner array is one unique constraint (single or composite properties).
268
+
269
+ const UNIQUE_CONSTRAINTS = {
270
+ // ── Core ──────────────────────────────────────────────────────────────────
271
+ Task: [['id']],
272
+ Comment: [['id']],
273
+ Workspace: [['id']],
274
+ Transcript: [['id']],
275
+ Routine: [['id']],
276
+ RoutineTrigger: [['id']],
277
+ RoutineRevision: [['id']],
278
+ RoutineRun: [['id']],
279
+ Approval: [['id']],
280
+ HeartbeatRun: [['id']],
281
+ Heartbeat: [['id']],
282
+ ActivityEvent: [['id']],
283
+ CostEvent: [['id']],
284
+ BudgetIncident: [['id']],
285
+ AndonAlert: [['id']],
286
+
287
+ // ── HBO goal hierarchy ────────────────────────────────────────────────────
288
+ Company: [['id']],
289
+ BusinessAgent: [['id']],
290
+ Department: [['id']],
291
+ CompanyGoal: [['id']],
292
+ BusinessGoal: [['id']],
293
+ BusinessTask: [['id']],
294
+ BusinessCostEvent:[['id']],
295
+ GoalNode: [['id']],
296
+
297
+ // ── Hoshin / cascade ──────────────────────────────────────────────────────
298
+ GoalPillar: [['id']],
299
+ ActionCell: [['id']],
300
+ GoalResearchBrief: [['id']],
301
+ StandardWorkDocument: [['id']],
302
+ StandardWorkDeviation:[['id']],
303
+ OKRSeed: [['id']],
304
+
305
+ // ── Project intelligence ──────────────────────────────────────────────────
306
+ HeliosProject: [['id']],
307
+ ProjectDocument: [['id']],
308
+ ProjectQuestion: [['id']],
309
+ DocumentEdit: [['id']],
310
+ PendingDocumentUpdate: [['id']],
311
+ DepartmentPage: [['id']],
312
+ PlanChangeProposal: [['id']],
313
+ // ── Department living document ────────────────────────────────────────────
314
+ DepartmentDocument: [['id']],
315
+ DeptPendingDocumentUpdate: [['id']],
316
+ DeptPlanChangeProposal: [['id']],
317
+
318
+ // ── Company beliefs (previously missing entirely) ─────────────────────────
319
+ CompanyBelief: [['id']], // CRITICAL: concurrent MERGE safety
320
+ BeliefObservation:[['id']], // CRITICAL: concurrent MERGE safety
321
+ BudgetPolicy: [['id']], // CRITICAL: budget enforcement + MERGE safety
322
+
323
+ // ── Anomaly / signals ─────────────────────────────────────────────────────
324
+ AnomalySignal: [['id']],
325
+ AndonSignal: [['id']], // legacy label — keep until re-label migration clears
326
+ RecurringPattern: [['id']],
327
+ KaizenProposal: [['id']],
328
+ CascadeReviewAlert:[['id']],
329
+
330
+ // ── PDSA / learning ───────────────────────────────────────────────────────
331
+ PDSACycle: [['id']],
332
+ HillChart: [['id']], // E-02: one per Task, keyed by id
333
+ KnowledgeAsset: [['id']],
334
+ HanseiRecord: [['id']],
335
+ Hansei: [['id']], // previously missing — written via MERGE
336
+
337
+ // ── OKRs ──────────────────────────────────────────────────────────────────
338
+ QuarterlyOKR: [['id']],
339
+ OKRFeedback: [['id']],
340
+ ObstacleRecord: [['id']],
341
+ CatchballDisagreement: [['id']],
342
+ BeliefCeilingAssessment: [['id']],
343
+ SacrificeDeclaration: [['id']],
344
+ PersonalCascade: [['id']],
345
+ CascadeIntegrityScore: [['id']],
346
+ SelfProposedOKR: [['id']],
347
+ GoalDecompositionDraft: [['id']],
348
+
349
+ // ── Agent capability ──────────────────────────────────────────────────────
350
+ CapabilityProfile: [['id']],
351
+ CapabilityGap: [['id']],
352
+ FutureStateCommitment:[['id']],
353
+ EmpiricalCapabilityAssessment:[['id']],
354
+ AgentSelfAnalysis: [['id']],
355
+ AgentCollabRequest: [['id']],
356
+ CoachingContract: [['id']],
357
+ KataCoachingSession: [['id']],
358
+ MirrorObservation: [['id']],
359
+ PatternResponse: [['id']],
360
+ MasteryMoment: [['id']],
361
+ MasteryReflection: [['id']],
362
+ MasteryTimeline: [['id']],
363
+ CharacterDimensionProfile:[['id']],
364
+ WeeklyReview: [['id']],
365
+ DailyRoutineCard: [['id']],
366
+ MonthlyPredictionValidation:[['id']],
367
+
368
+ // ── Cost ──────────────────────────────────────────────────────────────────
369
+ TaskEnrichment: [['id']],
370
+ SchedulerState: [['id']],
371
+
372
+ // ── CRM / outreach ────────────────────────────────────────────────────────
373
+ Lead: [['id']],
374
+ Opportunity: [['id']],
375
+ Account: [['id']],
376
+ Contact: [['id']],
377
+ ICPScore: [['id']],
378
+ Signal: [['id']],
379
+ Sequence: [['id']],
380
+ Campaign: [['id']],
381
+ Deal: [['id']],
382
+ CRMContact: [['id']],
383
+ Person: [['id']],
384
+ Identity: [['id']],
385
+ Organization: [['id']],
386
+
387
+ // ── People / social ───────────────────────────────────────────────────────
388
+ FAVEESnapshot: [['id']],
389
+ Conversation: [['id']],
390
+ Episode: [['id']],
391
+ Topic: [['id']],
392
+ Question: [['id']],
393
+ Commitment: [['id']],
394
+ Business: [['id']],
395
+ Property: [['id']],
396
+ ManagementCompany:[['id']],
397
+ ParentCompany: [['id']],
398
+ Outreach: [['id']],
399
+
400
+ // ── Strategy ──────────────────────────────────────────────────────────────
401
+ Strategy: [['id']],
402
+ SystemAim: [['id']],
403
+
404
+ // ── Auth / session ────────────────────────────────────────────────────────
405
+ User: [['id']],
406
+ Session: [['id'], ['token']], // two constraints: id AND token
407
+ HaradaMap: [['id']],
408
+
409
+ // ── Domain config — NON-ID unique constraints ─────────────────────────────
410
+ CompanySkillConfig: [['companyId']], // unique per company, not per id
411
+ PillarRoutingConfig: [['companyId', 'pillarName']], // composite unique
412
+ SkillDef: [['id']],
413
+
414
+ // ── HITL ──────────────────────────────────────────────────────────────────
415
+ HITLInteraction: [['id']],
416
+ HITLQuestion: [['id']],
417
+ HITLAnswer: [['id']],
418
+ AgentReadySignal:[['id']],
419
+
420
+ // ── HED ───────────────────────────────────────────────────────────────────
421
+ HEDOperation: [['id']],
422
+ HEDReviewFinding: [['id']],
423
+ BlastRadiusEvent: [['id']],
424
+
425
+ // ── SyncCursor — keyed on .name, not .id ─────────────────────────────────
426
+ SyncCursor: [['name']], // previously missing; MERGE'd on .name
427
+
428
+ // ── Process metrics ───────────────────────────────────────────────────────
429
+ ProcessMetric: [['id']],
430
+ ControlChartSignal: [['id']],
431
+ };
432
+
433
+ module.exports = { INDEXES, COMPOSITE_INDEXES, UNIQUE_CONSTRAINTS };
@@ -129,6 +129,16 @@ async function runHBOMigrations(mgQuery) {
129
129
  `CREATE INDEX FOR (n:PDSACycle) ON (n.companyId)`,
130
130
  `CREATE INDEX FOR (n:PDSACycle) ON (n.actDecision)`,
131
131
 
132
+ // ── HillChart (E-02: task work-cycle position for hill chart visualisation) ─
133
+ // One HillChart node per Task. Connected via (t:Task)-[:HAS_HILL_CHART]->(h:HillChart).
134
+ // position: float 0.0 (start) → 0.5 (peak/pivot) → 1.0 (done).
135
+ // phase: 'uphill' | 'downhill' | 'complete'
136
+ `CREATE CONSTRAINT FOR (n:HillChart) REQUIRE n.id IS UNIQUE`,
137
+ `CREATE INDEX FOR (n:HillChart) ON (n.taskId)`,
138
+ `CREATE INDEX FOR (n:HillChart) ON (n.companyId)`,
139
+ `CREATE INDEX FOR (n:HillChart) ON (n.phase)`,
140
+ `CREATE INDEX FOR (n:HillChart) ON (n.updatedAt)`,
141
+
132
142
  // ── KnowledgeAsset ────────────────────────────────────────────────────────
133
143
  `CREATE CONSTRAINT FOR (n:KnowledgeAsset) REQUIRE n.id IS UNIQUE`,
134
144
  `CREATE INDEX FOR (n:KnowledgeAsset) ON (n.companyId)`,
@@ -574,6 +584,16 @@ async function runHBOMigrations(mgQuery) {
574
584
  `CREATE CONSTRAINT FOR (n:ServiceAccount) REQUIRE n.id IS UNIQUE`,
575
585
  `CREATE INDEX FOR (n:ServiceAccount) ON (n.agentId, n.status)`,
576
586
  `CREATE INDEX FOR (n:ServiceAccount) ON (n.service, n.status)`,
587
+
588
+ // ── P4-03: executionStateJson backfill ────────────────────────────────────
589
+ // All existing Task nodes must have executionStateJson set to the string 'null'
590
+ // (not Cypher null) so the P4-04 CONTAINS check works correctly.
591
+ // A Cypher null property means the CONTAINS string operator would throw.
592
+ // Idempotent: WHERE clause skips nodes that already have the field.
593
+ // DEPLOYMENT NOTE: This daemon instance manages a single Memgraph tenant.
594
+ // The unscoped MATCH (t:Task) is intentional — all Task nodes in this Memgraph
595
+ // instance belong to the companies managed by this daemon.
596
+ `MATCH (t:Task) WHERE t.executionStateJson IS NULL SET t.executionStateJson = 'null'`,
577
597
  ];
578
598
 
579
599
  let successCount = 0;
@@ -0,0 +1,18 @@
1
+ 'use strict';
2
+ /**
3
+ * schema-migrations-hed.js — HED (Helios Event Dispatch) schema migrations.
4
+ * Stub implementation — full migrations run when Memgraph is available.
5
+ */
6
+
7
+ async function runHedMigrations(mgQuery) {
8
+ // HED schema migrations — no-op when Memgraph is unavailable
9
+ try {
10
+ await mgQuery(`CREATE INDEX IF NOT EXISTS ON :HEDEvent(companyId)`, {});
11
+ await mgQuery(`CREATE INDEX IF NOT EXISTS ON :HEDEvent(sessionId)`, {});
12
+ await mgQuery(`CREATE INDEX IF NOT EXISTS ON :HEDEvent(createdAt)`, {});
13
+ } catch (_) {
14
+ // Fail-open: Memgraph unavailable — skip HED migrations
15
+ }
16
+ }
17
+
18
+ module.exports = { runHedMigrations };
@@ -0,0 +1,153 @@
1
+ 'use strict';
2
+ const { randomUUID } = require('crypto');
3
+
4
+ async function runProjectMigrations(mgQuery) {
5
+ const migrations = [
6
+ // ── SM-proj-01: HeliosProject node ──────────────────────────────────────
7
+ `CREATE CONSTRAINT FOR (n:HeliosProject) REQUIRE n.id IS UNIQUE`,
8
+ `CREATE INDEX FOR (n:HeliosProject) ON (n.companyId)`,
9
+ `CREATE INDEX FOR (n:HeliosProject) ON (n.pillarId)`,
10
+ `CREATE INDEX FOR (n:HeliosProject) ON (n.status)`,
11
+ `CREATE INDEX FOR (n:HeliosProject) ON (n.createdAt)`,
12
+
13
+ // ── SM-proj-02: ProjectDocument node ────────────────────────────────────
14
+ `CREATE CONSTRAINT FOR (n:ProjectDocument) REQUIRE n.id IS UNIQUE`,
15
+ `CREATE INDEX FOR (n:ProjectDocument) ON (n.projectId)`,
16
+ `CREATE INDEX FOR (n:ProjectDocument) ON (n.companyId)`,
17
+ `CREATE INDEX FOR (n:ProjectDocument) ON (n.updatedAt)`,
18
+
19
+ // ── SM-proj-03: ProjectQuestion node ────────────────────────────────────
20
+ `CREATE CONSTRAINT FOR (n:ProjectQuestion) REQUIRE n.id IS UNIQUE`,
21
+ `CREATE INDEX FOR (n:ProjectQuestion) ON (n.projectId)`,
22
+ `CREATE INDEX FOR (n:ProjectQuestion) ON (n.companyId)`,
23
+ `CREATE INDEX FOR (n:ProjectQuestion) ON (n.status)`,
24
+ `CREATE INDEX FOR (n:ProjectQuestion) ON (n.createdAt)`,
25
+
26
+ // ── SM-proj-04: DocumentEdit node ────────────────────────────────────────
27
+ `CREATE CONSTRAINT FOR (n:DocumentEdit) REQUIRE n.id IS UNIQUE`,
28
+ `CREATE INDEX FOR (n:DocumentEdit) ON (n.documentId)`,
29
+ `CREATE INDEX FOR (n:DocumentEdit) ON (n.projectId)`,
30
+ `CREATE INDEX FOR (n:DocumentEdit) ON (n.companyId)`,
31
+ `CREATE INDEX FOR (n:DocumentEdit) ON (n.editedAt)`,
32
+
33
+ // ── SM-proj-05: PendingDocumentUpdate node ──────────────────────────────
34
+ `CREATE CONSTRAINT FOR (n:PendingDocumentUpdate) REQUIRE n.id IS UNIQUE`,
35
+ `CREATE INDEX FOR (n:PendingDocumentUpdate) ON (n.documentId)`,
36
+ `CREATE INDEX FOR (n:PendingDocumentUpdate) ON (n.projectId)`,
37
+ `CREATE INDEX FOR (n:PendingDocumentUpdate) ON (n.companyId)`,
38
+ `CREATE INDEX FOR (n:PendingDocumentUpdate) ON (n.status)`,
39
+
40
+ // ── SM-proj-06: Additions to existing nodes ──────────────────────────────
41
+ `CREATE INDEX FOR (n:Task) ON (n.projectId)`,
42
+ `CREATE INDEX FOR (n:AnomalySignal) ON (n.projectId)`,
43
+
44
+ // -- SM-proj-07: DepartmentPage node ------------------------------------------
45
+ `CREATE CONSTRAINT FOR (n:DepartmentPage) REQUIRE n.id IS UNIQUE`,
46
+ `CREATE INDEX FOR (n:DepartmentPage) ON (n.companyId)`,
47
+ `CREATE INDEX FOR (n:DepartmentPage) ON (n.department)`,
48
+ `CREATE INDEX FOR (n:DepartmentPage) ON (n.generatedAt)`,
49
+ `CREATE INDEX FOR (n:DepartmentPage) ON (n.status)`,
50
+
51
+ // SM-proj-08: GoalPillar absorbs HeliosProject properties -- new indexes
52
+ `CREATE INDEX FOR (n:GoalPillar) ON (n.projectStatus)`,
53
+ `CREATE INDEX FOR (n:GoalPillar) ON (n.driAgentId)`,
54
+ `CREATE INDEX FOR (n:GoalPillar) ON (n.currentPhaseLabel)`,
55
+
56
+ // SM-proj-09: DeptLearning node type for department page institutional memory
57
+ `CREATE CONSTRAINT FOR (n:DeptLearning) REQUIRE n.id IS UNIQUE`,
58
+ `CREATE INDEX FOR (n:DeptLearning) ON (n.companyId)`,
59
+ `CREATE INDEX FOR (n:DeptLearning) ON (n.department)`,
60
+ `CREATE INDEX FOR (n:DeptLearning) ON (n.capturedAt)`,
61
+
62
+ // SM-proj-10: DepartmentPage enhanced fields indexes
63
+ `CREATE INDEX FOR (n:DepartmentPage) ON (n.exceptionLevel)`,
64
+ `CREATE INDEX FOR (n:DepartmentPage) ON (n.version)`,
65
+
66
+ // ── SM-proj-11: DepartmentDocument node ────────────────────────────────
67
+ // User-editable Notion-feel living document scoped to a department.
68
+ // ID convention: ddoc:<companyId>:<deptSlug>:main
69
+ `CREATE CONSTRAINT FOR (n:DepartmentDocument) REQUIRE n.id IS UNIQUE`,
70
+ `CREATE INDEX FOR (n:DepartmentDocument) ON (n.companyId)`,
71
+ `CREATE INDEX FOR (n:DepartmentDocument) ON (n.department)`,
72
+ `CREATE INDEX FOR (n:DepartmentDocument) ON (n.updatedAt)`,
73
+
74
+ // ── SM-proj-12: DeptPendingDocumentUpdate node ──────────────────────────
75
+ // User-visible edit tracker for dept document (mirrors PendingDocumentUpdate).
76
+ `CREATE CONSTRAINT FOR (n:DeptPendingDocumentUpdate) REQUIRE n.id IS UNIQUE`,
77
+ `CREATE INDEX FOR (n:DeptPendingDocumentUpdate) ON (n.companyId)`,
78
+ `CREATE INDEX FOR (n:DeptPendingDocumentUpdate) ON (n.deptSlug)`,
79
+ `CREATE INDEX FOR (n:DeptPendingDocumentUpdate) ON (n.status)`,
80
+
81
+ // ── SM-proj-13: DeptPlanChangeProposal node ─────────────────────────────
82
+ // LLM analysis task for dept document edits (mirrors PlanChangeProposal).
83
+ `CREATE CONSTRAINT FOR (n:DeptPlanChangeProposal) REQUIRE n.id IS UNIQUE`,
84
+ `CREATE INDEX FOR (n:DeptPlanChangeProposal) ON (n.companyId)`,
85
+ `CREATE INDEX FOR (n:DeptPlanChangeProposal) ON (n.deptSlug)`,
86
+ `CREATE INDEX FOR (n:DeptPlanChangeProposal) ON (n.status)`,
87
+ ];
88
+
89
+ let success = 0, skipped = 0, failed = 0;
90
+ for (const cypher of migrations) {
91
+ try {
92
+ await mgQuery(cypher, {});
93
+ success++;
94
+ } catch (err) {
95
+ const msg = (err.message || '').toLowerCase();
96
+ if (msg.includes('already exists') || msg.includes('constraint already exists') ||
97
+ msg.includes('index already exists')) {
98
+ skipped++;
99
+ } else {
100
+ console.error('[schema-migrations-proj] Migration failed:', cypher.slice(0, 80), err.message);
101
+ failed++;
102
+ }
103
+ }
104
+ }
105
+ console.log(`[schema-migrations-proj] done — success:${success} skipped:${skipped} failed:${failed}`);
106
+ await runIntelligenceNodeMigrations(mgQuery);
107
+ }
108
+
109
+
110
+ // SM-proj-08 — Financial + CS + Marketing + Ops node schemas
111
+ async function runIntelligenceNodeMigrations(mgQuery) {
112
+ const migrations = [
113
+ // Finance nodes
114
+ "CREATE CONSTRAINT fin_snapshot_id IF NOT EXISTS FOR (n:FinancialSnapshot) REQUIRE n.id IS UNIQUE",
115
+ "CREATE INDEX fin_snapshot_cid IF NOT EXISTS FOR (n:FinancialSnapshot) ON (n.companyId)",
116
+ "CREATE CONSTRAINT burn_mult_id IF NOT EXISTS FOR (n:BurnMultipleCalc) REQUIRE n.id IS UNIQUE",
117
+ "CREATE INDEX burn_mult_cid IF NOT EXISTS FOR (n:BurnMultipleCalc) ON (n.companyId)",
118
+ "CREATE CONSTRAINT nrr_calc_id IF NOT EXISTS FOR (n:NRRCalc) REQUIRE n.id IS UNIQUE",
119
+ "CREATE INDEX nrr_calc_cid IF NOT EXISTS FOR (n:NRRCalc) ON (n.companyId)",
120
+ "CREATE CONSTRAINT gross_margin_id IF NOT EXISTS FOR (n:GrossMarginTimeSeries) REQUIRE n.id IS UNIQUE",
121
+ "CREATE INDEX gross_margin_cid IF NOT EXISTS FOR (n:GrossMarginTimeSeries) ON (n.companyId)",
122
+ "CREATE CONSTRAINT headcount_id IF NOT EXISTS FOR (n:HeadcountPlan) REQUIRE n.id IS UNIQUE",
123
+ "CREATE INDEX headcount_cid IF NOT EXISTS FOR (n:HeadcountPlan) ON (n.companyId)",
124
+ "CREATE CONSTRAINT infra_cost_id IF NOT EXISTS FOR (n:InfrastructureCost) REQUIRE n.id IS UNIQUE",
125
+ "CREATE INDEX infra_cost_cid IF NOT EXISTS FOR (n:InfrastructureCost) ON (n.companyId)",
126
+ "CREATE CONSTRAINT vendor_contract_id IF NOT EXISTS FOR (n:VendorContract) REQUIRE n.id IS UNIQUE",
127
+ "CREATE INDEX vendor_contract_cid IF NOT EXISTS FOR (n:VendorContract) ON (n.companyId)",
128
+ "CREATE CONSTRAINT cac_by_channel_id IF NOT EXISTS FOR (n:CACByChannel) REQUIRE n.id IS UNIQUE",
129
+ "CREATE INDEX cac_by_channel_cid IF NOT EXISTS FOR (n:CACByChannel) ON (n.companyId)",
130
+ // CS nodes
131
+ "CREATE CONSTRAINT acct_health_id IF NOT EXISTS FOR (n:AccountHealthScore) REQUIRE n.id IS UNIQUE",
132
+ "CREATE INDEX acct_health_cid IF NOT EXISTS FOR (n:AccountHealthScore) ON (n.companyId)",
133
+ "CREATE CONSTRAINT customer_journey_id IF NOT EXISTS FOR (n:CustomerJourneyNode) REQUIRE n.id IS UNIQUE",
134
+ "CREATE INDEX customer_journey_cid IF NOT EXISTS FOR (n:CustomerJourneyNode) ON (n.companyId)",
135
+ "CREATE CONSTRAINT renewal_id IF NOT EXISTS FOR (n:Renewal) REQUIRE n.id IS UNIQUE",
136
+ "CREATE INDEX renewal_cid IF NOT EXISTS FOR (n:Renewal) ON (n.companyId)",
137
+ "CREATE CONSTRAINT expansion_opp_id IF NOT EXISTS FOR (n:ExpansionOpportunity) REQUIRE n.id IS UNIQUE",
138
+ "CREATE INDEX expansion_opp_cid IF NOT EXISTS FOR (n:ExpansionOpportunity) ON (n.companyId)",
139
+ ];
140
+
141
+ for (const stmt of migrations) {
142
+ try {
143
+ await mgQuery(stmt, {});
144
+ } catch (e) {
145
+ // IF NOT EXISTS protects against duplicates; log other errors
146
+ if (!e.message.includes('already exists') && !e.message.includes('IF NOT EXISTS')) {
147
+ console.warn('[schema-migrations-proj] SM-proj-08 warn:', e.message);
148
+ }
149
+ }
150
+ }
151
+ }
152
+
153
+ module.exports = { runProjectMigrations };