@cgh567/agent 2.4.2 → 2.4.3
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/daemon/adapters/tui_wakeup.js +8 -0
- package/daemon/daemon-manager.js +1 -1
- package/daemon/db/email-infrastructure-migrate.js +192 -0
- package/daemon/db/hbo-core-migrate.js +189 -0
- package/daemon/helios-api.js +574 -20
- package/daemon/helios-company-daemon.js +103 -13
- package/daemon/lib/hbo-bridge.js +1 -1
- package/daemon/lib/hed-engine.js +25 -0
- package/daemon/lib/task-completion-processor.js +11 -0
- package/daemon/lib/wizard-engine.js +57 -6
- package/daemon/routes/hbo.js +253 -47
- package/daemon/routes/project.js +190 -59
- package/daemon/routes/routines.js +14 -0
- package/daemon/routes/tasks.js +15 -1
- package/daemon/schema-apply.js +174 -0
- package/daemon/schema-definitions.js +423 -0
- package/daemon/schema-migrations-hbo.js +10 -0
- package/daemon/schema-migrations-hed.js +18 -0
- package/daemon/schema-migrations-proj.js +131 -0
- package/extensions/cortex/wal-replay.ts +91 -0
- package/extensions/hema-dispatch-v3/index.ts +13 -7
- package/extensions/warm-tick/warm-tick-maintenance.ts +8 -0
- package/lib/__tests__/hbo-core-store.test.js +238 -0
- package/lib/event-bus.mts +1 -1
- package/lib/graph-availability.js +62 -0
- package/lib/hbo-core-store.compiled.js +834 -0
- package/lib/hbo-core-store.js +124 -0
- package/lib/hbo-core-store.ts +908 -0
- package/lib/triage-core/classifier.ts +3 -2
- package/lib/triage-core/graph/schema.cypher +10 -0
- package/lib/triage-core/mental-model/key-facts.ts +1 -2
- package/lib/triage-core/orchestrator.ts +4 -11
- package/package.json +9 -5
|
@@ -0,0 +1,423 @@
|
|
|
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
|
+
|
|
98
|
+
// ── Company beliefs (previously missing entirely) ──────────────────────────
|
|
99
|
+
CompanyBelief: [
|
|
100
|
+
'companyId', 'department', 'templateKey', 'isConfirmed',
|
|
101
|
+
],
|
|
102
|
+
BeliefObservation: ['companyId', 'beliefId'],
|
|
103
|
+
BudgetPolicy: ['companyId'], // high-frequency budget enforcement queries
|
|
104
|
+
|
|
105
|
+
// ── Anomaly / signals ──────────────────────────────────────────────────────
|
|
106
|
+
AnomalySignal: [
|
|
107
|
+
'companyId', 'agentId', 'taskId', 'tier', 'signalType',
|
|
108
|
+
'severity', 'status', 'detectedAt', 'source',
|
|
109
|
+
],
|
|
110
|
+
AndonAlert: ['companyId', 'agentId', 'resolvedAt'],
|
|
111
|
+
AndonSignal: ['companyId'], // legacy label — kept until re-label migration clears it
|
|
112
|
+
RecurringPattern: ['companyId', 'signalType', 'agentId', 'status'],
|
|
113
|
+
KaizenProposal: ['companyId', 'status', 'ownerAgentId', 'sourceType'],
|
|
114
|
+
CascadeReviewAlert: ['companyId', 'agentId', 'actioned', 'triggeredAt'],
|
|
115
|
+
|
|
116
|
+
// ── PDSA / learning ───────────────────────────────────────────────────────
|
|
117
|
+
PDSACycle: ['companyId', 'taskId', 'agentId', 'actDecision', 'pillarId'],
|
|
118
|
+
KnowledgeAsset:['companyId', 'status', 'confidence', 'lastAppliedAt', 'applicableTaskTypes'],
|
|
119
|
+
HanseiRecord: ['companyId', 'taskId', 'agentId'],
|
|
120
|
+
|
|
121
|
+
// ── OKRs / cascade integrity ───────────────────────────────────────────────
|
|
122
|
+
QuarterlyOKR: [
|
|
123
|
+
'companyId', 'status', 'quarter', 'assigneeAgentId', 'catchballStatus', 'grade',
|
|
124
|
+
],
|
|
125
|
+
OKRFeedback: ['okrId', 'fromAgentId', 'status'],
|
|
126
|
+
ObstacleRecord: ['companyId', 'linkedOKRId', 'agentId', 'status'],
|
|
127
|
+
CatchballDisagreement: ['okrId', 'fromAgentId', 'objectionType'],
|
|
128
|
+
BeliefCeilingAssessment: ['okrId', 'agentId'],
|
|
129
|
+
SacrificeDeclaration: ['agentId', 'okrId', 'sacrificeVerified'],
|
|
130
|
+
PersonalCascade: ['companyId', 'agentId', 'integrityScore'],
|
|
131
|
+
CascadeIntegrityScore: ['companyId', 'agentId', 'calculatedAt', 'overallScore'],
|
|
132
|
+
SelfProposedOKR: ['companyId', 'agentId', 'status', 'selfAnalysisId'],
|
|
133
|
+
GoalDecompositionDraft: ['companyId', 'agentId', 'sourceAnalysisId'],
|
|
134
|
+
|
|
135
|
+
// ── Agent capability / coaching ───────────────────────────────────────────
|
|
136
|
+
CapabilityProfile: ['companyId', 'agentId', 'overallScore'],
|
|
137
|
+
CapabilityGap: ['companyId', 'agentId', 'dimension', 'status'],
|
|
138
|
+
FutureStateCommitment: ['companyId', 'agentId', 'gapClosureRate'],
|
|
139
|
+
EmpiricalCapabilityAssessment: ['companyId', 'agentId'],
|
|
140
|
+
AgentSelfAnalysis: ['companyId', 'agentId', 'triggerType', 'isComplete'],
|
|
141
|
+
AgentCollabRequest: ['companyId', 'fromAgentId', 'toAgentId', 'status'],
|
|
142
|
+
CoachingContract: ['companyId', 'agentId', 'coachAgentId', 'status'],
|
|
143
|
+
KataCoachingSession: ['companyId', 'agentId', 'weekOf', 'sessionStatus'],
|
|
144
|
+
MirrorObservation: ['companyId', 'agentId', 'patternType', 'status'],
|
|
145
|
+
PatternResponse: ['mirrorObservationId', 'agentId'],
|
|
146
|
+
MasteryMoment: ['companyId', 'agentId', 'okrId', 'status'],
|
|
147
|
+
MasteryReflection: ['masteryMomentId', 'agentId', 'specificityScore'],
|
|
148
|
+
MasteryTimeline: ['companyId', 'agentId'],
|
|
149
|
+
CharacterDimensionProfile: ['agentId'],
|
|
150
|
+
WeeklyReview: ['companyId', 'weekOf'],
|
|
151
|
+
DailyRoutineCard: ['companyId', 'agentId', 'date'],
|
|
152
|
+
|
|
153
|
+
// ── Agent mastery ──────────────────────────────────────────────────────────
|
|
154
|
+
MonthlyPredictionValidation: ['companyId', 'agentId', 'month'],
|
|
155
|
+
|
|
156
|
+
// ── Cost & budget ──────────────────────────────────────────────────────────
|
|
157
|
+
CostEvent: ['companyId', 'agentId', 'createdAt', 'model', 'biller'],
|
|
158
|
+
BusinessCostEvent: ['companyId', 'agentId', 'createdAt', 'businessTaskId'],
|
|
159
|
+
BudgetIncident: ['companyId', 'status', 'policyId'],
|
|
160
|
+
ActivityEvent: ['companyId', 'action', 'createdAt'],
|
|
161
|
+
TaskEnrichment: ['companyId', 'taskId', 'createdAt', 'workType', 'originKind'],
|
|
162
|
+
|
|
163
|
+
// ── CRM / outreach ────────────────────────────────────────────────────────
|
|
164
|
+
Lead: ['companyId', 'status', 'score', 'personId', 'businessId'],
|
|
165
|
+
Opportunity: ['companyId', 'stage', 'accountId', 'ownerId'],
|
|
166
|
+
Account: ['companyId', 'domain'],
|
|
167
|
+
Contact: ['companyId', 'accountId', 'email'],
|
|
168
|
+
ICPScore: ['companyId', 'accountId'],
|
|
169
|
+
Signal: ['companyId', 'type', 'accountId', 'createdAt'],
|
|
170
|
+
Sequence: ['companyId', 'status'],
|
|
171
|
+
Campaign: ['companyId', 'status'],
|
|
172
|
+
Deal: ['companyId', 'stage', 'ownerId'],
|
|
173
|
+
BusinessTask: ['companyId', 'status', 'assigneeId', 'goalId', 'dueDate', 'createdAt'],
|
|
174
|
+
CRMContact: ['companyId', 'personId', 'email', 'dunbarLayer', 'needsFollowUp'],
|
|
175
|
+
ActiveSequence: ['companyId', 'status'],
|
|
176
|
+
|
|
177
|
+
// ── People / social graph ─────────────────────────────────────────────────
|
|
178
|
+
Person: ['dunbarLayer', 'name', 'healthStatus', 'relationshipStrength', 'communityId'],
|
|
179
|
+
Identity: ['handle', 'platform'],
|
|
180
|
+
Organization: ['name', 'domain'],
|
|
181
|
+
FAVEESnapshot: ['personId', 'week'],
|
|
182
|
+
Conversation: ['platform', 'threadId'],
|
|
183
|
+
Episode: ['rawId', 'receivedAt', 'platform', 'sessionId'],
|
|
184
|
+
Topic: ['status', 'name'],
|
|
185
|
+
Question: ['status'],
|
|
186
|
+
Commitment: ['status', 'dueDate'],
|
|
187
|
+
|
|
188
|
+
// ── Email / triage ────────────────────────────────────────────────────────
|
|
189
|
+
Email: ['processedAt', 'address', 'triagePriority'],
|
|
190
|
+
EmailTriageResult: ['processedAt'],
|
|
191
|
+
TriageDecision: ['userOverride'],
|
|
192
|
+
|
|
193
|
+
// ── Auth / session (Harada) ───────────────────────────────────────────────
|
|
194
|
+
User: ['email', 'createdAt'],
|
|
195
|
+
Session: ['userId', 'expiresAt', 'createdAt'],
|
|
196
|
+
HaradaMap: ['userId', 'createdAt', 'updatedAt'],
|
|
197
|
+
|
|
198
|
+
// ── Strategy / OKR ───────────────────────────────────────────────────────
|
|
199
|
+
Strategy: ['companyId', 'goalId'],
|
|
200
|
+
SystemAim: ['companyId', 'industry'],
|
|
201
|
+
Department: ['companyId'], // gap filled — missing companyId index
|
|
202
|
+
|
|
203
|
+
// ── Domain config ─────────────────────────────────────────────────────────
|
|
204
|
+
CompanySkillConfig: ['domain'],
|
|
205
|
+
PillarRoutingConfig: ['companyId'],
|
|
206
|
+
SkillDef: ['name', 'contentHash'],
|
|
207
|
+
CompanySkill: ['key'],
|
|
208
|
+
// KnowledgeAsset indexes defined earlier (line ~117) with full property list
|
|
209
|
+
|
|
210
|
+
// ── HITL / interaction loop ───────────────────────────────────────────────
|
|
211
|
+
HITLInteraction: [
|
|
212
|
+
'companyId', 'taskId', 'agentId', 'status', 'kind',
|
|
213
|
+
'batchId', 'idempotencyKey', 'createdAt', 'resolvedAt', 'continuationPolicy',
|
|
214
|
+
],
|
|
215
|
+
HITLQuestion: ['interactionId', 'companyId', 'kind'],
|
|
216
|
+
HITLAnswer: ['interactionId', 'companyId', 'createdAt'],
|
|
217
|
+
AgentReadySignal: ['companyId', 'agentId', 'status', 'createdAt', 'sourceType'],
|
|
218
|
+
|
|
219
|
+
// ── HED (Helios Engineering Daemon) ──────────────────────────────────────
|
|
220
|
+
HEDOperation: ['hedId', 'status', 'wave', 'assignedAgentRole'],
|
|
221
|
+
HEDReviewFinding: ['opId', 'verdict'],
|
|
222
|
+
BlastRadiusEvent: ['opId', 'severity'],
|
|
223
|
+
|
|
224
|
+
// ── Misc (previously uncovered — gaps filled) ────────────────────────────
|
|
225
|
+
Hansei: ['agentId'], // written via MERGE in routes/sensei.js
|
|
226
|
+
ApprovalComment: ['taskId', 'approvalId'], // written via CREATE
|
|
227
|
+
ProcessMetric: ['companyId', 'agentId', 'metricName'],
|
|
228
|
+
ControlChartSignal: ['metricId', 'agentId', 'status'],
|
|
229
|
+
|
|
230
|
+
// ── Outreach / property / realty ─────────────────────────────────────────
|
|
231
|
+
Outreach: ['sentAt', 'replied'],
|
|
232
|
+
Property: ['name', 'city'],
|
|
233
|
+
ManagementCompany: ['name', 'domain', 'lifecycleStage', 'icpTier'],
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
// ── Composite indexes (multi-property together) ───────────────────────────────
|
|
237
|
+
// schema.assert() handles single-property indexes via the INDEXES map above.
|
|
238
|
+
// Composite indexes must be created via separate Cypher statements because
|
|
239
|
+
// schema.assert() only creates single-property label indexes.
|
|
240
|
+
// These are applied in schema-apply.js via direct mgQuery calls.
|
|
241
|
+
|
|
242
|
+
const COMPOSITE_INDEXES = [
|
|
243
|
+
{ label: 'Task', props: ['companyId', 'status'] },
|
|
244
|
+
{ label: 'KnowledgeAsset', props: ['companyId', 'status'] },
|
|
245
|
+
{ label: 'CompanySkill', props: ['companyId', 'key'] },
|
|
246
|
+
{ label: 'PillarRoutingConfig', props: ['companyId', 'pillarName'] },
|
|
247
|
+
{ label: 'HITLInteraction', props: ['taskId', 'status'] },
|
|
248
|
+
{ label: 'HITLInteraction', props: ['companyId', 'status'] },
|
|
249
|
+
{ label: 'HITLInteraction', props: ['companyId', 'idempotencyKey'] },
|
|
250
|
+
{ label: 'HITLInteraction', props: ['companyId', 'kind', 'status'] },
|
|
251
|
+
{ label: 'AgentReadySignal', props: ['agentId', 'status', 'sourceType'] },
|
|
252
|
+
// M6: Approval queries in decisions timeline and approvals list both filter by companyId+pillarId
|
|
253
|
+
// and companyId+status respectively. Single-prop indexes require sequential scans at scale.
|
|
254
|
+
{ label: 'Approval', props: ['companyId', 'pillarId'] },
|
|
255
|
+
{ label: 'Approval', props: ['companyId', 'status'] },
|
|
256
|
+
// PDSACycle decisions timeline filters by companyId+pillarId
|
|
257
|
+
{ label: 'PDSACycle', props: ['companyId', 'pillarId'] },
|
|
258
|
+
];
|
|
259
|
+
|
|
260
|
+
// ── Unique constraints ────────────────────────────────────────────────────────
|
|
261
|
+
// schema.assert() takes a map of label → list-of-lists.
|
|
262
|
+
// Each inner array is one unique constraint (single or composite properties).
|
|
263
|
+
|
|
264
|
+
const UNIQUE_CONSTRAINTS = {
|
|
265
|
+
// ── Core ──────────────────────────────────────────────────────────────────
|
|
266
|
+
Task: [['id']],
|
|
267
|
+
Comment: [['id']],
|
|
268
|
+
Workspace: [['id']],
|
|
269
|
+
Transcript: [['id']],
|
|
270
|
+
Routine: [['id']],
|
|
271
|
+
RoutineTrigger: [['id']],
|
|
272
|
+
RoutineRevision: [['id']],
|
|
273
|
+
RoutineRun: [['id']],
|
|
274
|
+
Approval: [['id']],
|
|
275
|
+
HeartbeatRun: [['id']],
|
|
276
|
+
Heartbeat: [['id']],
|
|
277
|
+
ActivityEvent: [['id']],
|
|
278
|
+
CostEvent: [['id']],
|
|
279
|
+
BudgetIncident: [['id']],
|
|
280
|
+
AndonAlert: [['id']],
|
|
281
|
+
|
|
282
|
+
// ── HBO goal hierarchy ────────────────────────────────────────────────────
|
|
283
|
+
Company: [['id']],
|
|
284
|
+
BusinessAgent: [['id']],
|
|
285
|
+
Department: [['id']],
|
|
286
|
+
CompanyGoal: [['id']],
|
|
287
|
+
BusinessGoal: [['id']],
|
|
288
|
+
BusinessTask: [['id']],
|
|
289
|
+
BusinessCostEvent:[['id']],
|
|
290
|
+
GoalNode: [['id']],
|
|
291
|
+
|
|
292
|
+
// ── Hoshin / cascade ──────────────────────────────────────────────────────
|
|
293
|
+
GoalPillar: [['id']],
|
|
294
|
+
ActionCell: [['id']],
|
|
295
|
+
GoalResearchBrief: [['id']],
|
|
296
|
+
StandardWorkDocument: [['id']],
|
|
297
|
+
StandardWorkDeviation:[['id']],
|
|
298
|
+
OKRSeed: [['id']],
|
|
299
|
+
|
|
300
|
+
// ── Project intelligence ──────────────────────────────────────────────────
|
|
301
|
+
HeliosProject: [['id']],
|
|
302
|
+
ProjectDocument: [['id']],
|
|
303
|
+
ProjectQuestion: [['id']],
|
|
304
|
+
DocumentEdit: [['id']],
|
|
305
|
+
PendingDocumentUpdate: [['id']],
|
|
306
|
+
DepartmentPage: [['id']],
|
|
307
|
+
PlanChangeProposal: [['id']],
|
|
308
|
+
|
|
309
|
+
// ── Company beliefs (previously missing entirely) ─────────────────────────
|
|
310
|
+
CompanyBelief: [['id']], // CRITICAL: concurrent MERGE safety
|
|
311
|
+
BeliefObservation:[['id']], // CRITICAL: concurrent MERGE safety
|
|
312
|
+
BudgetPolicy: [['id']], // CRITICAL: budget enforcement + MERGE safety
|
|
313
|
+
|
|
314
|
+
// ── Anomaly / signals ─────────────────────────────────────────────────────
|
|
315
|
+
AnomalySignal: [['id']],
|
|
316
|
+
AndonSignal: [['id']], // legacy label — keep until re-label migration clears
|
|
317
|
+
RecurringPattern: [['id']],
|
|
318
|
+
KaizenProposal: [['id']],
|
|
319
|
+
CascadeReviewAlert:[['id']],
|
|
320
|
+
|
|
321
|
+
// ── PDSA / learning ───────────────────────────────────────────────────────
|
|
322
|
+
PDSACycle: [['id']],
|
|
323
|
+
KnowledgeAsset: [['id']],
|
|
324
|
+
HanseiRecord: [['id']],
|
|
325
|
+
Hansei: [['id']], // previously missing — written via MERGE
|
|
326
|
+
|
|
327
|
+
// ── OKRs ──────────────────────────────────────────────────────────────────
|
|
328
|
+
QuarterlyOKR: [['id']],
|
|
329
|
+
OKRFeedback: [['id']],
|
|
330
|
+
ObstacleRecord: [['id']],
|
|
331
|
+
CatchballDisagreement: [['id']],
|
|
332
|
+
BeliefCeilingAssessment: [['id']],
|
|
333
|
+
SacrificeDeclaration: [['id']],
|
|
334
|
+
PersonalCascade: [['id']],
|
|
335
|
+
CascadeIntegrityScore: [['id']],
|
|
336
|
+
SelfProposedOKR: [['id']],
|
|
337
|
+
GoalDecompositionDraft: [['id']],
|
|
338
|
+
|
|
339
|
+
// ── Agent capability ──────────────────────────────────────────────────────
|
|
340
|
+
CapabilityProfile: [['id']],
|
|
341
|
+
CapabilityGap: [['id']],
|
|
342
|
+
FutureStateCommitment:[['id']],
|
|
343
|
+
EmpiricalCapabilityAssessment:[['id']],
|
|
344
|
+
AgentSelfAnalysis: [['id']],
|
|
345
|
+
AgentCollabRequest: [['id']],
|
|
346
|
+
CoachingContract: [['id']],
|
|
347
|
+
KataCoachingSession: [['id']],
|
|
348
|
+
MirrorObservation: [['id']],
|
|
349
|
+
PatternResponse: [['id']],
|
|
350
|
+
MasteryMoment: [['id']],
|
|
351
|
+
MasteryReflection: [['id']],
|
|
352
|
+
MasteryTimeline: [['id']],
|
|
353
|
+
CharacterDimensionProfile:[['id']],
|
|
354
|
+
WeeklyReview: [['id']],
|
|
355
|
+
DailyRoutineCard: [['id']],
|
|
356
|
+
MonthlyPredictionValidation:[['id']],
|
|
357
|
+
|
|
358
|
+
// ── Cost ──────────────────────────────────────────────────────────────────
|
|
359
|
+
TaskEnrichment: [['id']],
|
|
360
|
+
SchedulerState: [['id']],
|
|
361
|
+
|
|
362
|
+
// ── CRM / outreach ────────────────────────────────────────────────────────
|
|
363
|
+
Lead: [['id']],
|
|
364
|
+
Opportunity: [['id']],
|
|
365
|
+
Account: [['id']],
|
|
366
|
+
Contact: [['id']],
|
|
367
|
+
ICPScore: [['id']],
|
|
368
|
+
Signal: [['id']],
|
|
369
|
+
Sequence: [['id']],
|
|
370
|
+
Campaign: [['id']],
|
|
371
|
+
Deal: [['id']],
|
|
372
|
+
CRMContact: [['id']],
|
|
373
|
+
Person: [['id']],
|
|
374
|
+
Identity: [['id']],
|
|
375
|
+
Organization: [['id']],
|
|
376
|
+
|
|
377
|
+
// ── People / social ───────────────────────────────────────────────────────
|
|
378
|
+
FAVEESnapshot: [['id']],
|
|
379
|
+
Conversation: [['id']],
|
|
380
|
+
Episode: [['id']],
|
|
381
|
+
Topic: [['id']],
|
|
382
|
+
Question: [['id']],
|
|
383
|
+
Commitment: [['id']],
|
|
384
|
+
Business: [['id']],
|
|
385
|
+
Property: [['id']],
|
|
386
|
+
ManagementCompany:[['id']],
|
|
387
|
+
ParentCompany: [['id']],
|
|
388
|
+
Outreach: [['id']],
|
|
389
|
+
|
|
390
|
+
// ── Strategy ──────────────────────────────────────────────────────────────
|
|
391
|
+
Strategy: [['id']],
|
|
392
|
+
SystemAim: [['id']],
|
|
393
|
+
|
|
394
|
+
// ── Auth / session ────────────────────────────────────────────────────────
|
|
395
|
+
User: [['id']],
|
|
396
|
+
Session: [['id'], ['token']], // two constraints: id AND token
|
|
397
|
+
HaradaMap: [['id']],
|
|
398
|
+
|
|
399
|
+
// ── Domain config — NON-ID unique constraints ─────────────────────────────
|
|
400
|
+
CompanySkillConfig: [['companyId']], // unique per company, not per id
|
|
401
|
+
PillarRoutingConfig: [['companyId', 'pillarName']], // composite unique
|
|
402
|
+
SkillDef: [['id']],
|
|
403
|
+
|
|
404
|
+
// ── HITL ──────────────────────────────────────────────────────────────────
|
|
405
|
+
HITLInteraction: [['id']],
|
|
406
|
+
HITLQuestion: [['id']],
|
|
407
|
+
HITLAnswer: [['id']],
|
|
408
|
+
AgentReadySignal:[['id']],
|
|
409
|
+
|
|
410
|
+
// ── HED ───────────────────────────────────────────────────────────────────
|
|
411
|
+
HEDOperation: [['id']],
|
|
412
|
+
HEDReviewFinding: [['id']],
|
|
413
|
+
BlastRadiusEvent: [['id']],
|
|
414
|
+
|
|
415
|
+
// ── SyncCursor — keyed on .name, not .id ─────────────────────────────────
|
|
416
|
+
SyncCursor: [['name']], // previously missing; MERGE'd on .name
|
|
417
|
+
|
|
418
|
+
// ── Process metrics ───────────────────────────────────────────────────────
|
|
419
|
+
ProcessMetric: [['id']],
|
|
420
|
+
ControlChartSignal: [['id']],
|
|
421
|
+
};
|
|
422
|
+
|
|
423
|
+
module.exports = { INDEXES, COMPOSITE_INDEXES, UNIQUE_CONSTRAINTS };
|
|
@@ -574,6 +574,16 @@ async function runHBOMigrations(mgQuery) {
|
|
|
574
574
|
`CREATE CONSTRAINT FOR (n:ServiceAccount) REQUIRE n.id IS UNIQUE`,
|
|
575
575
|
`CREATE INDEX FOR (n:ServiceAccount) ON (n.agentId, n.status)`,
|
|
576
576
|
`CREATE INDEX FOR (n:ServiceAccount) ON (n.service, n.status)`,
|
|
577
|
+
|
|
578
|
+
// ── P4-03: executionStateJson backfill ────────────────────────────────────
|
|
579
|
+
// All existing Task nodes must have executionStateJson set to the string 'null'
|
|
580
|
+
// (not Cypher null) so the P4-04 CONTAINS check works correctly.
|
|
581
|
+
// A Cypher null property means the CONTAINS string operator would throw.
|
|
582
|
+
// Idempotent: WHERE clause skips nodes that already have the field.
|
|
583
|
+
// DEPLOYMENT NOTE: This daemon instance manages a single Memgraph tenant.
|
|
584
|
+
// The unscoped MATCH (t:Task) is intentional — all Task nodes in this Memgraph
|
|
585
|
+
// instance belong to the companies managed by this daemon.
|
|
586
|
+
`MATCH (t:Task) WHERE t.executionStateJson IS NULL SET t.executionStateJson = 'null'`,
|
|
577
587
|
];
|
|
578
588
|
|
|
579
589
|
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,131 @@
|
|
|
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
|
+
|
|
67
|
+
let success = 0, skipped = 0, failed = 0;
|
|
68
|
+
for (const cypher of migrations) {
|
|
69
|
+
try {
|
|
70
|
+
await mgQuery(cypher, {});
|
|
71
|
+
success++;
|
|
72
|
+
} catch (err) {
|
|
73
|
+
const msg = (err.message || '').toLowerCase();
|
|
74
|
+
if (msg.includes('already exists') || msg.includes('constraint already exists') ||
|
|
75
|
+
msg.includes('index already exists')) {
|
|
76
|
+
skipped++;
|
|
77
|
+
} else {
|
|
78
|
+
console.error('[schema-migrations-proj] Migration failed:', cypher.slice(0, 80), err.message);
|
|
79
|
+
failed++;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
console.log(`[schema-migrations-proj] done — success:${success} skipped:${skipped} failed:${failed}`);
|
|
84
|
+
await runIntelligenceNodeMigrations(mgQuery);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
// SM-proj-08 — Financial + CS + Marketing + Ops node schemas
|
|
89
|
+
async function runIntelligenceNodeMigrations(mgQuery) {
|
|
90
|
+
const migrations = [
|
|
91
|
+
// Finance nodes
|
|
92
|
+
"CREATE CONSTRAINT fin_snapshot_id IF NOT EXISTS FOR (n:FinancialSnapshot) REQUIRE n.id IS UNIQUE",
|
|
93
|
+
"CREATE INDEX fin_snapshot_cid IF NOT EXISTS FOR (n:FinancialSnapshot) ON (n.companyId)",
|
|
94
|
+
"CREATE CONSTRAINT burn_mult_id IF NOT EXISTS FOR (n:BurnMultipleCalc) REQUIRE n.id IS UNIQUE",
|
|
95
|
+
"CREATE INDEX burn_mult_cid IF NOT EXISTS FOR (n:BurnMultipleCalc) ON (n.companyId)",
|
|
96
|
+
"CREATE CONSTRAINT nrr_calc_id IF NOT EXISTS FOR (n:NRRCalc) REQUIRE n.id IS UNIQUE",
|
|
97
|
+
"CREATE INDEX nrr_calc_cid IF NOT EXISTS FOR (n:NRRCalc) ON (n.companyId)",
|
|
98
|
+
"CREATE CONSTRAINT gross_margin_id IF NOT EXISTS FOR (n:GrossMarginTimeSeries) REQUIRE n.id IS UNIQUE",
|
|
99
|
+
"CREATE INDEX gross_margin_cid IF NOT EXISTS FOR (n:GrossMarginTimeSeries) ON (n.companyId)",
|
|
100
|
+
"CREATE CONSTRAINT headcount_id IF NOT EXISTS FOR (n:HeadcountPlan) REQUIRE n.id IS UNIQUE",
|
|
101
|
+
"CREATE INDEX headcount_cid IF NOT EXISTS FOR (n:HeadcountPlan) ON (n.companyId)",
|
|
102
|
+
"CREATE CONSTRAINT infra_cost_id IF NOT EXISTS FOR (n:InfrastructureCost) REQUIRE n.id IS UNIQUE",
|
|
103
|
+
"CREATE INDEX infra_cost_cid IF NOT EXISTS FOR (n:InfrastructureCost) ON (n.companyId)",
|
|
104
|
+
"CREATE CONSTRAINT vendor_contract_id IF NOT EXISTS FOR (n:VendorContract) REQUIRE n.id IS UNIQUE",
|
|
105
|
+
"CREATE INDEX vendor_contract_cid IF NOT EXISTS FOR (n:VendorContract) ON (n.companyId)",
|
|
106
|
+
"CREATE CONSTRAINT cac_by_channel_id IF NOT EXISTS FOR (n:CACByChannel) REQUIRE n.id IS UNIQUE",
|
|
107
|
+
"CREATE INDEX cac_by_channel_cid IF NOT EXISTS FOR (n:CACByChannel) ON (n.companyId)",
|
|
108
|
+
// CS nodes
|
|
109
|
+
"CREATE CONSTRAINT acct_health_id IF NOT EXISTS FOR (n:AccountHealthScore) REQUIRE n.id IS UNIQUE",
|
|
110
|
+
"CREATE INDEX acct_health_cid IF NOT EXISTS FOR (n:AccountHealthScore) ON (n.companyId)",
|
|
111
|
+
"CREATE CONSTRAINT customer_journey_id IF NOT EXISTS FOR (n:CustomerJourneyNode) REQUIRE n.id IS UNIQUE",
|
|
112
|
+
"CREATE INDEX customer_journey_cid IF NOT EXISTS FOR (n:CustomerJourneyNode) ON (n.companyId)",
|
|
113
|
+
"CREATE CONSTRAINT renewal_id IF NOT EXISTS FOR (n:Renewal) REQUIRE n.id IS UNIQUE",
|
|
114
|
+
"CREATE INDEX renewal_cid IF NOT EXISTS FOR (n:Renewal) ON (n.companyId)",
|
|
115
|
+
"CREATE CONSTRAINT expansion_opp_id IF NOT EXISTS FOR (n:ExpansionOpportunity) REQUIRE n.id IS UNIQUE",
|
|
116
|
+
"CREATE INDEX expansion_opp_cid IF NOT EXISTS FOR (n:ExpansionOpportunity) ON (n.companyId)",
|
|
117
|
+
];
|
|
118
|
+
|
|
119
|
+
for (const stmt of migrations) {
|
|
120
|
+
try {
|
|
121
|
+
await mgQuery(stmt, {});
|
|
122
|
+
} catch (e) {
|
|
123
|
+
// IF NOT EXISTS protects against duplicates; log other errors
|
|
124
|
+
if (!e.message.includes('already exists') && !e.message.includes('IF NOT EXISTS')) {
|
|
125
|
+
console.warn('[schema-migrations-proj] SM-proj-08 warn:', e.message);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
module.exports = { runProjectMigrations };
|