@catalyst-cloud/schema 0.1.14 → 0.1.15
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/index.ts +7 -0
- package/src/migrations.generated.ts +9 -0
- package/src/mirror.ts +45 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@catalyst-cloud/schema",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.15",
|
|
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/index.ts
CHANGED
|
@@ -19,6 +19,8 @@ import {
|
|
|
19
19
|
projects,
|
|
20
20
|
cycles,
|
|
21
21
|
workflow_states,
|
|
22
|
+
// CTC-624: the tenant's per-team workflow DECISION, projected from the D1 registry (ADR-0033).
|
|
23
|
+
team_workflow_mapping,
|
|
22
24
|
initiatives,
|
|
23
25
|
project_initiatives,
|
|
24
26
|
comments,
|
|
@@ -79,6 +81,8 @@ export const mirrorSchema = {
|
|
|
79
81
|
projects,
|
|
80
82
|
cycles,
|
|
81
83
|
workflow_states,
|
|
84
|
+
// CTC-624: projected from the D1 registry, the one mirror table whose authority is not a provider.
|
|
85
|
+
team_workflow_mapping,
|
|
82
86
|
initiatives,
|
|
83
87
|
project_initiatives,
|
|
84
88
|
comments,
|
|
@@ -143,6 +147,9 @@ export const mirrorEntityTables = {
|
|
|
143
147
|
projects,
|
|
144
148
|
cycles,
|
|
145
149
|
workflow_states,
|
|
150
|
+
// CTC-624: the tenant's per-team workflow decision. On this plane deliberately — it is how
|
|
151
|
+
// anything reaches a host at all, and the browser board is a legitimate consumer (ADR-0033).
|
|
152
|
+
team_workflow_mapping,
|
|
146
153
|
initiatives,
|
|
147
154
|
project_initiatives,
|
|
148
155
|
comments,
|
|
@@ -179,6 +179,13 @@ export const MIRROR_MIGRATIONS = {
|
|
|
179
179
|
tag: "0023_ambiguous_micromax",
|
|
180
180
|
breakpoints: true,
|
|
181
181
|
},
|
|
182
|
+
{
|
|
183
|
+
idx: 24,
|
|
184
|
+
version: "6",
|
|
185
|
+
when: 1786984427741,
|
|
186
|
+
tag: "0024_long_spencer_smythe",
|
|
187
|
+
breakpoints: true,
|
|
188
|
+
},
|
|
182
189
|
],
|
|
183
190
|
},
|
|
184
191
|
migrations: {
|
|
@@ -228,5 +235,7 @@ export const MIRROR_MIGRATIONS = {
|
|
|
228
235
|
"CREATE TABLE `workflow_states` (\n\t`id` text PRIMARY KEY NOT NULL,\n\t`team_id` text,\n\t`name` text,\n\t`type` text,\n\t`position` real,\n\t`color` text,\n\t`archived_at` integer,\n\t`updated_at` integer\n);\n--> statement-breakpoint\nCREATE INDEX `idx_workflow_states_team` ON `workflow_states` (`team_id`);--> statement-breakpoint\nALTER TABLE `issues` ADD `state_type` text;--> statement-breakpoint\nALTER TABLE `issues` ADD `state_position` real;",
|
|
229
236
|
"0023_ambiguous_micromax":
|
|
230
237
|
"CREATE TABLE `push_subscriptions` (\n\t`endpoint` text PRIMARY KEY NOT NULL,\n\t`member_id` text NOT NULL,\n\t`p256dh` text NOT NULL,\n\t`auth` text NOT NULL,\n\t`created_at` integer,\n\t`last_used_at` integer\n);\n--> statement-breakpoint\nCREATE INDEX `idx_push_subscriptions_member` ON `push_subscriptions` (`member_id`);",
|
|
238
|
+
"0024_long_spencer_smythe":
|
|
239
|
+
"CREATE TABLE `team_workflow_mapping` (\n\t`team_id` text NOT NULL,\n\t`slot` text NOT NULL,\n\t`linear_state_id` text NOT NULL,\n\t`linear_state_name` text,\n\t`linear_state_type` text,\n\t`workflow_rev` integer,\n\t`updated_at` integer,\n\t`removed_at` integer,\n\tPRIMARY KEY(`team_id`, `slot`)\n);\n",
|
|
231
240
|
},
|
|
232
241
|
} as const;
|
package/src/mirror.ts
CHANGED
|
@@ -318,6 +318,51 @@ export const cycles = sqliteTable("cycles", {
|
|
|
318
318
|
// (the prerequisite CTC-277's kanban board blocks on). Small, workspace-wide set — full sweep each
|
|
319
319
|
// pass, same discipline as `labels`/`cycles`. No `removed_at`: Linear ARCHIVES a workflow state rather
|
|
320
320
|
// than deleting it (`archived_at`), same convention `issues.archived_at` already uses.
|
|
321
|
+
/**
|
|
322
|
+
* CTC-624 — the tenant's per-team workflow DECISION, replicated to hosts and the browser.
|
|
323
|
+
*
|
|
324
|
+
* ⭐ THIS IS THE ONE MIRROR TABLE WHOSE AUTHORITY IS NOT A PROVIDER. Every other table here is a
|
|
325
|
+
* projection of something Linear or GitHub owns; this is a projection of the **D1 registry**
|
|
326
|
+
* (`team_workflow_mapping`), which is where the tenant's choice actually lives. It rides this plane
|
|
327
|
+
* because the plane is how anything reaches a host at all, and ADR-0033 records why that does not
|
|
328
|
+
* re-open ADR-0028: the states themselves stay a mirrored Linear fact (`workflow_states`, below),
|
|
329
|
+
* while "which state means dispatchable" is a decision Linear has no field for.
|
|
330
|
+
*
|
|
331
|
+
* ⛔ `linear_state_name` / `linear_state_type` ARE DISPLAY SNAPSHOTS AND MUST NEVER BE JOINED ON.
|
|
332
|
+
* CTC-403: a Linear-to-Linear import preserved every human identifier and re-minted ~248 state UUIDs,
|
|
333
|
+
* so every cache still MATCHED by name while pointing at nothing. `linear_state_id` is the authority;
|
|
334
|
+
* a consumer resolving a slot joins THAT against `workflow_states` and finds the row absent when the
|
|
335
|
+
* workspace has been re-minted. That absence is the alarm, and it only works because the join key is
|
|
336
|
+
* the id.
|
|
337
|
+
*
|
|
338
|
+
* ⭐ `workflow_rev` RIDES EVERY ROW, and that is what makes the receipt possible. A host reads the max
|
|
339
|
+
* `workflow_rev` present in its own replica and echoes it on `/connect` (CTC-628) — read at RUNTIME
|
|
340
|
+
* FROM THE REPLICA, never from a config file, the same LOADED-not-LOCKED discipline
|
|
341
|
+
* `loadedSchemaIdentity` enforces for the migration bundle. Without the column on the row there is
|
|
342
|
+
* nothing for the host to read, and "we pushed it" would again be unfalsifiable.
|
|
343
|
+
*
|
|
344
|
+
* PK is `(team_id, slot)` — no `account_id`, because a Mirror DO **is** one account (same convention
|
|
345
|
+
* as every table here). Soft-deleted via `removed_at` so clearing a slot propagates as a change rather
|
|
346
|
+
* than as a row that silently stops being mentioned.
|
|
347
|
+
*/
|
|
348
|
+
export const team_workflow_mapping = sqliteTable(
|
|
349
|
+
"team_workflow_mapping",
|
|
350
|
+
{
|
|
351
|
+
team_id: text("team_id").notNull(),
|
|
352
|
+
/** One of the eleven Catalyst slots; the five load-bearing ones gate the fleet. */
|
|
353
|
+
slot: text("slot").notNull(),
|
|
354
|
+
/** THE AUTHORITY — joined against workflow_states.id by every consumer. */
|
|
355
|
+
linear_state_id: text("linear_state_id").notNull(),
|
|
356
|
+
linear_state_name: text("linear_state_name"),
|
|
357
|
+
linear_state_type: text("linear_state_type"),
|
|
358
|
+
/** The ACCOUNT-wide revision this row was written at. The scalar a host echoes back. */
|
|
359
|
+
workflow_rev: integer("workflow_rev"),
|
|
360
|
+
updated_at: integer("updated_at"),
|
|
361
|
+
removed_at: integer("removed_at"),
|
|
362
|
+
},
|
|
363
|
+
(t) => [primaryKey({ columns: [t.team_id, t.slot] })],
|
|
364
|
+
);
|
|
365
|
+
|
|
321
366
|
export const workflow_states = sqliteTable(
|
|
322
367
|
"workflow_states",
|
|
323
368
|
{
|