@catalyst-cloud/schema 0.1.50 → 0.1.52

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.50",
3
+ "version": "0.1.52",
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,68 +791,86 @@ 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.
794
+ // CTC-1530 — `intake` joins `RELAY_PHASES` at its HEAD, gated dark behind `CATALYST_INTAKE_PHASE`.
795
+ // This block mirrors `phase.remediate.*`'s own set exactly (no git/rebase-specific suffixes —
796
+ // intake never touches a branch), so the generic phase-lifecycle/park machinery every relay phase
797
+ // already shares works for it with no special-casing. Supersedes CTC-1597's placeholder
798
+ // declaration of the same family (a smaller, `phase.triage.*`-shaped set, since at that point
799
+ // `intake` was still dormant outside `RELAY_PHASES`) now that this ticket makes it a real,
800
+ // lease-backed, preemptable phase.
797
801
  "phase.intake.abandoned": {
798
802
  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.",
803
+ "ADR-0039 durable-fact example ('a phase started, completed, failed, or was preempted'): the phase's worker abandoned it — a lifecycle terminal fact.",
800
804
  ingestVerb: "coordination.publish",
801
805
  wakes: false,
802
806
  entity: "ticket",
803
807
  },
804
808
  "phase.intake.boot-resume": {
805
809
  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.",
810
+ "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.",
807
811
  ingestVerb: "coordination.publish",
808
812
  wakes: false,
809
813
  entity: "ticket",
810
814
  },
811
- "phase.intake.complete": {
815
+ "phase.intake.boot-resume-gated": {
812
816
  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.",
817
+ "ADR-0039 durable-fact example ('a phase started, completed, failed, or was preempted'): the daemon restarted but gated (did not immediately resume) this phase.",
814
818
  ingestVerb: "coordination.publish",
815
819
  wakes: false,
816
820
  entity: "ticket",
817
821
  },
818
- "phase.intake.escalated": {
822
+ "phase.intake.complete": {
819
823
  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.",
824
+ "ADR-0039 durable-fact example ('a phase started, completed, failed, or was preempted'): the phase reported success.",
821
825
  ingestVerb: "coordination.publish",
822
- wakes: { consumerClass: "host-channel" },
826
+ wakes: false,
823
827
  entity: "ticket",
824
828
  },
825
829
  "phase.intake.failed": {
826
830
  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.",
831
+ "ADR-0039 durable-fact example ('a phase started, completed, failed, or was preempted'): the phase reported failure.",
828
832
  ingestVerb: "coordination.publish",
829
833
  wakes: false,
830
834
  entity: "ticket",
831
835
  },
832
- "phase.intake.failure-retracted": {
836
+ "phase.intake.orphan-detected": {
833
837
  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.",
838
+ "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.",
835
839
  ingestVerb: "coordination.publish",
836
840
  wakes: false,
837
841
  entity: "ticket",
838
842
  },
839
- "phase.intake.linear-transition": {
843
+ "phase.intake.park": {
840
844
  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.",
845
+ "ADR-0039 durable-fact example ('a phase started, completed, failed, or was preempted'): the intake phase was PARKED after N consecutive failures (CTC-1270) — a terminal-until-released outcome, distinct from `phase.intake.failed`, which a reader must be able to tell apart. Paired with `phase.intake.revive`.",
842
846
  ingestVerb: "coordination.publish",
843
847
  wakes: false,
844
848
  entity: "ticket",
845
849
  },
846
- "phase.intake.orphan-detected": {
850
+ "phase.intake.preempted": {
847
851
  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.",
852
+ "ADR-0039 durable-fact example ('a phase started, completed, failed, or was preempted'): a higher-priority worker preempted this phase.",
853
+ ingestVerb: "coordination.publish",
854
+ wakes: false,
855
+ entity: "ticket",
856
+ },
857
+ "phase.intake.reclaim": {
858
+ rationale:
859
+ "ADR-0039 durable-fact example ('a phase started, completed, failed, or was preempted'): ownership of orphaned work was reclaimed by a new worker — an ownership transition akin to a lease reassignment.",
860
+ ingestVerb: "coordination.publish",
861
+ wakes: false,
862
+ entity: "ticket",
863
+ },
864
+ "phase.intake.resumed-after-preemption": {
865
+ rationale:
866
+ "ADR-0039 durable-fact example ('a phase started, completed, failed, or was preempted'): the phase resumed after being preempted earlier — the paired fact to *.preempted.",
849
867
  ingestVerb: "coordination.publish",
850
868
  wakes: false,
851
869
  entity: "ticket",
852
870
  },
853
871
  "phase.intake.revive": {
854
872
  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.",
873
+ "ADR-0039 durable-fact example ('a phase started, completed, failed, or was preempted'): a repeated-failure park of the intake phase was revived (CTC-1270) — the paired fact to `phase.intake.park`.",
856
874
  ingestVerb: "coordination.publish",
857
875
  wakes: false,
858
876
  entity: "ticket",
@@ -277,6 +277,13 @@ export const MIRROR_MIGRATIONS = {
277
277
  tag: "0037_ctc1597_workflow_slot_intake",
278
278
  breakpoints: true,
279
279
  },
280
+ {
281
+ idx: 38,
282
+ version: "6",
283
+ when: 1788900456820,
284
+ tag: "0038_ctc1855_fleet_activity_routing",
285
+ breakpoints: true,
286
+ },
280
287
  ],
281
288
  },
282
289
  migrations: {
@@ -353,5 +360,7 @@ export const MIRROR_MIGRATIONS = {
353
360
  "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
361
  "0037_ctc1597_workflow_slot_intake":
355
362
  "-- 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",
363
+ "0038_ctc1855_fleet_activity_routing":
364
+ "ALTER TABLE `fleet_activity` ADD `provider` text;--> statement-breakpoint\nALTER TABLE `fleet_activity` ADD `model` text;--> statement-breakpoint\nALTER TABLE `fleet_activity` ADD `effort` text;--> statement-breakpoint\nALTER TABLE `fleet_activity` ADD `routing_source` text;",
356
365
  },
357
366
  } as const;
package/src/mirror.ts CHANGED
@@ -1179,6 +1179,14 @@ export const fleet_activity = sqliteTable(
1179
1179
  pr_number: integer("pr_number"),
1180
1180
  pr_url: text("pr_url"),
1181
1181
  orch_id: text("orch_id"),
1182
+ // CTC-1855 — the CTC-1095 routing tuple a phase actually started with, projected from the
1183
+ // worker.session.started / phase.<p>.complete attrs the runner already emits. COALESCE-forward
1184
+ // in reduceFleetActivity (a tuple-less later event never wipes it). Nullable: sibling-daemon
1185
+ // rows and pre-migration rows genuinely have no tuple, and absence must stay absence.
1186
+ provider: text("provider"),
1187
+ model: text("model"),
1188
+ effort: text("effort"),
1189
+ routing_source: text("routing_source"), // StageRouting.source verbatim: "exact" | "fallback"
1182
1190
  last_event_name: text("last_event_name"),
1183
1191
  last_event_ts: integer("last_event_ts"), // ms epoch — the reducer watermark
1184
1192
  updated_at: integer("updated_at"), // ms epoch — mirrors last_event_ts; upsertRow guard column