Add multi_entry_summary column to bulk_address_uploads

Additive nullable jsonb column for multi-entry building-part detection
(ADR-0004), generated off main. No data migration. Co-located the jsonb
shape type with the column so the schema is self-contained.

The sub_task.service column from the related feature work is already on
main, so this migration adds only multi_entry_summary.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Jun-te Kim 2026-06-02 12:28:36 +00:00
parent 546316eda9
commit deb70a98a7
4 changed files with 10172 additions and 0 deletions

View file

@ -0,0 +1 @@
ALTER TABLE "bulk_address_uploads" ADD COLUMN "multi_entry_summary" jsonb;

File diff suppressed because it is too large Load diff

View file

@ -1520,6 +1520,13 @@
"when": 1779992128370,
"tag": "0216_add_subtask_service",
"breakpoints": true
},
{
"idx": 217,
"version": "7",
"when": 1780403254457,
"tag": "0217_rare_cerise",
"breakpoints": true
}
]
}

View file

@ -1,6 +1,30 @@
import { pgTable, uuid, text, timestamp, jsonb } from "drizzle-orm/pg-core";
import { sql } from "drizzle-orm";
// Shape of the multi_entry_summary jsonb (ADR-0004). Co-located with the column
// so the schema is self-contained; the detection logic in
// src/lib/bulkUpload/multiEntry.ts imports these.
export interface MultiEntryEntry {
raw: string;
description: string;
}
export interface MultiEntryColumn {
field: string;
header: string;
entries: MultiEntryEntry[];
}
export interface MultiEntrySample {
address: string;
count: number;
columns: MultiEntryColumn[];
}
export interface MultiEntrySummary {
multiValuedFields: string[];
countDistribution: Record<string, number>;
largestCount: number;
sample: MultiEntrySample | null;
}
export const bulkAddressUploads = pgTable("bulk_address_uploads", {
id: uuid("id").defaultRandom().primaryKey(),
portfolioId: text("portfolio_id").notNull(),
@ -11,6 +35,9 @@ export const bulkAddressUploads = pgTable("bulk_address_uploads", {
status: text("status").notNull().default("ready_for_processing"),
sourceHeaders: text("source_headers").array().notNull().default(sql`'{}'`),
columnMapping: jsonb("column_mapping").$type<Record<string, string>>(),
// Multi-entry building-part detection, computed at start-address-matching
// and read by the awaiting_review review surface (ADR-0004).
multiEntrySummary: jsonb("multi_entry_summary").$type<MultiEntrySummary>(),
taskId: uuid("task_id"),
combinedOutputS3Uri: text("combined_output_s3_uri"),
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),