@catalyst-cloud/schema 0.1.36 → 0.1.38
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 +1 -1
- package/src/events/registry.ts +12 -1
- package/src/migrations.generated.ts +9 -0
- package/src/mirror.ts +25 -6
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@catalyst-cloud/schema",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.38",
|
|
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",
|
package/src/events/registry.ts
CHANGED
|
@@ -85,7 +85,18 @@ export const durableEventTypes = {
|
|
|
85
85
|
"ask.replied": {
|
|
86
86
|
rationale:
|
|
87
87
|
"A human answered — or tried to answer — an ask in its own thread, and the mirror's ask-decision trigger reached a named outcome. ADR-0039's own durable-fact example ('a human answer was received'), applied to the customer-facing ask loop (CTC-1326). Wake-eligible because the operator learning about it LATE is the defect CTC-1326 exists to close: before this, the only surface was a 15-minute Linear read poll.",
|
|
88
|
-
|
|
88
|
+
// ⛔ NOT `webhook.ingest`, THOUGH A COMMENT WEBHOOK IS WHAT SETS IT OFF (CTC-1049 review). That verb
|
|
89
|
+
// carries a hard invariant this fact cannot satisfy: `test/webhook-event-types.test.ts` asserts the
|
|
90
|
+
// mapping is SURJECTIVE over the webhook.ingest slice — every type in it must be reachable from a
|
|
91
|
+
// raw (source, eventType, action) tuple — and the CTC-1049 gate enforces the same coverage from the
|
|
92
|
+
// other side. This type is not a mapping of a delivery; it is MINTED INSIDE the mirror DO after the
|
|
93
|
+
// ask-decision trigger runs (which itself calls Linear), so no fixture could ever produce it and
|
|
94
|
+
// declaring it webhook.ingest silently broke both checks the moment it was added.
|
|
95
|
+
// ⚠️ `coordination.publish` is the closest TRUE verb, not a perfect one: the operator copy really
|
|
96
|
+
// does arrive over a cross-DO publish, but the origin copy is appended in place. The registry has
|
|
97
|
+
// no verb for "minted by the mirror itself", which is a real gap worth a follow-up rather than a
|
|
98
|
+
// reason to mislabel this one.
|
|
99
|
+
ingestVerb: "coordination.publish",
|
|
89
100
|
wakes: { consumerClass: "host-channel" },
|
|
90
101
|
entity: "ticket",
|
|
91
102
|
},
|
|
@@ -249,6 +249,13 @@ export const MIRROR_MIGRATIONS = {
|
|
|
249
249
|
tag: "0033_lethal_electro",
|
|
250
250
|
breakpoints: true,
|
|
251
251
|
},
|
|
252
|
+
{
|
|
253
|
+
idx: 34,
|
|
254
|
+
version: "6",
|
|
255
|
+
when: 1788312053951,
|
|
256
|
+
tag: "0034_special_onslaught",
|
|
257
|
+
breakpoints: true,
|
|
258
|
+
},
|
|
252
259
|
],
|
|
253
260
|
},
|
|
254
261
|
migrations: {
|
|
@@ -317,5 +324,7 @@ export const MIRROR_MIGRATIONS = {
|
|
|
317
324
|
"ALTER TABLE `issues` ADD `creator_id` text;--> statement-breakpoint\nALTER TABLE `issues` ADD `creator_name` text;--> statement-breakpoint\nALTER TABLE `issues` ADD `creator_avatar_url` text;",
|
|
318
325
|
"0033_lethal_electro":
|
|
319
326
|
"ALTER TABLE `labels` ADD `team_id` text;--> statement-breakpoint\nCREATE INDEX `idx_labels_team` ON `labels` (`team_id`);",
|
|
327
|
+
"0034_special_onslaught":
|
|
328
|
+
"CREATE INDEX `idx_processed_events_source_received` ON `processed_events` (`source`,`received_at`);",
|
|
320
329
|
},
|
|
321
330
|
} as const;
|
package/src/mirror.ts
CHANGED
|
@@ -1070,12 +1070,31 @@ export const pr_review_threads = sqliteTable(
|
|
|
1070
1070
|
|
|
1071
1071
|
// ── Mirror infra (not domain entities — never ride the change-feed) ─────────────────────────────────
|
|
1072
1072
|
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1073
|
+
/**
|
|
1074
|
+
* CTC-1441 — the webhook dedup marker, one row per provider delivery. DELIBERATELY NEVER PRUNED (see
|
|
1075
|
+
* `linearPurgeStatements`' doc in apps/mirror/src/do/MirrorDO.ts: old markers must never collide with
|
|
1076
|
+
* a new workspace's deliveries), so this table only ever grows — 762,190 rows on tenant-0 as of
|
|
1077
|
+
* 2026-09-02, climbing ~53,000 a day.
|
|
1078
|
+
*
|
|
1079
|
+
* ⭐ THE INDEX IS LOAD-BEARING, AND ITS COLUMN ORDER IS THE WHOLE POINT. `computeFreshness`
|
|
1080
|
+
* (apps/mirror/src/do/changefeed.ts) runs `SELECT max(received_at) ... WHERE source = ?` once per
|
|
1081
|
+
* source on EVERY reconcile pass, which is every alarm tick. With only the `delivery_id` primary key
|
|
1082
|
+
* that predicate was a full table scan, twice per alarm — measured at 2.21 BILLION rows read a day on
|
|
1083
|
+
* tenant-0, 93-100% of the account's entire Durable Objects rows-read bill. `(source, received_at)`
|
|
1084
|
+
* lets SQLite's max() optimization seek to the end of the source group and read ONE index entry;
|
|
1085
|
+
* `(source)` alone would only narrow the scan to that source's ~half of the table, which is not the
|
|
1086
|
+
* fix. Pinned by apps/mirror/test/processed-events-query-plan.test.ts.
|
|
1087
|
+
*/
|
|
1088
|
+
export const processed_events = sqliteTable(
|
|
1089
|
+
"processed_events",
|
|
1090
|
+
{
|
|
1091
|
+
delivery_id: text("delivery_id").primaryKey(),
|
|
1092
|
+
source: text("source"),
|
|
1093
|
+
event_type: text("event_type"),
|
|
1094
|
+
received_at: integer("received_at"),
|
|
1095
|
+
},
|
|
1096
|
+
(t) => [index("idx_processed_events_source_received").on(t.source, t.received_at)],
|
|
1097
|
+
);
|
|
1079
1098
|
|
|
1080
1099
|
export const change_log = sqliteTable("change_log", {
|
|
1081
1100
|
seq: integer("seq").primaryKey({ autoIncrement: true }),
|