← back to Homesonspec
feat(specs): InventoryHome.specs Json foundation — schema + migration + publisher wiring
26f91560bd0b8a1990efade94de45981865184d9 · 2026-08-02 20:32:04 -0700 · Steve Abrams
Adds nullable JSONB specs column (extensible grab-bag) + staged migration
(NOT applied to prod) + publisher persist from a reserved 'specs' payload key
(create + update, specless-extract-never-clobbers guard). The moment any
extractor emits a specs payload field it flows to the DB. Publisher typechecks
clean. One 'prisma migrate deploy' from live — Steve-gated.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Files touched
A packages/database/prisma/migrations/20260802203133_add_inventory_specs/migration.sqlM packages/database/prisma/schema.prismaM packages/publisher/src/index.ts
Diff
commit 26f91560bd0b8a1990efade94de45981865184d9
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Sun Aug 2 20:32:04 2026 -0700
feat(specs): InventoryHome.specs Json foundation — schema + migration + publisher wiring
Adds nullable JSONB specs column (extensible grab-bag) + staged migration
(NOT applied to prod) + publisher persist from a reserved 'specs' payload key
(create + update, specless-extract-never-clobbers guard). The moment any
extractor emits a specs payload field it flows to the DB. Publisher typechecks
clean. One 'prisma migrate deploy' from live — Steve-gated.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---
.../migrations/20260802203133_add_inventory_specs/migration.sql | 3 +++
packages/database/prisma/schema.prisma | 4 ++++
packages/publisher/src/index.ts | 8 ++++++++
3 files changed, 15 insertions(+)
diff --git a/packages/database/prisma/migrations/20260802203133_add_inventory_specs/migration.sql b/packages/database/prisma/migrations/20260802203133_add_inventory_specs/migration.sql
new file mode 100644
index 00000000..74954de1
--- /dev/null
+++ b/packages/database/prisma/migrations/20260802203133_add_inventory_specs/migration.sql
@@ -0,0 +1,3 @@
+-- Add extensible spec grab-bag to InventoryHome (see collectors/SPECS-AUDIT.md).
+-- Nullable JSONB, no default, no backfill — zero-risk additive change.
+ALTER TABLE "InventoryHome" ADD COLUMN "specs" JSONB;
diff --git a/packages/database/prisma/schema.prisma b/packages/database/prisma/schema.prisma
index ed4e9cd0..d9255fc1 100644
--- a/packages/database/prisma/schema.prisma
+++ b/packages/database/prisma/schema.prisma
@@ -200,6 +200,10 @@ model InventoryHome {
constructionStatus ConstructionStatus @default(UNDER_CONSTRUCTION)
estCompletionDate DateTime?
availabilityStatus String?
+ specs Json? // extensible grab-bag of every extra spec a builder exposes
+ // (monthly payment, was-price, plan code, multi-gen, lifecycle
+ // flags, incentives, energy, virtual tour, …). Core filter/sort
+ // fields stay as typed columns above; this holds the long tail.
status PublishStatus @default(PUBLISHED)
freshness Freshness @default(FRESH)
verificationLabel VerificationLabel
diff --git a/packages/publisher/src/index.ts b/packages/publisher/src/index.ts
index 7faa196a..89b9a1b5 100644
--- a/packages/publisher/src/index.ts
+++ b/packages/publisher/src/index.ts
@@ -151,6 +151,9 @@ export async function publishStagedRecord(stagedRecordId: string) {
: null,
status: "PUBLISHED",
freshness: "FRESH",
+ // Long-tail specs: extractors emit a reserved "specs" payload field (an object
+ // of everything the builder exposes beyond the core columns). Persisted as-is.
+ specs: (value<Record<string, unknown>>("specs") as never) ?? undefined,
verificationLabel: label,
lastVerifiedAt: new Date(),
sourceId: staged.sourceId,
@@ -169,6 +172,11 @@ export async function publishStagedRecord(stagedRecordId: string) {
// coordless extract never clobbers a good pin. [yolo iter-4 contrarian fix]
...(value<number>("lat") != null ? { lat: value<number>("lat"), lon: value<number>("lon") } : {}),
images: value<string[]>("images") ?? undefined,
+ // Only overwrite specs when the new extract HAS them — a later specless
+ // extract never clobbers a good spec map (same guard as lat/lon above).
+ ...(value<Record<string, unknown>>("specs") != null
+ ? { specs: value<Record<string, unknown>>("specs") as never }
+ : {}),
constructionStatus: (value<string>("constructionStatus") as never) ?? undefined,
estCompletionDate: value<string>("estCompletionDate")
? new Date(value<string>("estCompletionDate")!)
← dad73003 docs(specs): audit pass-2 — 14 zeros are unparsed blob shape
·
back to Homesonspec
·
feat(specs): dr-horton capture proving slice — extract->vali 7c3609cc →