Commitb45db84unknown_key
feat(db): add processed_events idempotency table
feat(db): add processed_events idempotency table Signal Bus P1 — introduces a `processed_events` table used by the new inbound events receiver to short-circuit duplicate deliveries from Crontech (retry, at-least-once bus, manual replay). Records (eventId, eventType, source, receivedAt, payload) with a UNIQUE constraint on eventId and a supporting index. Drizzle schema lives in a NEW module `src/db/schema-events.ts` because `src/db/schema.ts` is in BUILD_BIBLE.md §4 LOCKED BLOCKS — the repo's documented pattern is "new tables only via new migration", so the Drizzle schema for this new migration is added alongside rather than inside the locked aggregate schema file.
2 files changed+62−0b45db843a4a5ca264c9ac15215a67e9be724b3de
2 changed files+62−0
Addeddrizzle/0034_processed_events.sql+30−0View fileUnifiedSplit
@@ -0,0 +1,30 @@
1-- Signal Bus P1 — Idempotency table for inbound events.
2--
3-- Records every external event we have successfully processed so that
4-- duplicate deliveries (Crontech retry, network replay, at-least-once bus)
5-- can be detected and short-circuited without firing side-effects twice.
6--
7-- `event_id` is the provider-supplied uuid v4 carried on the wire.
8-- `source` namespaces event_id in case two providers happen to collide
9-- (e.g. 'crontech', 'gatetest', 'github').
10--
11-- This migration is additive and reversible — drop the table to remove it.
12
13CREATE TABLE IF NOT EXISTS "processed_events" (
14 "id" uuid PRIMARY KEY DEFAULT gen_random_uuid(),
15 "event_id" text NOT NULL UNIQUE,
16 "event_type" text NOT NULL,
17 "source" text NOT NULL,
18 "received_at" timestamp NOT NULL DEFAULT now(),
19 "payload" jsonb
20);
21
22--> statement-breakpoint
23
24CREATE INDEX IF NOT EXISTS "processed_events_event_id_idx"
25 ON "processed_events" ("event_id");
26
27--> statement-breakpoint
28
29CREATE INDEX IF NOT EXISTS "processed_events_source_type_idx"
30 ON "processed_events" ("source", "event_type");
Addedsrc/db/schema-events.ts+32−0View fileUnifiedSplit
@@ -0,0 +1,32 @@
1/**
2 * Signal Bus P1 — Drizzle schema for the inbound-event idempotency table.
3 *
4 * Defined in a SEPARATE module because `src/db/schema.ts` is listed in
5 * §4 LOCKED BLOCKS of BUILD_BIBLE.md and must not be edited. New tables are
6 * allowed "only via new migration"; this module supplies the matching Drizzle
7 * definitions that migration `drizzle/0034_processed_events.sql` creates at
8 * the SQL layer. Import `processedEvents` directly from this module.
9 *
10 * Columns mirror the SQL migration 1:1. Keep them in sync whenever the
11 * migration is superseded by a follow-up additive migration.
12 */
13
14import { index, jsonb, pgTable, text, timestamp, uuid } from "drizzle-orm/pg-core";
15
16export const processedEvents = pgTable(
17 "processed_events",
18 {
19 id: uuid("id").primaryKey().defaultRandom(),
20 eventId: text("event_id").notNull().unique(),
21 eventType: text("event_type").notNull(),
22 source: text("source").notNull(),
23 receivedAt: timestamp("received_at").defaultNow().notNull(),
24 // Raw payload retained for forensics / replay. jsonb column — callers
25 // pass a plain object and drizzle serialises it.
26 payload: jsonb("payload"),
27 },
28 (table) => [
29 index("processed_events_event_id_idx").on(table.eventId),
30 index("processed_events_source_type_idx").on(table.source, table.eventType),
31 ]
32);
033