Loop INV-1 · Inventory & purchasing · reports to Lerris

A quarter of our stock value is asleep on the shelves.

Every Monday it re-checks every item on our shelves, tells Lerris what newly went to sleep and what woke up, and keeps a liquidation shortlist ready — because AED 2.7M of stock turned out to be sitting unmoved.

Built & tested — pilotWave 1🕐 Weekly Monday 08:30⚙️ Hermes bizops (planned)
Part 1 — the plain-english view

Nobody could see the sleeping stock. Now someone checks weekly.

What it watches
  • Every item with stock on hand, against its last movement date
  • Buckets: never moved · idle 6–12 months · idle 12+ months
  • Week-over-week: items newly crossing into dead, items that revived
  • Top sleepers by value, grouped by product family
When & how it speaks

Monday 08:30 digest: new sleepers, revivals, top 20 by value — every item linking straight to its ERP record. A liquidation shortlist (discount / bundle / scrap suggestions) is drafted on request.

Why it exists

First verified run found AED ~989K unmoved 6+ months. Then the data-pipe fix revealed the warehouse copy had been incomplete — the honest figure is AED 2,685,152 across 1,430 items, ~25% of on-hand value. The number nobody could see got bigger when we could finally see it.

AED 2.69M
of stock unmoved for 6+ months — revised up from 989K once the data pipe was fixed and complete
1,430
items asleep — roughly 25% of everything on our shelves by value
Top 20
sleepers by value in every digest, each one a click away from its ERP record

Monday 08:30

After the weekend's data sync

👀

Reads the warehouse

Stock on hand × last transaction date per item

📸

Snapshots & diffs

New sleepers and revivals vs last week

💬

Digest to Lerris

Buckets, deltas, top sleepers — all clickable

Shortlist on request

Liquidation ideas drafted; humans decide

What it will never doNever discounts, moves, or writes off anything — liquidation ideas are suggestions for humans to act on.
Where it stands today · July 2026Built and live-tested on the repaired, complete warehouse. Digest states its honest caveats in every issue: “movement” is based on order-line history (fulfilments aren't in the warehouse copy), so it's directionally strong rather than audit-grade.
Part 2 — under the hood

How it's wired: systems, models, and the path a number takes.

For implementers and the technically curious. The full build sheet — verified queries, thresholds, and build notes — lives on the specs page.

ReadsNetSuite warehouse
Stock on hand, item costs, transaction lines
Stepinv1_deadstock.py
The verified dead-stock query + bucketing
Stateinv1 snapshots
Weekly history in the loops schema → new-sleeper / revival diffs
StepEmail renderer
Tables, item links, Metabase drill-downs per bucket
DeliversMonday digest
To Lerris; a feed-card version follows
Human gateLiquidation shortlist
Drafted on request — approve, edit, or ignore
System we read Automated step State / memory Human decision
SystemRole in this loop
NetSuite warehousePostgres on our serverOur nightly read-only copy of NetSuite (ERP) data — items, stock, transactions — refreshed by the ETL sync every morning. The loop queries this copy, never NetSuite itself.
Metabasebi.huxapps.comThe BI layer over the warehouse. The loop runs its SQL through Metabase's API, and every number in the digest links to a live, clickable Metabase list so you can drill into the exact records.
Postgres “loops” schemareporting databaseA small dedicated schema where loops keep snapshots and history that need SQL (week-over-week diffs, mirrors for drill-down links). Scoped role; Metabase can read it for the clickable aggregate links.
Hermesagent runtime on our serverThe scheduler that wakes the loop up. Each loop is a cron job under a Hermes profile; the planned bizops profile will host the business digests (IT loops run under vpsops).
Healthcheckshealthchecks.huxapps.comThe dead-man's switch. The loop pings it only after a clean run — if the loop dies or errors, the ping stops and Healthchecks raises the alarm independently. This is how 'never silent' is enforced by machinery, not promises.
Email renderer + gwsrender_email.pyAll digests pass through one shared renderer: Huxberry-branded HTML, tables for repeated rows, a coral 'needs your response' box when the loop has questions, and an arrow link on every record. Sent from the loops mailbox via the Google Workspace CLI.
Loop chassisloop_common.pyShared plumbing every loop reuses instead of reinventing: state files, run-over-run diffing, Metabase drill-down link building, quiet-on-green notify logic.
Model / brainWhat it does here
None at run timedeterministic scriptA normal cycle is a plain Python script — no AI tokens are spent unless a diagnosis or judgment step is actually needed. AI wrote the script; the script does the rounds.
GPT-5.5 via Codexthe bulk-work modelWrote and maintains the mechanical parts — SQL, diffing, digest assembly. Effectively free on our existing subscription, so routine cycles cost almost nothing.
Claude Opusthe judgment modelReviews alert wording, thresholds, and anything a human will read and act on. Post-Fable, Opus owns everything that ships.
State & memory

Weekly snapshot per item in the loops schema — that's what turns a static report into a watchdog: “newly crossed into dead” only exists because last week is remembered.

Delivery

Monday email; every aggregate links to a live Metabase list and every item to its ERP record. Feed card (headline: dead-stock value; tiles: item count, % of stock, WoW delta) planned.

Safety rails

Read-only warehouse credential. Every digest carries a data-freshness stamp from DATA-0 and its honest-caveats footer.

Full build sheet →