Damages

Every unit that stopped being sellable without being sold · four discovery contexts, one register · the 48h matching rule that stops one physical damage becoming two write-offs · the DISPLAY_DAMAGED / QUARANTINE / WRITTEN_OFF ladder · and the Finance gate that makes a write-off a ledger event with an approver attached rather than an edit

Pattern detected · 4 damages at MCC bin shelf-A3 in 41 hours, 3 of them ceramics, versus a 30-day baseline of 0.4 per bin per week. Cluster key loc:MCC:shelf-A3. This raised alert rule dmg-cluster-bin, which emitted alert.raise once and will not re-emit inside its throttle bucket.
6 write-off requests are waiting on Finance · AED 21,840 · oldest 4 days. Every one of those units is sitting in QUARANTINE: counted in on_hand and owned_qty, excluded from sellable. Two expire in 72 hours and will need re-requesting. An unsigned request is not a neutral state, it is frozen stock. Approval queue →

Intake by discovery context

Where the damage was found, which is not the same axis as which system told us. v2 welded a stock type, a discovery context and a reporting system into one five-tab strip; i3 keeps them apart because RFID reports damage from all four contexts and ops2 reports from three.

MTD 96 incidents · 141 units · AED 71.2K
2 intakes could not resolve a location this week and were rejected with 422 into the unresolved queue. Under ops2 today both would have silently become HQ_319: app.py:13565 sets loc_code = "HQ_319" before any lookup, and the QC bridge repeats the same fallback. A wrong site is worse than a missing one. Unresolved queue →

The disposition ladder

On confirm the quantity always moves into the holding pen. Nothing is decided at intake, because the person who finds a broken thing is rarely the person who decides what happens to it.

STOCK→QUARANTINE on confirm, always
QUARANTINE→STOCKrecover · cleaned and re-inspected · sellable again
QUARANTINE→DISPLAY_DAMAGEDmarkdown · floor-sellable at a discount, never webstore-eligible
QUARANTINE→QUARANTINErepair · custody unchanged, unit hops to in_repair, module 08 owns the ticket
QUARANTINE→RTS_PENDINGsupplier claim · module 06 owns the credit
QUARANTINE→WRITTEN_OFFFinance only · needs an approved request id on the ledger row
Two of five exits change the money, and only one of those is terminal. A markdown recovers part of the value and the unit stays real. A write-off destroys the value and the unit leaves the estate. That asymmetry is why one of them needs a signature and four of them do not.

Severity rubric

A grade is an assessment event, not a typed field. The current grade is a projection of the latest assessment, so re-grading leaves a trail. The grade then constrains which dispositions are legal: a total loss cannot be recovered to stock, and the API refuses it rather than the UI hiding it.

Cosmetic
Visible only on close inspection. Function untouched. Sells at full price with no disclosure.
all 5 exits legal
Minor
Visible at arm's length. Function untouched. Not full-price sellable.
recover blocked
Major
Function impaired or structural. Repairable in principle.
recover + markdown blocked
Total loss
Unrepairable, unsellable, unsafe or contaminated.
write-off or RTS only
Re-grading is normal. 11 of 96 incidents MTD were re-graded after a second inspection, 8 of them downward, which recovered AED 6,140 that a first-pass grade would have written off. The v2 detail page asked for a grade once, in a chip, with no history.
cosmetic 34minor 31major 22total loss 9

48-hour matching window

The rule the v2 PRD promised and the shipped i2 schema could not implement: matching on (unit_id OR carton_id) AND 48h against a damages table that has neither column. Here the key is generated and indexed, the matcher runs every 5 minutes, and its hit rate is published so a silent zero is distinguishable from a dead job.

matcher ran 14:39 · 4.2s hit rate 6.2% of intakes
NewerCandidateBasisGapScoreWhyDecision
DMG-…0729-014DMG-…0729-009 unit3.2h Same EPC E280 6894 0000 4021 8F3C. RFID handheld and the ops2 store report are the same broken vase. auto-merged
DMG-…0729-011DMG-…0728-041 carton19.4h Carton MC-4471, same SKU. Could be two units of six, could be one counted twice. A human decides.
DMG-…0729-008DMG-…0729-002 carton6.1h Carton MC-4468, GRN INF-2318, both flagged crushed corner at the same bay.
DMG-…0728-037DMG-…0728-030 sku+loc31.8h MST-CRM-42-BLK at MCC, both store discovery. Weak: MCC holds 6 of this SKU and two really can break.
DMG-…0727-022DMG-…0726-018 order line27.5h Order #MIN10482 line 2. CSD raised a customer claim, then the parcel arrived damaged and ops2 raised again. rejected · both real
DMG-…0726-014DMG-…0725-051 sku+loc46.9h Inside the window by 66 minutes. Marginal by construction, and the gap column says so rather than hiding it. rejected · distinct
DMG-…0724-009DMG-…0722-044 sku+loc52.1h Outside 48h. Not proposed. A human may still link these two manually, which records as a manual link, never as a match. not proposed
Only a unit-basis match may auto-merge. Carton and fuzzy matches always go to a person, because a carton holds many units and two units of the same SKU at the same location can genuinely both break. A rejected candidate is never deleted: the rejection is the evidence that somebody looked. 2 pending · 4 rejected 30d · 11 merged 30d
Write-off queue →

Damage register

reported → confirmed → triaged → pending approval → resolved · 23 open of 96 MTD · row click opens the incident
ReferenceStateContextSrcSKU / identity LocationQtyCategorySeverity DispositionValue AEDRecov. AgeEvid.Match
DMG-2026-0729-014 confirmed storerfid MIN-VAS-022 · …4021 8F3C MCC · shelf-A3 1surface_crackminor markdown 30% 180126 5h+1 merged 1
DMG-2026-0729-013 reported customer_returncsd MST-CRM-42-BLK HQ_319 · RETURNS-IN 1stitching_failmajor not set 1,5200 3h -
DMG-2026-0729-011 confirmed supplier_arrivaliwms JRY01 · MC-4471 HQ_319 · QC-bay 6impact_breakmajor rts_claim 9,3000 14h+5 1 pending
DMG-2026-0729-009 merged storeops2 MIN-VAS-022 MCC · shelf-A3 0surface_crackminor into …014 00 closed loser · frozen
DMG-2026-0729-008 reported supplier_arrivalgrn PP02 · MC-4468 HQ_319 · RECEIVING 4seal_brokenminor not set 1,5960 9h 1 pending
DMG-2026-0729-004 pending approval warehouseops2 CDW01 · …9917 B2E1 HQ_319 · A3-04 1mechanism_failtotal loss write_off · tier 1 1,4000 54h+2 -
DMG-2026-0728-041 confirmed supplier_arrivaliwms JRY01 · MC-4471 HQ_319 · QC-bay 2stone_loosemajor rts_claim 3,1003,100 28h+3 candidate
DMG-2026-0728-037 triaged storeops2 MST-CRM-42-BLK MCC · shelf-A3 1surface_scuffcosmetic recover_to_stock 1,5201,520 33h rejected
DMG-2026-0728-030 triaged storerfid MST-CRM-42-BLK · …7A18 C044 MCC · shelf-A3 1surface_scuffminor markdown 25% 1,5201,140 41h rejected
DMG-2026-0728-021 pending approval customer_returncsd LLW-BAG-047 HQ_319 · RETURNS-IN 1contaminationtotal loss write_off · tier 2 2,8900 46h+4 -
DMG-2026-0728-016 confirmed warehouseiwms FYN01 · MC-4452 HQ_325 8packaging_crushminor repair 3,3600 36h+6 -
DMG-2026-0727-022 resolved customer_returnops2 TNS01 HQ_319 1lens_scratchmajor written off 4700 closed rejected
DMG-2026-0726-018 resolved customer_returncsd TNS01 customer → HQ_319 1frame_crackmajor rts_claim 470470 closed rejected
DMG-2026-0726-014 resolved warehouseops2 MIN-CNL-019 HQ_319 · A1-11 3expirytotal loss written off · tier 0 6300 closed rejected
DMG-2026-0725-051 resolved storeops2 MIN-CNL-019 YAS 1surface_scuffcosmetic markdown 20% 210168 closed -
DMG-2026-0725-033 resolved supplier_arrivaliwms LBA01 · MC-4430 HQ_319 · QC-bay 5stitching_failmajor rts_claim 10,25010,250 closed+7 rejected
DMG-2026-0724-009 resolved storecsd JAE17 CCZ 1stone_looseminor repair → stock 420420 closed -
DMG-2026-0722-044 resolved warehouserfid JAE17 · …3C90 1188 HQ_319 · A2-07 1surface_scuffcosmetic recover_to_stock 420420 closed -
18 of 96 shown · sorted by age descending · keys J/K move · Enter open · C confirm · G grade · D disposition · M merge · W request write-off Value column is landed cost from cost_ledger layer L-2026-07, frozen at confirm. Recovered is markdown proceeds plus supplier credit plus insurance payout.

Damage rate by source · 12 weeks

Bars are incident counts per source. The line is the rate: damages per 1,000 units handled. A raw count rises with volume and a rate does not, which is why the count peak in w29 is not the worst week.

w18 · warehouse 5 w18 · store 4 w18 · customer return 7 w18 · damaged on arrival 2 w19 · warehouse 7 w19 · store 3 w19 · customer return 6 w19 · damaged on arrival 1 w20 · warehouse 5 w20 · store 4 w20 · customer return 8 w20 · damaged on arrival 2 w21 · warehouse 6 w21 · store 5 w21 · customer return 6 w21 · damaged on arrival 3 w22 · warehouse 8 w22 · store 6 w22 · customer return 5 w22 · damaged on arrival 2 w23 · warehouse 5 w23 · store 7 w23 · customer return 5 w23 · damaged on arrival 3 w24 · warehouse 6 w24 · store 8 w24 · customer return 4 w24 · damaged on arrival 4 w25 · warehouse 7 w25 · store 9 w25 · customer return 5 w25 · damaged on arrival 3 w26 · warehouse 8 w26 · store 10 w26 · customer return 6 w26 · damaged on arrival 4 w27 · warehouse 6 w27 · store 11 w27 · customer return 7 w27 · damaged on arrival 5 w28 · warehouse 7 w28 · store 13 · MCC shelf-A3 cluster begins w28 · customer return 6 w28 · damaged on arrival 4 w29 · warehouse 8 w29 · store 16 · cluster peak w29 · customer return 7 w29 · damaged on arrival 5 w18 · 1.4 per 1,000 handled w19 · 1.3 w20 · 1.5 w21 · 1.5 w22 · 1.6 w23 · 1.6 w24 · 1.7 w25 · 1.8 w26 · 1.9 w27 · 2.0 w28 · 2.1 w29 · 2.4 per 1,000 · highest rate in the window
w18w19w20w21w22w23w24w25w26w27w28w29
Warehouse Store Customer return Damaged on arrival Rate per 1,000 units handled

Damage rate by location

Normalised by throughput. HQ_319 handles roughly nine times the units MCC does, so a raw count would put it top and mislead. The bar is rate, the number in brackets is the raw count.

MCC · 6.1 per 1,000 · 14 incidents · shelf-A3 cluster AJM · 3.1 per 1,000 · 3 incidents YAS · 2.8 per 1,000 · 6 incidents CCZ · 2.2 per 1,000 · 4 incidents HQ_325 · 1.9 per 1,000 · 7 incidents HQ_319 · 1.4 per 1,000 · 24 incidents · highest count, lowest-but-one rate BAS · 0.8 per 1,000 · 1 incident MCC (14) AJM (3) YAS (6) CCZ (4) HQ_325 (7) HQ_319 (24) BAS (1) 6.13.12.8 2.21.91.40.8
0246
Denominator is units handled at the location in the period: receipts, dispatches, transfers in and out, and picks. Stated here because a shrinkage or damage percentage without a denominator is a number nobody can check, the same discipline ruling L9 imposes on days of cover.

Write-off authority chain

Finance owns write-off approval. Nobody else may move stock to WRITTEN_OFF, and that is a database constraint rather than a permission check, because a permission check has a bypass and a constraint does not.

Queue →
TierBand (landed cost)UnitsSigsWho signsTTLOpen
0< AED 250≤ 21 A named auto-approval rule, for a closed reason set: expired, confirmed lost under 2 units, consumable opened. The rule code is the approver identity and every tier-0 approval is sampled monthly by Finance. 168h0
1AED 250 to 2,500≤ 201 Finance, and never the requester. 168h4
2AED 2,500 to 25,000≤ 502 Finance plus ops_manager or admin. Two humans, two timestamps, two evidence digests. 168h2
3AED 25,000 and above> 502 Finance plus admin, and a mandatory operational incident with a written post-mortem. A loss this size is not a form, it is an event that needs explaining. 168h0
A request takes the highest tier any of its three axes selects. Fifty-one units of an AED 10 item is tier 3, not tier 1, because the unit count says something the value does not.
Anti-splitting. The rolling 30-day sum of requests for the same SKU by the same requester, including this one, is tested against the bands too. Ten separate AED 2,400 write-offs of the same SKU by the same person are one tier-2 decision, not ten tier-1 ones.
Requester is never a signer. Enforced by a trigger on write_off_approvals, not by hiding a button.
An unposted approval expires in 7 days. A stale signature cannot be spent on a later, larger loss.
Reversal is a new request. The original row and the original ledger row are never touched. Events in, never edits.
Pending signatures
RequestIncidentSKUQtyAEDTierBasisSignedExpires
WO-2026-0729-006DMG-…0728-021LLW-BAG-04712,8902value1 of 2 · AY 09:144d
WO-2026-0729-005DMG-…0729-004CDW0111,4001value0 of 12d
WO-2026-0728-011SHR-…0728-002MIN-CHR-00123,1802value1 of 2 · FZ 11:023d
WO-2026-0728-009DMG-…0727-014TNS0141,8802rolling_30d0 of 22d
WO-2026-0727-018DMG-…0726-031PP0262,3941value0 of 11d
WO-2026-0727-004SHR-…0726-001FYN01229,2402units1 of 2 · AY 16:401d
WO-2026-0728-009 is tier 2 on rolling_30d, not on its own value of AED 1,880. The same requester has raised AED 2,610 of TNS01 write-offs in the trailing 30 days, which puts the cumulative total over the tier-1 ceiling. This is the case the ops2 endpoint cannot even see, because it has no request concept and no approver.

Disposition mix · 30d

73 resolved incidents

Disposition of 73 resolved damage incidents in 30 days markdown · 25 of 73 · 34% recover to stock · 18 of 73 · 25% written off · 13 of 73 · 18% RTS claim · 10 of 73 · 14% repair · 7 of 73 · 9% Resolved 73
  • markdown25 · 34%
  • recover to stock18 · 25%
  • written off13 · 18%
  • RTS claim10 · 14%
  • repair7 · 9%
Only 18% ended in a write-off. The other 82% either kept the value or moved it to somebody who owed it. That ratio is the argument for the whole ladder: a system whose only damage outcome is a write-off destroys value it did not have to.

Damage is not shrinkage

A damaged unit is present and holds value. A shrunk unit is absent. Netting them into one loss number makes shrinkage invisible, because damage is loud - somebody reports it, there are photos - and shrinkage is silent, because nobody reports an absence.

Damage 30d
96u
AED 71.2K · present, graded, dispositioned
Shrinkage 30d
14u
AED 9.8K · absent, investigated, 6 still open
The shrinkage register lives beside this one, keyed on EPC where a tag exists, fed by cycle counts and by an EPC-decay detector. Its three resolutions are: found elsewhere, sold without a scan, or confirmed lost. Only the third can become a write-off, and it needs the same signature ladder.
found elsewhere 5sold unscanned 3confirmed lost 6
Decay findings are suppressed for any window covered by an open device_failure incident at that location. A dark gate manufactures absences, and treating them as shrinkage would poison the rate and burn an investigator's week.
3-way reconcile →

Root cause · top 6 · QTD

packaging fault24
display handling19
in-transit mishandling14
defective material11
bin instability9
storage humidity7
Every code here maps to an owning module in incident_root_causes, so a leaderboard is a work queue and not a wall chart. bin instability owns module 04, packaging fault owns module 06.
Incident register →

Supplier exposure · QTD

damage rate per SKU received · attribution decided by the PO incoterm, not by who noticed
SupplierPOIncotermSKUs recdSKUs damagedRateUnitsValue AEDClaimWindow
CIC JewelleryPO-2026-118DAP18527.8%1112,400RTS-0022open
VJ JewelsPO-2026-114FOB11327.3%78,150RTS-0020open
LC StrapsPO-2026-109DAP24312.5%62,880RTS-0019closing 4d
Atelier CeramicsPO-2026-102EXW3139.7%41,440carrierexpired
Nour LeatherworksPO-2026-097DAP4224.8%510,250RTS-0017credited
Emirates Candle CoPO-2026-093DAP1616.3%3630noneexpiry, not defect
Attribution is a rule, not a judgement. Under DAP the supplier bears transit risk and a crushed carton is their claim. Under EXW it passes at their door and the same carton is the carrier's, which is why Atelier Ceramics shows no supplier claim despite three damaged SKUs. The incoterm lives on the PO (module 06) and this page reads it. Suppliers →

Damage register

loading
KPI tiles and the four intake tiles keep their exact heights, so nothing reflows when the rows land.

No damage incidents in this window.

That is a real state and it is not automatically good news. Check that the four intake paths are actually delivering before celebrating: ops2 last pushed 14:22, CSD 13:58, IWMS 12:04, RFID 14:39, GRN 09:10. A silent register and a healthy operation look identical from here.

Sibling health Incident register

No matches.

4 filters are hiding all 23 open incidents.

source: grnseverity: total losslocation: BAShas EPC

Could not load the damage register.

The damage service timed out after 8 seconds. Nothing was changed. Evidence thumbnails are served from Tigris and may be throttled independently, so the register can be healthy while photos are not.

Error id dmg-4b71c2 · 29-07-2026 14:41:08 GST · no write was attempted
Service health

You can see damages but you cannot sign for them.

Your grant on app i3 is manager, which lets you raise, confirm, grade and disposition an incident, and raise a write-off request. It does not carry the finance_signoff feature, so the sign buttons are absent rather than disabled: a control you can press and be refused teaches nothing.

apps.i3 = {role: manager, features: [damage_raise, damage_disposition]} · needs feature finance_signoff
Request the featureWho can do what

Read-only for 25 minutes.

The nightly hash-chain verify is running. Intake still accepts and queues, because refusing a sibling's damage report would lose it. Confirmations, dispositions and write-off postings are held until 21:25 and then drain in order.

21:00 to 21:25 GST · job chain-verify-0729 · 4 intakes queued

GATE 7 is in shadow, day 3 of 7.

The register is complete and correct, and its numbers are not yet authoritative. Damage intake, matching and disposition all run and post; the write-off chain runs in shadow, so requests collect signatures and the posting step is held. Recovery rate and shrinkage rate are marked provisional rather than hidden, because a hidden number reads as a bug and a marked one reads as a schedule.

GATE-7 · day 3/7 · damage identity holds 3/3 days · 0 WRITTEN_OFF rows without an approval id · unlocks 02-08
Gate report

Connected to

Damage detail → Incident register → Write-off approvals → Compensating entries → Returns → Repairs → Return to supplier → GRN + QC → Suppliers → Cartons → Drift → RFID gates → 3-way reconcile → Stock count → Locations → Location detail → SKU detail → CSD events → IWMS events → Sibling events → Audit log → Reports →