ReportingStore.lowStock is a bare scan of the inventory table:
selectFrom("inventory").select(["sku","on_hand"]).where("on_hand","<=",threshold)
(packages/store-postgres/src/kysely-reporting-store.ts:148-157). No join to product_commerce,
so there is no deleted_at filter and no product_kind filter. The default threshold is 5
(packages/domain/src/ports/settings-store.ts:39).
Why this is now worse
PR 1a establishes the invariant "a product with a SKU has an inventory row", seeding a row at 0
on both write paths. So every SKU'd product now appears in the low-stock report at 0 until it is
stocked, where previously only products that had been explicitly seeded appeared at all.
Two categories can never clear:
- Digital products. They hold no stock by design — nothing ever restocks them — so a digital
SKU sits in the low-stock report permanently. product_kind lives on product_commerce, which
this query never joins.
- Soft-deleted products.
softDelete sets deleted_at on product_commerce and retains the
row; the inventory row is untouched and keeps being reported.
The result is a report that a merchant learns to ignore, which is the failure mode a low-stock
alert can least afford.
Suggested direction
Join product_commerce and filter to deleted_at is null and product_kind = 'physical'. Needs
cases on both dialects and a decision on whether an inventory row with no matching
product_commerce row (possible after a SKU rename — see the SKU-rename stranding issue) should
be reported or hidden.
Deliberately not fixed in PR 1a: it is an [Adapters] + reporting-contract change, and 1a's
scope is the seeding invariant.
ReportingStore.lowStockis a bare scan of theinventorytable:(
packages/store-postgres/src/kysely-reporting-store.ts:148-157). No join toproduct_commerce,so there is no
deleted_atfilter and noproduct_kindfilter. The default threshold is5(
packages/domain/src/ports/settings-store.ts:39).Why this is now worse
PR 1a establishes the invariant "a product with a SKU has an inventory row", seeding a row at
0on both write paths. So every SKU'd product now appears in the low-stock report at 0 until it is
stocked, where previously only products that had been explicitly seeded appeared at all.
Two categories can never clear:
SKU sits in the low-stock report permanently.
product_kindlives onproduct_commerce, whichthis query never joins.
softDeletesetsdeleted_atonproduct_commerceand retains therow; the inventory row is untouched and keeps being reported.
The result is a report that a merchant learns to ignore, which is the failure mode a low-stock
alert can least afford.
Suggested direction
Join
product_commerceand filter todeleted_at is nullandproduct_kind = 'physical'. Needscases on both dialects and a decision on whether an inventory row with no matching
product_commercerow (possible after a SKU rename — see the SKU-rename stranding issue) shouldbe reported or hidden.
Deliberately not fixed in PR 1a: it is an
[Adapters]+ reporting-contract change, and 1a'sscope is the seeding invariant.