Stock Health

Everything wrong with the stock, ranked by money, plus the three registers that gate the phases: negative balances, unverified openings, and SKUs that cannot be measured at all. scored 29-07-2026 03:12 GST stock_health_scores · nightly · 18,204 keys

31 SKUs are structurally unmeasurable. The FB_, LP_ and JB_ composite prefixes have revenue attribution but no quantity decomposition in any engine: they sell, the money lands, no component ledger row is ever written. They are excluded from every average on this page. Left in, they would read as perfectly healthy. Build the recipes →
GATE 0 is amber. 4,118 balance keys across 5 stores are Shopify-seeded and unsigned. 0 keys are never_verified, which is the blocking class, so the gate can still close once the store managers sign. Signoff queue →
Health state
Window

Days-of-cover distribution

1,535 SKUssame 5 bands as the live pivot legend, on purpose
440330220110 0 days · stockout · 410 SKUs · AED 1.86M of lost cover 1 to 6 days · critical · 218 SKUs 7 to 29 days · watch · 317 SKUs 30 to 89 days · healthy · 268 SKUs 90 to 179 days · long but funded · 146 SKUs 180 days plus · surplus capital · 64 SKUs · AED 1.42M No velocity · cover is NULL · 181 SKUs · 42 of them are dead stock Unmeasurable · 31 SKUs · cannot compute cover, no component rows exist 628 SKUs under 7 days of cover
01-67-2930-8990-179180+no vel.unmeas.
Cover is NULL, never 999, when the 28-day rate cannot be computed. The two grey bars on the right are the honest bars: 212 SKUs where the number does not exist.

Aging buckets

by unit, not by SKU46,213 on-hand units
41% 27% 18% 10% 4%
0 to 30 days18,947u41%AED 3.94M
31 to 90 days12,478u27%AED 2.59M
91 to 180 days8,318u18%AED 1.73M
181 to 365 days4,621u10%AED 0.96M
over 365 days · write-off candidates1,849u4%AED 0.39M
Age is measured over units using FIFO receipt cohorts, not over SKUs. v2 aged a SKU from its last outbound movement, so a SKU with 599 units from 2024 and 1 unit from last week read as fresh. Here those 599 units sit in the 365+ bar where they belong.

Per-location health score

weighted by capital at riskscore = 100 minus weighted penalties · weights live in settings
LocationScoreSKUsStockoutCritical OverstockDeadNegativeUnverifiedAt risk AEDLast count
HQ_319 82 1,512148624128101.12M18-07 · full
HQ_325 88 41824963000.09M18-07 · full
MCC 54 88496486411,0460.31Mnever · seeded
YAS 57 81272385309680.24Mnever · seeded
AJM 59 74458313208860.19Mnever · seeded
CCZ 61 69044182107240.14Mnever · seeded
AJM 63 51231121103180.08Mnever · seeded
BAS 66 3411870001760.04Mnever · seeded
CONS · 5 44 206124149000.21M01-07 · partner
TRANSIT 71 940000000.03Mn/a
SUPPLIER_HOLD 38 2200014000.06Mn/a
DISCONTINUED 22 6100061100.11Mn/a
Every store below 70 is there for the same reason: it has never been physically counted, so a quarter of its balance keys are unverified. Score is not a judgement of the store, it is a measure of how much we know.

Top offenders

worst 5 by value
MIN-CNL-038AED 72.2KHome · dead · 412d idle · 2,245u · ↑ 5% WoW
MIN-APP-534AED 49.0KApparel · slow · 238d idle · 96 units · ↑ 18% WoW
LBA01AED 36.5KHandbags · overstock · 61d cover, 187d oldest cohort
LLW-SUN-702AED 28.4KAccessories · stockout at 4 stores while 560u sit in pipeline
JRY01AED 24.4KJewellery · 120u at CONS_FLOWARD, no partner sale in 90d

Improving

best 4
PP02AED 5.8KPerfume · RNI cleared 48u · ↓ 18% WoW
FYN01AED 14.4KJewellery · cover 19d and rising · ↓ 12% WoW
MSR31-08AED 18.7KWatches · aging cohort cleared · ↓ 8% WoW
CDW01AED 9.1KWatches · quarantine unit released after QC · ↓ 6% WoW

Negative balance register

3 keys · blocks GATE 1every row has an auto-opened drift case, none may be silently repaired
SKULocationStock typeQtybal_afterFirst negativeLikely causeDrift caseAgeAction
ANQ07BMCCSTOCK -1-129-07 09:22 Ghost exchange · refund with 0 line itemsDRF-01485h Open correction →
LLW-PRF-201HQ_319STOCK -3-328-07 18:52 Refund restock posted twice under two idempotency keysDRF-015119h Open correction →
ANQ04PDISCONTINUEDWRITTEN_OFF -2-214-07 11:03 Write-off exceeded the on-hand it was written off fromDRF-013915d Finance review →
i2 reached minus 140,753 sellable because nothing on any screen refused a negative. Here a negative is an event: it emits in the same transaction, opens a case, and holds the gate open until a compensating row closes it.

Unverified opening balances

4,118 keysGATE 0
LocationKeysUnitsSourceSeededSignerAction
MCC1,0463,506Shopify levels12-07 05:00store managerSchedule count →
YAS9683,241Shopify levels12-07 05:00store managerSchedule count →
AJM8862,489Shopify levels12-07 05:00store managerSchedule count →
CCZ7241,502Shopify levels12-07 05:00store managerSchedule count →
AJM318874Shopify levels12-07 05:00store managerSchedule count →
BAS176418Shopify levels12-07 05:00store managerSchedule count →
HQ_319033,914RFID mass-tag count18-07 22:40signed · A. Yousufdone
HQ_32503,108physical count18-07 22:40signed · A. Yousufdone
All locations0-never_verified-0 keysgate can close
Provenance decays. A key is counted only until its next movement, then it becomes derived_from_count. Nothing here is sticky, so the register cannot slowly become a lie.

Unmeasurable composites

31 SKUsrevenue attributed, zero component rows
SKUPrefixSold 90dRevenue AEDComponent rowsRecipeAction
FB_MSR31_SETFB_142248,6000noneBuild recipe →
FB_FYN_DUOFB_9686,4000noneBuild recipe →
FB_LBA_GIFTFB_61114,9000noneBuild recipe →
LP_ENGRAVE_MSRLP_5817,4000noneBuild recipe →
LP_ENGRAVE_JRYLP_4413,2000noneBuild recipe →
LP_MONOGRAM_LBALP_3115,5000noneBuild recipe →
JB_FYN_TRIOJB_2748,6000noneBuild recipe →
JB_ANQ_PAIRJB_1922,8000noneBuild recipe →
23 moremixed312401,3000noneSee all 31 →
Total790968,7000excluded from every average
790 units sold in 90 days and not one component deduction anywhere. These SKUs are struck out on this page rather than scored, because a health score computed over a SKU that cannot be decomposed is a fabricated number.

Slow movers and dead stock

184 slow42 deadslow = cover over 180d · dead = no computable velocity and 180d+ idle · the two are disjoint
SKUTitleCategoryLocationHealth UnitsDays idleCoverOldest cohortValue AEDWoWAction
MIN-CNL-038Soy candle · Rose 400gHomeHQ_319dead 1,640412-438d72,160↑ 5%Request write-off
MIN-APP-534Wool coat · Camel LApparelHQ_319slow 96238311296d49,020↑ 18%Discount plan
LBA01Alyazia handbag · Black & tanHandbagsCCZoverstock 7296204187d36,864↑ 2%Rebalance
JRY01Alia ring · Gold vermeilJewelleryCONS_FLOWARDdead 120204-211d37,200flatRecall from partner
MIN-APP-501Linen shirt · Ivory LApparelHQ_319slow 840187268241d26,040↑ 4%Discount plan
ANQ01PAmiira pendant · RoseJewelleryDISCONTINUEDdead 61396-402d24,644flatRequest write-off
MSK15-BLKStrap 20mm · BlackStrapsHQ_319slow 8862194158d3,344↓ 3%Watch
MIN-2159Bloom vase · LargeHomeHQ_319slow 6274188132d10,540flatRebalance
LC-STRAP-42LC strap 42mm · assortedStrapsSUPPLIER_HOLDdead 12088-96d6,120flatRTS-0022 ready to ship
FYN04-SFyne ear cuff · sample batchJewelleryHQ_319SLA breach 53-3d410newClassify, 80h in QC
LLW-BAG-062Lulwa evening clutch · MidnightHandbagsHQ_319slow 44122216176d18,480↑ 6%Push to Namshi
MIN-MRR-003Round mirror · 90cmHomeHQ_319dead 18288-311d11,880flatRequest write-off
PP02Amber leather parfum 100mlPerfumeHQ_319critical 60614d624↓ 12%48u in RNI, book invoice
LLW-JWL-301Lulwa crescent pendant · GoldJewelleryall storesstockout 0110-0↑ new280u in pipeline, ETA 04-08
MSR31-12Sereen · Gold / IvoryWatchesHQ_319critical 90628d1,962↑ 9%40u in production
Slow and dead are disjoint by definition (R-02.17): a SKU with velocity and too much cover is slow, a SKU with no computable velocity and 180 days of silence is dead. Nothing is both, so the two counts add up.

Unhealthy value · 90 days

AED 3.12Mstacked by class · this is the number to shrink
4.0M3.0M2.0M1.0M Dead stock · AED 0.84M today, was 0.72M 90 days ago Overstock · AED 1.42M today, was 1.08M Stockout opportunity cost · AED 0.86M today 29-07 · total unhealthy value AED 3.12M 01-05 · total unhealthy value AED 2.58M
01-0515-0501-0615-0601-0715-0729-07
Dead stock AED 0.84M Overstock AED 1.42M Stockout opportunity AED 0.86M Up AED 0.54M in 90 days. Overstock is the whole increase.

Known drift classes

carried over from INV1
Ghost exchange812SKUs 61
Cross-location fulfil1,205SKUs 288
Re-ship double deduct111SKUs 44
Exchange with balance11SKUs 9
Serial gap12keys
Reserved over stock2keys
Chain mismatch0keys
Unmeasurable composite31SKUs
These counts are the historical order totals from the INV1 audit, replayed into i3 as flags on stock_health_scores.flags. They lived in a document before and in no screen, which is why they were never worked.

Recommended actions

5 suggestionseach opens a draft in the owning module, none acts on its own
Build recipes for the 31 unmeasurable composites
790 units sold in 90 days with zero component deductions · AED 968.7K of revenue attributed against stock that never moved on paper. Every day this runs, the component SKUs read higher than they are.
Open recipe editorExport the 31
Schedule counts at the 6 unsigned stores
4,118 keys are Shopify-seeded. GATE 0 stays amber until each store manager signs. A blind cycle count of the top 200 SKUs per store clears 78% of the keys.
Create sessionsSignoff queue
Clear the AED 1.42M overstock tail
64 SKUs over 180 days of cover, up AED 0.34M in 90 days. Handbags and Home carry 71% of it. Last season's elasticity says 20% off clears about 60%.
Discount planRebalance instead
Ship RTS-0022 · 120 units to LC Straps
Staged in SUPPLIER_HOLD for 88 days. Supplier credit AED 6.1K is still within terms for another 12 days, after which it is a write-off.
Book pickupSupplier terms
Write off 61 discontinued units, AED 24.6K
ANQ01P and 3 siblings, over 396 days idle at DISCONTINUED, no sale since Jun 2025. Needs finance approval before any ledger row is written.
Request approvalExport list
Pull forward LLW-JWL-301 · stockout at every store
280 units sit in pipeline (200 IN_PRODUCTION, 80 GIT_INBOUND) while all 8 locations read zero. ETA 04-08. Air freight the GIT leg and the stockout ends 6 days earlier.
Open PI-2026-044Production board

How a health state is decided

Every state below is a rule over the projection, not a heuristic. If two rules could match, the earlier one wins, so the classes are disjoint and the counts add up.

unmeasurable
SKU prefix has revenue attribution but no quantity decomposition. Excluded from every denominator on this page.
negative
any key below zero. Blocks GATE 1, auto-opens a drift case.
stockout
available = 0 at a sellable location.
critical
cover under 7 days.
low
at or below the reorder buffer.
dead
on_hand over 0, 180 days idle, and cover is NULL.
slow
on_hand over 0 and cover over 180 days. Disjoint from dead.
overstock
cover over 180 days and value over AED 10K.
healthy
everything else, with a signed or counted provenance.

Score is 100 - 30(stockout) - 20(critical) - 15(negative) - 15(unverified) - 10(overstock) - 10(dead), clamped to 0..100 and averaged per location weighted by capital at risk. The weights live in settings so an argument about them is a config change, not a deploy.

Aging comes from FIFO receipt cohorts, so it measures units. Cover comes from the 28-day rate with out-of-stock days removed from the denominator, so a SKU that was unavailable for 20 of 28 days does not read as having infinite cover.

Scores not computed yet.

The nightly scorer runs at 03:00 Dubai and needs at least 28 days of inventory_balance_daily to compute velocity.

Scorer failed last night.

Showing the 28-07 scores. Nothing has been recomputed since.
Error ID i3-health-9d21

System health
dead no computable velocity, 180d+ idle slow has velocity, cover over 180d unmeasurable no decomposition exists, excluded from every average - cover is NULL, never 999 Aging by unit via FIFO cohorts · cover from the 28d rate, out-of-stock days removed J/K rows · Enter panel · click a bucket to filter · E export · ⌘K palette

Connected to

Live pivot → Classic table → Mix analysis → Drift → Opening stock → Stock count → Recipe editor → Ledger correction → Approvals → RTS → Damages → Consignment → Goods in transit → Production → 3-way reconcile → Reports →