@catalyst-cloud/schema 0.1.48 → 0.1.49

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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@catalyst-cloud/schema",
3
- "version": "0.1.48",
3
+ "version": "0.1.49",
4
4
  "type": "module",
5
5
  "description": "Typed Drizzle schema = single source of truth for the per-tenant Mirror DO SQLite store (CTC-13 / ADR-0002). Shared by the mirror Worker, the host-sync replica, and the browser OPFS replica.",
6
6
  "license": "MIT",
@@ -791,6 +791,72 @@ export const durableEventTypes = {
791
791
  wakes: false,
792
792
  entity: "ticket",
793
793
  },
794
+ // ── CTC-1597 — `intake` is the renamed `triage` phase, ADDITIVE beside it (D4). Same shape as the
795
+ // `phase.triage.*` block below; nothing in this repo emits these yet (the phase is dormant, outside
796
+ // RELAY_PHASES), declared now so CTC-1530 can be re-cut under the right name.
797
+ "phase.intake.abandoned": {
798
+ rationale:
799
+ "ADR-0039 durable-fact example ('a phase started, completed, failed, or was preempted'): the phase's worker abandoned it — a lifecycle terminal fact. CTC-1597 — the renamed `triage` phase.",
800
+ ingestVerb: "coordination.publish",
801
+ wakes: false,
802
+ entity: "ticket",
803
+ },
804
+ "phase.intake.boot-resume": {
805
+ rationale:
806
+ "ADR-0039 durable-fact example ('a phase started, completed, failed, or was preempted'): the daemon restarted and resumed this phase — a real coordination fact about continuity across a restart. CTC-1597 — the renamed `triage` phase.",
807
+ ingestVerb: "coordination.publish",
808
+ wakes: false,
809
+ entity: "ticket",
810
+ },
811
+ "phase.intake.complete": {
812
+ rationale:
813
+ "ADR-0039 durable-fact example ('a phase started, completed, failed, or was preempted'): the phase reported success. CTC-1597 — the renamed `triage` phase.",
814
+ ingestVerb: "coordination.publish",
815
+ wakes: false,
816
+ entity: "ticket",
817
+ },
818
+ "phase.intake.escalated": {
819
+ rationale:
820
+ "ADR-0039 durable-fact example ('a phase started, completed, failed, or was preempted'): the phase escalated to a human. CTC-1597 — the renamed `triage` phase.",
821
+ ingestVerb: "coordination.publish",
822
+ wakes: { consumerClass: "host-channel" },
823
+ entity: "ticket",
824
+ },
825
+ "phase.intake.failed": {
826
+ rationale:
827
+ "ADR-0039 durable-fact example ('a phase started, completed, failed, or was preempted'): the phase reported failure. CTC-1597 — the renamed `triage` phase.",
828
+ ingestVerb: "coordination.publish",
829
+ wakes: false,
830
+ entity: "ticket",
831
+ },
832
+ "phase.intake.failure-retracted": {
833
+ rationale:
834
+ "ADR-0039 durable-fact example ('a phase started, completed, failed, or was preempted'): a previously-reported failure was retracted (corrected) — a real correction fact. CTC-1597 — the renamed `triage` phase.",
835
+ ingestVerb: "coordination.publish",
836
+ wakes: false,
837
+ entity: "ticket",
838
+ },
839
+ "phase.intake.linear-transition": {
840
+ rationale:
841
+ "ADR-0039 durable-fact example ('a phase started, completed, failed, or was preempted'): the phase performed a real Linear ticket-state transition as part of its own execution. CTC-1597 — the renamed `triage` phase.",
842
+ ingestVerb: "coordination.publish",
843
+ wakes: false,
844
+ entity: "ticket",
845
+ },
846
+ "phase.intake.orphan-detected": {
847
+ rationale:
848
+ "ADR-0039 durable-fact example ('a phase started, completed, failed, or was preempted'): an orphaned phase-agent worker was detected — a coordination fact (state discovered), not a sample. CTC-1597 — the renamed `triage` phase.",
849
+ ingestVerb: "coordination.publish",
850
+ wakes: false,
851
+ entity: "ticket",
852
+ },
853
+ "phase.intake.revive": {
854
+ rationale:
855
+ "ADR-0039 durable-fact example ('a phase started, completed, failed, or was preempted'): a parked phase was revived. CTC-1597 — the renamed `triage` phase.",
856
+ ingestVerb: "coordination.publish",
857
+ wakes: false,
858
+ entity: "ticket",
859
+ },
794
860
  "phase.merge.complete": {
795
861
  rationale:
796
862
  "ADR-0039 durable-fact example ('a phase started, completed, failed, or was preempted'): the phase reported success.",
@@ -1519,65 +1585,70 @@ export const durableEventTypes = {
1519
1585
  wakes: false,
1520
1586
  entity: "ticket",
1521
1587
  },
1588
+ // ⛔ CTC-1597 — RETAINED, NOT DELETED (D4). These nine classify a real, committed August-2026 corpus
1589
+ // (docs/event-backbone/observed-types-2026-08.json) produced by the sibling `catalyst` repo's now-
1590
+ // retired daemon. Deleting them would make a legacy re-publish fall through to `unclassified`. The
1591
+ // phase was renamed to `intake` (see the `phase.intake.*` block above) — nothing in this repo emits
1592
+ // `phase.triage.*` going forward; each rationale below says so.
1522
1593
  "phase.triage.abandoned": {
1523
1594
  rationale:
1524
- "ADR-0039 durable-fact example ('a phase started, completed, failed, or was preempted'): the phase's worker abandoned it — a lifecycle terminal fact.",
1595
+ "ADR-0039 durable-fact example ('a phase started, completed, failed, or was preempted'): the phase's worker abandoned it — a lifecycle terminal fact. CTC-1597 renamed this phase to `intake`; this key is RETAINED to classify the historical corpus and any legacy re-publish — nothing emits it going forward.",
1525
1596
  ingestVerb: "coordination.publish",
1526
1597
  wakes: false,
1527
1598
  entity: "ticket",
1528
1599
  },
1529
1600
  "phase.triage.boot-resume": {
1530
1601
  rationale:
1531
- "ADR-0039 durable-fact example ('a phase started, completed, failed, or was preempted'): the daemon restarted and resumed this phase — a real coordination fact about continuity across a restart.",
1602
+ "ADR-0039 durable-fact example ('a phase started, completed, failed, or was preempted'): the daemon restarted and resumed this phase — a real coordination fact about continuity across a restart. CTC-1597 renamed this phase to `intake`; this key is RETAINED to classify the historical corpus and any legacy re-publish — nothing emits it going forward.",
1532
1603
  ingestVerb: "coordination.publish",
1533
1604
  wakes: false,
1534
1605
  entity: "ticket",
1535
1606
  },
1536
1607
  "phase.triage.complete": {
1537
1608
  rationale:
1538
- "ADR-0039 durable-fact example ('a phase started, completed, failed, or was preempted'): the phase reported success.",
1609
+ "ADR-0039 durable-fact example ('a phase started, completed, failed, or was preempted'): the phase reported success. CTC-1597 renamed this phase to `intake`; this key is RETAINED to classify the historical corpus and any legacy re-publish — nothing emits it going forward.",
1539
1610
  ingestVerb: "coordination.publish",
1540
1611
  wakes: false,
1541
1612
  entity: "ticket",
1542
1613
  },
1543
1614
  "phase.triage.escalated": {
1544
1615
  rationale:
1545
- "ADR-0039 durable-fact example ('a phase started, completed, failed, or was preempted'): the phase escalated to a human.",
1616
+ "ADR-0039 durable-fact example ('a phase started, completed, failed, or was preempted'): the phase escalated to a human. CTC-1597 renamed this phase to `intake`; this key is RETAINED to classify the historical corpus and any legacy re-publish — nothing emits it going forward.",
1546
1617
  ingestVerb: "coordination.publish",
1547
1618
  wakes: { consumerClass: "host-channel" },
1548
1619
  entity: "ticket",
1549
1620
  },
1550
1621
  "phase.triage.failed": {
1551
1622
  rationale:
1552
- "ADR-0039 durable-fact example ('a phase started, completed, failed, or was preempted'): the phase reported failure.",
1623
+ "ADR-0039 durable-fact example ('a phase started, completed, failed, or was preempted'): the phase reported failure. CTC-1597 renamed this phase to `intake`; this key is RETAINED to classify the historical corpus and any legacy re-publish — nothing emits it going forward.",
1553
1624
  ingestVerb: "coordination.publish",
1554
1625
  wakes: false,
1555
1626
  entity: "ticket",
1556
1627
  },
1557
1628
  "phase.triage.failure-retracted": {
1558
1629
  rationale:
1559
- "ADR-0039 durable-fact example ('a phase started, completed, failed, or was preempted'): a previously-reported failure was retracted (corrected) — a real correction fact.",
1630
+ "ADR-0039 durable-fact example ('a phase started, completed, failed, or was preempted'): a previously-reported failure was retracted (corrected) — a real correction fact. CTC-1597 renamed this phase to `intake`; this key is RETAINED to classify the historical corpus and any legacy re-publish — nothing emits it going forward.",
1560
1631
  ingestVerb: "coordination.publish",
1561
1632
  wakes: false,
1562
1633
  entity: "ticket",
1563
1634
  },
1564
1635
  "phase.triage.linear-transition": {
1565
1636
  rationale:
1566
- "ADR-0039 durable-fact example ('a phase started, completed, failed, or was preempted'): the phase performed a real Linear ticket-state transition as part of its own execution.",
1637
+ "ADR-0039 durable-fact example ('a phase started, completed, failed, or was preempted'): the phase performed a real Linear ticket-state transition as part of its own execution. CTC-1597 renamed this phase to `intake`; this key is RETAINED to classify the historical corpus and any legacy re-publish — nothing emits it going forward.",
1567
1638
  ingestVerb: "coordination.publish",
1568
1639
  wakes: false,
1569
1640
  entity: "ticket",
1570
1641
  },
1571
1642
  "phase.triage.orphan-detected": {
1572
1643
  rationale:
1573
- "ADR-0039 durable-fact example ('a phase started, completed, failed, or was preempted'): an orphaned phase-agent worker was detected — a coordination fact (state discovered), not a sample.",
1644
+ "ADR-0039 durable-fact example ('a phase started, completed, failed, or was preempted'): an orphaned phase-agent worker was detected — a coordination fact (state discovered), not a sample. CTC-1597 renamed this phase to `intake`; this key is RETAINED to classify the historical corpus and any legacy re-publish — nothing emits it going forward.",
1574
1645
  ingestVerb: "coordination.publish",
1575
1646
  wakes: false,
1576
1647
  entity: "ticket",
1577
1648
  },
1578
1649
  "phase.triage.revive": {
1579
1650
  rationale:
1580
- "ADR-0039 durable-fact example ('a phase started, completed, failed, or was preempted'): a parked phase was revived.",
1651
+ "ADR-0039 durable-fact example ('a phase started, completed, failed, or was preempted'): a parked phase was revived. CTC-1597 renamed this phase to `intake`; this key is RETAINED to classify the historical corpus and any legacy re-publish — nothing emits it going forward.",
1581
1652
  ingestVerb: "coordination.publish",
1582
1653
  wakes: false,
1583
1654
  entity: "ticket",
@@ -270,6 +270,13 @@ export const MIRROR_MIGRATIONS = {
270
270
  tag: "0036_watery_hercules",
271
271
  breakpoints: true,
272
272
  },
273
+ {
274
+ idx: 37,
275
+ version: "6",
276
+ when: 1788479421934,
277
+ tag: "0037_ctc1597_workflow_slot_intake",
278
+ breakpoints: true,
279
+ },
273
280
  ],
274
281
  },
275
282
  migrations: {
@@ -344,5 +351,7 @@ export const MIRROR_MIGRATIONS = {
344
351
  "ALTER TABLE `agent_activities` ADD `action` text;--> statement-breakpoint\nALTER TABLE `agent_activities` ADD `parameter` text;--> statement-breakpoint\nALTER TABLE `agent_activities` ADD `result` text;--> statement-breakpoint\nUPDATE agent_activities\n SET action = json_extract(raw, '$.content.action'),\n parameter = json_extract(raw, '$.content.parameter'),\n result = json_extract(raw, '$.content.result')\n WHERE raw IS NOT NULL\n AND json_valid(raw)\n AND json_extract(raw, '$.content.type') = 'action';\n",
345
352
  "0036_watery_hercules":
346
353
  "CREATE TABLE `fleet_activity_rate` (\n\t`host_id` text NOT NULL,\n\t`ticket` text NOT NULL,\n\t`bucket_start_ms` integer NOT NULL,\n\t`events` integer NOT NULL,\n\tPRIMARY KEY(`host_id`, `ticket`, `bucket_start_ms`)\n);\n--> statement-breakpoint\nCREATE TABLE `fleet_anomalies` (\n\t`kind` text NOT NULL,\n\t`host_id` text NOT NULL,\n\t`ticket` text NOT NULL,\n\t`detected_at_ms` integer NOT NULL,\n\t`observed` integer NOT NULL,\n\t`baseline` integer NOT NULL,\n\t`detail` text,\n\t`updated_at` integer NOT NULL,\n\tPRIMARY KEY(`kind`, `host_id`, `ticket`)\n);\n--> statement-breakpoint\nCREATE TABLE `fleet_health_sends` (\n\t`host_id` text NOT NULL,\n\t`status` text NOT NULL,\n\t`status_changed_at_ms` integer NOT NULL,\n\t`sent_at_ms` integer NOT NULL,\n\t`delivered` integer NOT NULL,\n\t`failed` integer NOT NULL,\n\tPRIMARY KEY(`host_id`, `status`, `status_changed_at_ms`)\n);\n--> statement-breakpoint\nCREATE TABLE `fleet_host_liveness` (\n\t`host_id` text PRIMARY KEY NOT NULL,\n\t`status` text NOT NULL,\n\t`reason` text,\n\t`last_heartbeat_ms` integer,\n\t`last_activity_ms` integer,\n\t`last_seen_ms` integer,\n\t`period_ms` integer,\n\t`grace_ms` integer,\n\t`agent_version` text,\n\t`status_changed_at_ms` integer,\n\t`updated_at` integer NOT NULL\n);\n",
354
+ "0037_ctc1597_workflow_slot_intake":
355
+ "-- CTC-1597 — rename the persisted workflow slot `triage` -> `intake`.\n--\n-- ⭐ Deliberately a DATA migration in the SHARED bundle (the 0014/0031 pattern): the DO and every\n-- host / OPFS replica apply the same MIRROR_MIGRATIONS through the same runner, so each side renames\n-- its OWN rows locally. That converges every replica WITHOUT emitting a change_log delta per row\n-- (migrations bypass appendChange), so no ring-buffer flood.\n--\n-- ⚠️ THE REGISTRY D1 IS THE AUTHORITY and gets its own migration\n-- (apps/mirror/src/registry/migrations/20260903T1928_workflow_slot_intake.sql); this one exists\n-- because a D1-only rewrite bumps no `workflow_rev` and pushes nothing, so a DO that is never\n-- re-saved would sit on the old value forever. THIS migration is what makes the rename correct on\n-- every DO/replica, including one offline at merge time. There is no separate re-projection script:\n-- an already-connected browser/host that needs to see the rename before its own next open converges\n-- the ordinary way — an operator re-saving that team's stage mapping calls the existing full-replace\n-- projection handler (`handleWorkflowMappingUpsert`, MirrorDO.ts), which DOES emit a change_log\n-- delta. No new production route was added for a one-time rename.\n--\n-- PK-safe: (team_id, slot) — and the PK does NOT consider `removed_at`, so a SOFT-DELETED `intake`\n-- row still occupies the slot. Drop a stale `triage` row for a team that already holds ANY `intake`\n-- row — live or soft-deleted — then rename what is left. Idempotent under execReplaySafe replay — a\n-- row already renamed to `intake` no longer matches `slot = 'triage'` on a re-run.\n--\n-- ⛔ CTC-1597 remediate (C2): an earlier draft of the EXISTS guard filtered `removed_at IS NULL`,\n-- reasoning that a soft-deleted `intake` row should never cause a still-live `triage` row to be\n-- dropped instead of renamed. That reasoning had it backwards — the PK collision this guard exists\n-- to prevent happens precisely BECAUSE `removed_at` is not part of the primary key: a host that\n-- receives a soft-deleted `intake` row via replication (from a DO that already renamed and was then\n-- unmapped) before it has applied this migration locally still has its own live `triage` row: the\n-- DELETE's old filter left the `triage` row in place, and the following blind UPDATE then tried to\n-- rename it to a slot value a row already occupies, violating the PK and aborting the whole bundle.\n-- Matching on ANY `intake` row (regardless of `removed_at`) fixes it: the stale `triage` row is\n-- dropped, not renamed, whenever the slot is already spoken for.\n--\n-- ⛔ CTC-1597 remediate (validate-plan finding 1) — a `triage`-slot row whose OWN mapped state is\n-- ITSELF type='triage' (Linear's reserved Triage inbox, legally name-matched into the OLD\n-- `RECOMMENDED_STAGES` entry before this ticket) must NOT be renamed blind: `SLOT_STATE_TYPES.intake`\n-- no longer accepts that type (Scope B), so renaming it in place would silently flip the team's\n-- `types_compatible` check from pass to a blocking fail with no data fix and no recourse — the write\n-- path's own validation (the same narrowing) would refuse a re-save of the identical row. Soft-delete\n-- it instead, via `removed_at` — this table's own convention for \"this slot is unmapped\" (see the\n-- table's doc comment in packages/schema/src/mirror.ts) — so the team lands in the ordinary,\n-- already-actionable \"needs a stage\" state a brand-new team starts in, which Scope C's per-team\n-- runbook resolves. Runs BEFORE the final rename so no live-or-dead row anywhere keeps the retired\n-- `triage` slot value; guarded by `removed_at IS NULL` so a replay is a no-op.\nUPDATE `team_workflow_mapping`\n SET `removed_at` = CAST(unixepoch() AS INTEGER) * 1000\n WHERE `slot` = 'triage' AND `linear_state_type` = 'triage' AND `removed_at` IS NULL;\n--> statement-breakpoint\nDELETE FROM `team_workflow_mapping`\n WHERE `slot` = 'triage'\n AND EXISTS (SELECT 1 FROM `team_workflow_mapping` m2\n WHERE m2.`team_id` = `team_workflow_mapping`.`team_id` AND m2.`slot` = 'intake');\n--> statement-breakpoint\nUPDATE `team_workflow_mapping` SET `slot` = 'intake' WHERE `slot` = 'triage';\n",
347
356
  },
348
357
  } as const;
package/src/mirror.ts CHANGED
@@ -383,7 +383,11 @@ export const team_workflow_mapping = sqliteTable(
383
383
  "team_workflow_mapping",
384
384
  {
385
385
  team_id: text("team_id").notNull(),
386
- /** One of the eleven Catalyst slots; the five load-bearing ones gate the fleet. */
386
+ /** One of the eleven Catalyst slots; the five load-bearing ones gate the fleet. CTC-1597 renamed
387
+ * the `triage` slot to `intake` — migration 0037 rewrites `slot='triage'` rows in place, except a
388
+ * row whose OWN `linear_state_type` is itself `'triage'`, which it soft-deletes rather than
389
+ * renames (that type is no longer valid for `intake` — see `SLOT_STATE_TYPES` in
390
+ * `@catalyst-cloud/types`). */
387
391
  slot: text("slot").notNull(),
388
392
  /** THE AUTHORITY — joined against workflow_states.id by every consumer. */
389
393
  linear_state_id: text("linear_state_id").notNull(),