@teamscala/service-boot 0.23.367 → 0.23.369

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 CHANGED
@@ -6,7 +6,7 @@
6
6
  },
7
7
  "name": "@teamscala/service-boot",
8
8
  "type": "module",
9
- "version": "0.23.367",
9
+ "version": "0.23.369",
10
10
  "sideEffects": false,
11
11
  "imports": {
12
12
  "#service-boot/*": "./src/*",
@@ -40,6 +40,7 @@ export interface EscalationEpisode {
40
40
  workItemId: string | null;
41
41
  resolvedAt: Date | null;
42
42
  lastUpdatedAt: Date | null;
43
+ deliveredCommentId?: string | null;
43
44
  }
44
45
 
45
46
  export interface FaultEscalationPolicy {
@@ -82,8 +82,11 @@ const FaultEscalationConfigSchema = v.object({
82
82
  ),
83
83
  resolve_after_minutes: v.number(),
84
84
  update_after_minutes: v.number(),
85
- /** The developer-session project escalations are parented to. */
86
- parent_project_id: v.string(),
85
+ /** The orchestrator's work item — escalations are COMMENTS on it, never
86
+ * task rows (the fleet-task-placement CHECK constraint refuses a system
87
+ * task without session identity, and an escalation is a finding, not an
88
+ * assigned unit of work). */
89
+ orchestrator_work_item_id: v.string(),
87
90
  /** Per-kind copy for the message (impact + recommendation). Falls back to default_copy. */
88
91
  copy: v.record(v.string(), v.object({ impact: v.string(), recommendation: v.string() })),
89
92
  default_copy: v.object({ impact: v.string(), recommendation: v.string() }),
@@ -142,12 +145,12 @@ async function orpc(): Promise<OrpcSurface> {
142
145
  "platform-fault-escalation: no ORPC appRouter — the service has no generated routers to write through",
143
146
  );
144
147
  }
145
- const { getPrisma } = await import("@teamscala/db/prisma-registry");
146
- const { loadRegistryConfig } = await import("@teamscala/db/registry/load-config");
148
+ const prismaRegistry = await import("@teamscala/db/prisma-registry");
149
+ const registryLoader = await import("@teamscala/db/registry/load-config");
147
150
  const { PlatformIdentitySchema } = await import(
148
151
  "@teamscala/db-validation/registry-schemas/platform-identity"
149
152
  );
150
- const platformIdentity = await loadRegistryConfig(
153
+ const platformIdentity = await registryLoader.loadRegistryConfig(
151
154
  "config",
152
155
  "platform-identity",
153
156
  PlatformIdentitySchema,
@@ -156,7 +159,7 @@ async function orpc(): Promise<OrpcSurface> {
156
159
  { ...appRouter } as never,
157
160
  {
158
161
  context: {
159
- prisma: getPrisma(),
162
+ prisma: prismaRegistry.getPrisma(),
160
163
  user: {
161
164
  id: "system",
162
165
  isSuperAdmin: true,
@@ -316,26 +319,21 @@ async function deliverEscalation(
316
319
  );
317
320
  }
318
321
 
319
- // 4. Durable record: the work_items task the PA reads back / the operator answers on.
320
- const workItem = await (await orpc()).work_items.create({
322
+ // 4. Durable record: a COMMENT on the orchestrator's work item — an
323
+ // escalation is a finding the orchestrator/operator triages, not an
324
+ // assigned task (the fleet-task-placement constraint refuses task rows
325
+ // without session identity by design).
326
+ const comment = await (await orpc()).comments.create({
321
327
  data: {
322
- kind: "task",
323
- parent_id: config.parent_project_id,
324
- organisation_id: bot.organisation_id,
325
- title: `Fault: ${kind}`.slice(0, 50),
326
- description: composeEscalationDescription({ aggregate, impact: copy.impact, recommendation: copy.recommendation }),
327
- status: "pending",
328
- kind_data: {
329
- platform_fault: true,
330
- event_kind: kind,
331
- event_count: aggregate.count,
332
- first_seen_at: aggregate.firstSeen.toISOString(),
333
- last_seen_at: aggregate.lastSeen.toISOString(),
334
- delivered_message_id: messageId,
335
- escalated_at: now.toISOString(),
336
- },
328
+ work_item_id: config.orchestrator_work_item_id,
329
+ content: composeEscalationMessage({
330
+ kind,
331
+ aggregate,
332
+ impact: copy.impact,
333
+ recommendation: copy.recommendation,
334
+ }),
335
+ source_cli_session_id: null,
337
336
  },
338
- select: { id: true },
339
337
  });
340
338
 
341
339
  // 5. Readback-correct the episode: delivered + linked in one update.
@@ -343,7 +341,8 @@ async function deliverEscalation(
343
341
  where: { kind },
344
342
  data: {
345
343
  escalated_at: now.toISOString(),
346
- work_item_id: workItem.id,
344
+ work_item_id: config.orchestrator_work_item_id,
345
+ delivered_comment_id: comment.id,
347
346
  delivered_message_id: messageId,
348
347
  last_updated_at: now.toISOString(),
349
348
  updated_at: now.toISOString(),
@@ -355,11 +354,12 @@ async function deliverEscalation(
355
354
  payload: {
356
355
  event_kind: kind,
357
356
  count: aggregate.count,
358
- work_item_id: workItem.id,
357
+ work_item_id: config.orchestrator_work_item_id,
358
+ delivered_comment_id: comment.id,
359
359
  delivered_message_id: messageId,
360
360
  },
361
361
  });
362
- return { messageId, workItemId: workItem.id };
362
+ return { messageId, workItemId: config.orchestrator_work_item_id };
363
363
  }
364
364
 
365
365
  async function updateEpisode(aggregate: FaultAggregate, episode: EscalationEpisode): Promise<void> {