@catalyst-cloud/sdk 0.16.1 → 0.16.2

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 (2) hide show
  1. package/dist/node.js +18 -25
  2. package/package.json +5 -5
package/dist/node.js CHANGED
@@ -3187,6 +3187,13 @@ var MIRROR_MIGRATIONS = {
3187
3187
  when: 1790382152574,
3188
3188
  tag: "0042_ctc1858_fleet_activity_started_at_and_class",
3189
3189
  breakpoints: true
3190
+ },
3191
+ {
3192
+ idx: 43,
3193
+ version: "6",
3194
+ when: 1791583125906,
3195
+ tag: "0043_ctc5374_processed_events_without_rowid",
3196
+ breakpoints: true
3190
3197
  }
3191
3198
  ]
3192
3199
  },
@@ -3233,12 +3240,17 @@ var MIRROR_MIGRATIONS = {
3233
3240
  "0039_unknown_nebula": "CREATE TABLE `flow_daily` (\n `repo_id` text NOT NULL,\n `day` text NOT NULL,\n `active_count` integer NOT NULL,\n `entries` integer NOT NULL,\n `exits_to_done` integer NOT NULL,\n `exits_to_canceled` integer NOT NULL,\n `stage_counts` text,\n `aging_p50_seconds` integer,\n `aging_p90_seconds` integer,\n `fleet_active_count` integer,\n `fleet_exits_to_done` integer,\n `fleet_cycle_p50_seconds` integer,\n `fleet_cycle_p90_seconds` integer,\n `interactive_active_count` integer,\n `interactive_exits_to_done` integer,\n `interactive_cycle_p50_seconds` integer,\n `interactive_cycle_p90_seconds` integer,\n `stalled_count` integer,\n `estimated_ticket_count` integer,\n `open_pr_count` integer,\n `open_pr_draft_count` integer,\n `prs_merged` integer,\n `pr_cycle_p50_seconds` integer,\n `pr_cycle_p90_seconds` integer,\n `pr_age_p50_seconds` integer,\n `pr_age_p90_seconds` integer,\n `computed_at` integer NOT NULL,\n PRIMARY KEY(`repo_id`, `day`)\n);\n--> statement-breakpoint\nCREATE INDEX `idx_flow_daily_day` ON `flow_daily` (`day`);",
3234
3241
  "0040_ctc1885_workflow_state_name_backfill": "-- CTC-1885 \u2014 re-stamp cached workflow-state NAMES from the authoritative workflow_states row, by\n-- stable state id.\n--\n-- \u2B50 Deliberately a pure DATA migration in the SHARED bundle (the 0014/0031/0037 pattern): the DO\n-- and every host / OPFS replica apply the same MIRROR_MIGRATIONS through the same runner, so each\n-- store repairs its OWN rows locally. That converges the already-stale corpus WITHOUT emitting a\n-- change_log delta per historical row (migrations bypass appendChange), so the 200k-row / 7-day ring\n-- buffer is not flooded by the very backlog this repairs, and a replica that was OFFLINE at deploy\n-- time heals itself the moment it next opens and applies the bundle.\n--\n-- The defect: `issues.state` is denormalized at issue ingest and `team_workflow_mapping.\n-- linear_state_name` at mapping projection, while `workflow_states.name` is written by the\n-- workflow-state sweep. A Linear rename (Triage -> Intake) updated only the latter, so issues kept\n-- answering the OLD name (/api/v1/issues returns the column verbatim; the board keys columns by it)\n-- and the mapping kept displaying it. Runtime cascade (this ticket's reconcile change) covers FUTURE\n-- renames; THIS migration repairs every row already stale at upgrade time, including on stores the\n-- runtime never gets to replay.\n--\n-- Matching is by STABLE ID, never by the mutable old string (the table's own header: the name is a\n-- display snapshot, never a lookup key \u2014 a same-named row under a different state id must not move):\n-- \u2022 issues: `issues.state_id` -> `workflow_states.id`. The state id is globally unique (Linear\n-- UUID), which IS the team scoping \u2014 an issue's state belongs to exactly one team's workflow.\n-- \u2022 team_workflow_mapping: `linear_state_id` AND `team_id` both resolve. The mapping's team fence\n-- is kept because a Linear-to-Linear import can re-mint ids per team (CTC-403).\n-- Both updates are guarded by `ws.name IS NOT <cached>` so already-converged rows are untouched and\n-- a replay (execReplaySafe re-runs a statement after a crash between success and tag-record) is a\n-- no-op. `IS` / `IS NOT` keep every comparison NULL-safe.\n--\n-- \u26D4 NO timestamps, revisions, or tombstones are rewritten: `updated_at`, `workflow_rev`, and\n-- `removed_at` survive exactly as stored. A state rename is provider-owned state metadata, not an\n-- issue mutation and not a tenant mapping decision; advancing those fields would corrupt freshness,\n-- board ranking, last-write-wins ordering, and the host workflow-rev receipt. Only the NAME fields\n-- converge. (Tombstoned mapping rows converge too \u2014 a dead row's display snapshot changing is\n-- unobservable and emits nothing; the runtime cascade, which DOES emit deltas, stays live-only.)\nUPDATE `issues`\n SET `state` = (SELECT `ws`.`name` FROM `workflow_states` `ws` WHERE `ws`.`id` = `issues`.`state_id`)\n WHERE EXISTS (\n SELECT 1 FROM `workflow_states` `ws`\n WHERE `ws`.`id` = `issues`.`state_id`\n AND `ws`.`name` IS NOT NULL\n AND `ws`.`name` IS NOT `issues`.`state`);\n--> statement-breakpoint\nUPDATE `team_workflow_mapping`\n SET `linear_state_name` = (SELECT `ws`.`name`\n FROM `workflow_states` `ws`\n WHERE `ws`.`id` = `team_workflow_mapping`.`linear_state_id`\n AND `ws`.`team_id` = `team_workflow_mapping`.`team_id`)\n WHERE EXISTS (\n SELECT 1 FROM `workflow_states` `ws`\n WHERE `ws`.`id` = `team_workflow_mapping`.`linear_state_id`\n AND `ws`.`team_id` = `team_workflow_mapping`.`team_id`\n AND `ws`.`name` IS NOT NULL\n AND `ws`.`name` IS NOT `team_workflow_mapping`.`linear_state_name`);\n",
3235
3242
  "0041_many_marvel_boy": '-- CTC-3123: re-key flow_daily from (repo_id, day) to (project_id, day), so a per-project Flow page\n-- never double-counts a repo that two projects share (ADR-0013\'s project_repos).\n--\n-- A table recreation, because SQLite cannot change a primary key in place. Two edits to the\n-- drizzle-kit output, both deliberate:\n-- \u2022 The copy writes `\'repo:\' || repo_id` into project_id. The repo-to-project map lives in the\n-- registry D1, not in this store, so a migration cannot know a row\'s project. The snapshot writer\n-- (apps/mirror/src/do/flow-snapshot.ts) re-stamps `repo:<owner>/<name>` rows to the real project\n-- id on its next pass. A repo with no registry project keeps the marker: still readable, never\n-- dropped, and counted by the flow route\'s `flow-daily-unkeyed-rows` log line.\n-- \u2022 No PRAGMA foreign_keys lines: flow_daily has no foreign keys, so toggling enforcement buys\n-- nothing, and no earlier migration in this bundle issues a pragma. The leading DROP of\n-- `__new_flow_daily` clears a half-built copy while flow_daily still exists. If the old table\n-- was already dropped, applyMigrations first restores the copied table\'s name before replay.\nDROP TABLE IF EXISTS `__new_flow_daily`;--> statement-breakpoint\nCREATE TABLE `__new_flow_daily` (\n `project_id` text NOT NULL,\n `repo_id` text NOT NULL,\n `day` text NOT NULL,\n `active_count` integer NOT NULL,\n `entries` integer NOT NULL,\n `exits_to_done` integer NOT NULL,\n `exits_to_canceled` integer NOT NULL,\n `stage_counts` text,\n `aging_p50_seconds` integer,\n `aging_p90_seconds` integer,\n `fleet_active_count` integer,\n `fleet_exits_to_done` integer,\n `fleet_cycle_p50_seconds` integer,\n `fleet_cycle_p90_seconds` integer,\n `interactive_active_count` integer,\n `interactive_exits_to_done` integer,\n `interactive_cycle_p50_seconds` integer,\n `interactive_cycle_p90_seconds` integer,\n `stalled_count` integer,\n `estimated_ticket_count` integer,\n `open_pr_count` integer,\n `open_pr_draft_count` integer,\n `prs_merged` integer,\n `pr_cycle_p50_seconds` integer,\n `pr_cycle_p90_seconds` integer,\n `pr_age_p50_seconds` integer,\n `pr_age_p90_seconds` integer,\n `computed_at` integer NOT NULL,\n PRIMARY KEY(`project_id`, `day`)\n);\n--> statement-breakpoint\nINSERT INTO `__new_flow_daily`("project_id", "repo_id", "day", "active_count", "entries", "exits_to_done", "exits_to_canceled", "stage_counts", "aging_p50_seconds", "aging_p90_seconds", "fleet_active_count", "fleet_exits_to_done", "fleet_cycle_p50_seconds", "fleet_cycle_p90_seconds", "interactive_active_count", "interactive_exits_to_done", "interactive_cycle_p50_seconds", "interactive_cycle_p90_seconds", "stalled_count", "estimated_ticket_count", "open_pr_count", "open_pr_draft_count", "prs_merged", "pr_cycle_p50_seconds", "pr_cycle_p90_seconds", "pr_age_p50_seconds", "pr_age_p90_seconds", "computed_at") SELECT \'repo:\' || "repo_id", "repo_id", "day", "active_count", "entries", "exits_to_done", "exits_to_canceled", "stage_counts", "aging_p50_seconds", "aging_p90_seconds", "fleet_active_count", "fleet_exits_to_done", "fleet_cycle_p50_seconds", "fleet_cycle_p90_seconds", "interactive_active_count", "interactive_exits_to_done", "interactive_cycle_p50_seconds", "interactive_cycle_p90_seconds", "stalled_count", "estimated_ticket_count", "open_pr_count", "open_pr_draft_count", "prs_merged", "pr_cycle_p50_seconds", "pr_cycle_p90_seconds", "pr_age_p50_seconds", "pr_age_p90_seconds", "computed_at" FROM `flow_daily`;--> statement-breakpoint\nDROP TABLE `flow_daily`;--> statement-breakpoint\nALTER TABLE `__new_flow_daily` RENAME TO `flow_daily`;--> statement-breakpoint\nCREATE INDEX `idx_flow_daily_day` ON `flow_daily` (`day`);\n',
3236
- "0042_ctc1858_fleet_activity_started_at_and_class": "ALTER TABLE `fleet_activity` ADD `started_at` integer;--> statement-breakpoint\nALTER TABLE `fleet_activity` ADD `duration_ms` integer;--> statement-breakpoint\nALTER TABLE `fleet_activity` ADD `container_class` text;"
3243
+ "0042_ctc1858_fleet_activity_started_at_and_class": "ALTER TABLE `fleet_activity` ADD `started_at` integer;--> statement-breakpoint\nALTER TABLE `fleet_activity` ADD `duration_ms` integer;--> statement-breakpoint\nALTER TABLE `fleet_activity` ADD `container_class` text;",
3244
+ "0043_ctc5374_processed_events_without_rowid": '-- CTC-5374: rebuild processed_events as a WITHOUT ROWID table, so a webhook\'s dedup marker writes 2\n-- rows (the row in its primary-key b-tree, plus idx_processed_events_source_received) instead of 3\n-- (a rowid row, a separate primary-key index, and the source index). Measured 2026-10-09: about\n-- 113k rows written a day saved across tenant-0 and tenant-3.\n--\n-- Hand-written (drizzle-kit cannot declare WITHOUT ROWID), on the 0041 recreation pattern:\n-- \u2022 The copy keeps only the last 7 days, plus each source\'s newest row so computeFreshness\'s\n-- max(received_at) per source never turns null. GitHub redelivers only deliveries from the past\n-- 3 days and Linear retries within hours, so an older marker can never dedup anything. This is a\n-- one-time prune: nothing deletes from this table afterwards, because a recurring expiry would\n-- write 2 rows per delete and cost more than WITHOUT ROWID saves.\n-- \u2022 received_at is epoch milliseconds (MirrorDO\'s ingest and fleet-activity\'s coordination insert).\n-- \u2022 The leading DROP of `__new_processed_events` clears a half-built copy while processed_events\n-- still exists. If the old table was already dropped, applyMigrations first restores the copied\n-- table\'s name before replay.\nDROP TABLE IF EXISTS `__new_processed_events`;--> statement-breakpoint\nCREATE TABLE `__new_processed_events` (\n `delivery_id` text PRIMARY KEY NOT NULL,\n `source` text,\n `event_type` text,\n `received_at` integer\n) WITHOUT ROWID;\n--> statement-breakpoint\nINSERT INTO `__new_processed_events`("delivery_id", "source", "event_type", "received_at") SELECT "delivery_id", "source", "event_type", "received_at" FROM `processed_events` AS p WHERE p."received_at" >= (CAST(strftime(\'%s\', \'now\') AS INTEGER) - 7 * 86400) * 1000 OR p."received_at" = (SELECT max(q."received_at") FROM `processed_events` AS q WHERE q."source" = p."source");--> statement-breakpoint\nDROP TABLE `processed_events`;--> statement-breakpoint\nALTER TABLE `__new_processed_events` RENAME TO `processed_events`;--> statement-breakpoint\nCREATE INDEX `idx_processed_events_source_received` ON `processed_events` (`source`,`received_at`);\n'
3237
3245
  }
3238
3246
  };
3239
3247
 
3240
3248
  // node_modules/@catalyst-cloud/schema/src/migrate.ts
3241
3249
  var STATEMENT_BREAKPOINT = "--> statement-breakpoint";
3250
+ var RECREATED_TABLES = {
3251
+ "0041_many_marvel_boy": "flow_daily",
3252
+ "0043_ctc5374_processed_events_without_rowid": "processed_events"
3253
+ };
3242
3254
  var LEDGER_DDL = "CREATE TABLE IF NOT EXISTS __drizzle_migrations (id INTEGER PRIMARY KEY AUTOINCREMENT, hash TEXT NOT NULL, created_at INTEGER)";
3243
3255
  function applyMigrations(db, bundle = MIRROR_MIGRATIONS, now = Date.now()) {
3244
3256
  db.exec(LEDGER_DDL);
@@ -3254,14 +3266,15 @@ function applyMigrations(db, bundle = MIRROR_MIGRATIONS, now = Date.now()) {
3254
3266
  if (sql2 === void 0) {
3255
3267
  throw new Error(`applyMigrations: journal tag "${entry.tag}" has no SQL in the bundle`);
3256
3268
  }
3257
- if (entry.tag === "0041_many_marvel_boy") {
3269
+ const recreated = RECREATED_TABLES[entry.tag];
3270
+ if (recreated !== void 0) {
3258
3271
  const tables = new Set(
3259
3272
  db.query(
3260
- "SELECT name FROM sqlite_master WHERE type = 'table' AND name IN ('flow_daily', '__new_flow_daily')"
3273
+ `SELECT name FROM sqlite_master WHERE type = 'table' AND name IN ('${recreated}', '__new_${recreated}')`
3261
3274
  ).map((row) => String(row.name))
3262
3275
  );
3263
- if (!tables.has("flow_daily") && tables.has("__new_flow_daily")) {
3264
- db.exec("ALTER TABLE `__new_flow_daily` RENAME TO `flow_daily`");
3276
+ if (!tables.has(recreated) && tables.has(`__new_${recreated}`)) {
3277
+ db.exec(`ALTER TABLE \`__new_${recreated}\` RENAME TO \`${recreated}\``);
3265
3278
  }
3266
3279
  }
3267
3280
  for (const raw of sql2.split(STATEMENT_BREAKPOINT)) {
@@ -5073,18 +5086,10 @@ var currentStateEventTypes = {
5073
5086
  }
5074
5087
  };
5075
5088
  var telemetryEventTypes = {
5076
- "catalyst.mirror.request.summary": {
5077
- reason: "CTC-4063 \u2014 one request per route and verified caller. Sampling would discard the distribution required for per-key usage.",
5078
- sampling: { kind: "always" }
5079
- },
5080
5089
  "telemetry.ingest.tenant-forged": {
5081
5090
  reason: "CTC-2788 \u2014 a gateway payload named a tenant other than its token's on `account_id` or `catalyst.tenant`, and the gateway overwrote it. Rare by construction: a current runner image emits `catalyst.tenant` equal to its own tenant, so this fires only on a stale or wrong token, or on a deliberate forgery. Each occurrence is independently actionable (which tenant held the token, which tenant was claimed, which signal), so it is never sampled away.",
5082
5091
  sampling: { kind: "always" }
5083
5092
  },
5084
- "telemetry.ingest.scope-forged": {
5085
- reason: "CTC-2789 \u2014 a gateway payload claimed `catalyst.scope` itself (e.g. `platform`), and the gateway overwrote it to `tenant`, the only scope a tenant credential can ever write. Rare by construction: no producer in this repo sets the attribute, so this fires only on a stale image or a deliberate forgery. Each occurrence is independently actionable (which tenant held the token, which scope was claimed, which signal), so it is never sampled away.",
5086
- sampling: { kind: "always" }
5087
- },
5088
5093
  "account.ratelimit.sampled": {
5089
5094
  reason: "A periodic provider rate-limit budget reading \u2014 a gauge, not a state transition."
5090
5095
  },
@@ -5388,18 +5393,6 @@ var telemetryEventTypes = {
5388
5393
  },
5389
5394
  "worktree.salvage.skipped": {
5390
5395
  reason: "14,391 lines/mo \u2014 logged on every recovery pass that finds nothing to salvage (mirrors the recovery.skipped.* retry-storm shape). worktree.salvage.created is the durable fact when a salvage actually happens."
5391
- },
5392
- "setup.run": {
5393
- reason: "CTC-1175 \u2014 one point per setup-run terminalization (complete or failed), carrying the server-derived durationMs (completedAt - createdAt). Each occurrence is individually meaningful (one run's outcome), the same phase.lifecycle posture \u2014 always, never sampled away.",
5394
- sampling: { kind: "always" }
5395
- },
5396
- "setup.cache": {
5397
- reason: "CTC-1175 \u2014 one point per resolveSetupCache verdict (hit/miss/integrity_failure) \u2014 the server's own cache-layer decision, never caller-supplied. Individually meaningful per repo/declaration scope, not a rollup-only gauge.",
5398
- sampling: { kind: "always" }
5399
- },
5400
- "setup.extract": {
5401
- reason: "CTC-1175 \u2014 one point per runner-side snapshot-extract observation (restored/absent/integrity_failure/toolchain_refused/capacity_exceeded), carrying the runner-measured extract duration. Individually meaningful per run, not a rollup gauge.",
5402
- sampling: { kind: "always" }
5403
5396
  }
5404
5397
  };
5405
5398
  var DURABLE_EVENT_TYPES = keysOf(durableEventTypes);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@catalyst-cloud/sdk",
3
- "version": "0.16.1",
3
+ "version": "0.16.2",
4
4
  "description": "Read and update your Catalyst Cloud data, and receive live changes in your apps.",
5
5
  "license": "MIT",
6
6
  "author": "Coalesce Labs",
@@ -100,8 +100,8 @@
100
100
  ],
101
101
  "devDependencies": {
102
102
  "@catalyst-cloud/read-model": "^0.16.0",
103
- "@catalyst-cloud/replicate": "0.16.0",
104
- "@catalyst-cloud/schema": "0.16.0",
103
+ "@catalyst-cloud/replicate": "0.16.1",
104
+ "@catalyst-cloud/schema": "0.16.1",
105
105
  "@opentelemetry/api": "^1.9.0",
106
106
  "@opentelemetry/context-async-hooks": "^2.8.0",
107
107
  "@opentelemetry/resources": "^2.8.0",
@@ -119,8 +119,8 @@
119
119
  "@sqlite.org/sqlite-wasm": "*",
120
120
  "better-sqlite3": "*",
121
121
  "@catalyst-cloud/read-model": "^0.16.0",
122
- "@catalyst-cloud/replicate": "0.16.0",
123
- "@catalyst-cloud/schema": "0.16.0"
122
+ "@catalyst-cloud/replicate": "0.16.1",
123
+ "@catalyst-cloud/schema": "0.16.1"
124
124
  },
125
125
  "peerDependenciesMeta": {
126
126
  "@opentelemetry/api": {