Business operations: inventory, money and reusable AI actions¶
Status: implementation plan — authorized scope, queued work, 2026-09-17.
Angel's objective: one readable Grafana home shows inventory, inventory value, what can be built, sales, invoices owed to us and bills we owe. Employees such as Yara use natural-language instructions backed by their own permissions. Repeated operations become tested functions so every AI can resume from documented contracts instead of reinventing each task.
Architecture and reuse¶
Use the existing InvenTree APIs, Grafana, assistant, department agents, Kanboard and Obsidian vault. Avoid a new agent for every click. Keep a small department layer (Production, Purchasing, Sales, Finance, Engineering), with deterministic actions underneath. An MCP interface can expose the same functions later; it is an adapter, not the business logic or permission boundary.
Flow: signed-in employee → assistant extracts structured request → trusted action service resolves identity/permissions → validate and preview → execute authorized action → read back result and store receipt → Grafana reads authoritative records. Models never select their effective role or obtain an administrator token. Existing approvals in SOPs still apply; do not request an extra confirmation for every harmless read.
Every action contract contains input/output JSON schema, units/currency, required permissions, source record IDs, validation errors, duplicate/retry protection, before/after receipts, audit actor, applicable SOP and sandbox tests. Use a versioned operation ID and idempotency key for writes; after uncertain network outcomes, look up the prior outcome before retrying. Quantities and money use exact decimal arithmetic. Code and tests live with their owning application; this note links contracts rather than copying them.
Initial read actions: inventory availability/value, assembly capacity and shortages, supplier pricing, sales-order status. Initial write actions: receive against a PO, transfer stock, reasoned stock-count adjustment, create sales-order draft, create invoice draft only through the selected accounting system. Drafting, issuing and sending an invoice are separate permissions/actions. No automatic purchases, payment initiation or tax filing is implied.
Existing functionality verified before building¶
The existing assistant already has draft_invoice, customer/part lookup, purchasing drafts,
stock-location queries and Production build-shortage checks. draft_invoice currently
creates a sales-order draft; confirmation creates that order and prints an invoice document.
It is not a separate accounting invoice/payment register. Pending actions are tied to the
requesting email and expire after 15 minutes. Preserve and extend these functions.
Stock count/transfer phone tools currently require Production/Admin, while Yara resolves to Sales/Purchasing/Facilities. Historical ERP notes give her stock add/change permissions; verify the live ERP permissions before aligning the assistant. The action service chooses one department token per turn, with Sales taking precedence over Purchasing: test mixed-role users and resolve authority per operation so a role prompt cannot grant or suppress access. AI receiving against a PO and maximum build capacity still need implementation.
Evidence reviewed: ~/assistant/actions.py (draft/confirm), ~/assistant/app.py (role token
selection), ~/assistant/labels.py (stock-tool gate). Detailed bounded audit is retained
in the engineering-portal workspace under planning/business-operations/operations-plan-audit.md.
Grafana layout¶
| Area | Overview | Drill-down |
|---|---|---|
| Money | Verified cash balance with as-of date; invoices outstanding/overdue; supplier bills outstanding/due soon | Receivables/payables ageing, receipts, payments and reconciliation exceptions |
| Inventory | On-hand units, available units, known inventory value, valuation coverage | Part/bin/lot quantities, reserved stock, missing or estimated costs, unit and extended valuation |
| Production | Buildable Cow/Remote/Finger quantities and blocking parts | Requested quantity → shortage list, on-order supply/ETA separately, source BOM revision and release state |
| Sales | Orders and booked value YTD, monthly trend, open backlog, shipped value | Customer/product/month; cancellations and returns distinguished |
| Attention | Missing prices, overdue work, uncertain imports, unreconciled transactions | Direct links to ERP, accounting records and Kanboard |
Use a small number of large headline cards, monthly trends and short actionable tables, with drill-down detail. Keep quantity, money, currency and date visible. Show the last source update and unavailable data explicitly. Finance access follows department permissions; Yara does not automatically gain access to company bank statements because she can edit stock.
Current warning: Business/sales and System/home call shipped/completed sales-order line value “invoiced”. This is an order proxy, not a real invoice register. Correct that label before expanding finance metrics. Do not infer overdue invoices from order age, or bills owed from open POs. Missing finance data must display unavailable, not zero.
Work order and acceptance criteria¶
- Production / Engineering: inventory value and build capacity. Reuse stock and BOM APIs/model logic first. Calculate available stock after reservations and exclude quarantine, expired/unusable, customer-owned, consumed or in-build quantities as appropriate. Aggregate repeated component requirements before dividing. For a simple flat BOM, limiting capacity is the minimum integer floor of eligible available quantity divided by per-assembly quantity. Handle nested BOMs, variants, attrition and setup quantities explicitly or label unsupported cases. Independent capacity for Cow and Remote cannot be added because they share parts; a combined kit calculation reserves shared demand once. Keep material capacity distinct from manufacturing release readiness. Test reservations, shared parts, unknown costs, partial lots, zero stock and concurrent changes. Verify value against specific priced stock lots, never replacement quotes.
- Administration / Engineering: reusable actions and Yara pilot. Inventory existing assistant tools; wrap native APIs rather than browser automation. Start with read-only availability and a sandbox stock receipt/transfer. Prove an allowed Yara action succeeds, a forbidden action fails at the server, identity cannot be spoofed and retrying a receipt does not duplicate stock. Keep task-specific credentials outside model context. Audit the existing role-token model before trusting it as per-user authorization. A department prompt is not a security control.
- Finance: real invoice/bill overview. Model invoice number, issue/due date, currency, lines, credits, payments and outstanding balance in the chosen accounting source; tie each to ERP orders without treating them as identical. Separate issued, paid, outstanding and overdue. Reconcile partial payments/credit notes; split currencies unless an explicit rate policy exists. Existing Finance instructions preserve the CPA meeting before first invoice through the new system and CPA-informed ledger selection. Prepare mappings and sandbox fixtures now; do not silently choose a new accounting platform.
- Purchasing / Finance: supplier and Chase import pipeline. Private original files → source hash and account/statement period → parsed staging rows → duplicate detection and reconciliation → reviewed posting. Supplier orders establish quoted/ordered prices; receiving establishes stock lots; invoices establish billed amounts; bank entries establish money movement. Match them without counting purchases twice. Keep discounts, shipping, tax, tariffs, returns and credits separate, with an explicit allocation policy. Chase CSV plus corresponding PDF statements are useful: preserve beginning/ending balance and reconcile the period. Transfers, card payments and owner contributions are not automatically expenses. Do not publish raw statements, full account numbers or bank credentials in Notes/Grafana/Git.
Work one implementation worker at a time. Root scopes/reviews/tests; Haiku handles bounded inventories; Sonnet handles substantive work if actual allowance permits; a cheaper Codex worker is the fallback. Capacity checks precede substantial work; no spending to fill a quota and no automatic reset-credit use. Existing department agents remain the reusable briefs. Only queue the next few ready packages; Kanboard is the authoritative task register.
Claude audit permission incident¶
The failed Haiku run reported four Read-tool permission denials. The files themselves are readable by the current Linux user. This does not establish a Unix ownership or chmod problem. The new claude-report-audit launcher grants session-only Read rules for those exact files using Claude's absolute-path syntax, removes shell/write tools for that worker, keeps existing hooks/plugins/memory, and uses Haiku explicitly. It does not change global settings or disable permission checks.
Run claude-report-audit for a no-model local preflight; claude-report-audit --run performs one bounded audit and records denials, model and CLI-reported API-equivalent cost separately from subscription usage. No sudo or broad full-control installer is needed. Permission semantics: Claude Code permissions.
Memory and handoff¶
This note is the implementation plan; linked Kanboard tasks hold execution status. Existing SOPs own procedures; update or add a numbered SOP only when implementing a repeatable workflow. Existing Claude department prompts and memory stay in place. Every completed package records code commit, deployed URL, evidence, tests, limitations and the next restartable step. Files/receipts stay private where required; shared notes carry summaries and IDs.
The owner intends to provide Chase statements and LCSC/DigiKey purchasing records. Import and reconciliation wait for those files; capacity/action planning can proceed now. The Paul exemption correspondence hold remains unchanged.
Registered execution queue¶
All four tasks have Angel as the accountable owner under SOP-017, a 2026-10-17 default review date, deliverables and verification criteria. Worker roles describe the next bounded assignment; no unattended worker loop was started.
| Task | Department / worker | State |
|---|---|---|
| 240 | Production + Engineering: valuation and build capacity | Ready — first implementation |
| 241 | Administration + Engineering: deterministic actions and Yara authorization | Backlog |
| 242 | Finance: invoice/bill sources and overview | Backlog; existing CPA/ledger decisions retained |
| 243 | Finance + Purchasing: Chase/supplier import reconciliation | Backlog; awaiting source files |
One cheaper Codex worker audited the existing assistant read-only. Root independently inspected the draft/confirm functions, role selection and stock-tool gate. This turn delivers the plan, queue and audit launcher; the new dashboards and ERP actions above are not yet implemented.
Launcher verification: local prerequisite check passed; bounded Haiku run completed with
zero permission denials (artifact ~/.local/state/claude-report-audit/1789643146).
The first run hit its deliberately small API-equivalent budget because existing context/hooks
were large; no automatic retry occurred. A reviewed second run completed. CLI estimates were
USD0.1806854 and USD0.0641999 respectively, not measured subscription charges. The worker's
claim that shipped orders prove actual invoicing was rejected against source SQL. Successful
tool access is not proof of a correct analysis. Launcher keeps a modest per-run budget and
returns nonzero for denial, budget exhaustion or unexpected model.
Implementation checkpoint — September 17, inventory reporting¶
Task240 is in progress with one bounded Codex implementation worker and independent root review. A separate local Grafana preview points only to the sandbox database. Native stock source review confirmed that broad in-stock statuses include attention/damaged/returned; planning deliberately uses OK stock and excludes unavailable locations, expired lots and reservations. Build, sales and transfer reservations are all accounted for. Robot's existing BOM contains substitute parts, so primary-part capacity and full substitute-aware capacity must remain distinct.
Read-only live permission verification for task241 supersedes the historical group note: Yara has separate Sales and Purchasing groups, with stock/PO/SO add-change and build view, but no build add/change. No permissions were changed. Worker implementation must use the live permission set, not copy the historical combined-group description.
Reporting deployed¶
Inventory & Assembly Capacity (historical Grafana version) and
Reports Home (historical Grafana version) are live (configuration commit
09addae). Home now leads with business and operations, includes capacity/value summaries
and current-year ERP order counts. Shipped/completed order values are no longer called
actual invoices; accounting balances are explicitly unavailable until connected.
All 27 ERP-backed panels passed through sandbox Grafana and the production read-only query path. Desktop/mobile previews were checked. Angel and Yara can open the new inventory report through their existing identities. No stock, orders, payments or permissions changed.
Recorded held inventory currently contains 28 lots: USD19.00 recorded-cost subtotal, USD2,086.20 explicitly estimated subtotal, and 13 unpriced/non-USD lots. These are separate, partial categories, not a complete inventory value. The recorded stock supports zero complete Cow, Remote, Finger or three-board sets; required non-substitutable parts including bare PCBs are missing. Physical parts not yet entered into the ERP are not represented.
Positive substitute-aware maximum capacity remains deliberately uncalculated when alternate allocation is needed. Primary-only capacity is shown; overall zero can still be established when a fixed non-substitutable requirement cannot supply one assembly. Unsupported complex BOMs return unavailable with reasons. SOP-028 documents the planning rules.
Task241 has started in isolated branch sandbox-employee-actions: first correct per-operation
credential routing and role rechecks on confirmation. No new stock-write AI tool will be
exposed until its authorization path and individual-user permission limitations are resolved.
2026-09-17 — Employee action routing deployed¶
Task 241 first slice is live in assistant commit 3731348 (implementation 0ba5f47). Each Sales, Purchasing, Production and Engineering action family receives its authorized service credential; confirmation rechecks current registry roles and rejects unknown action types. Existing ownership and expiry checks remain. Ten focused regressions and all 45 local tests passed without model or ERP calls; service restart and health readback succeeded. Work used one gpt-5.6-sol worker at a time with independent orchestration review. No CLI cost was reported.
Remaining: department credentials do not enforce an employee's individual InvenTree permission overrides. Task 241 stays open; no stock receiving/count/transfer tools were added. SOP-010 v1.3 records this boundary. No new ERP records, stock movements, permission grants, emails or financial transactions were made for validation.
2026-09-17 — CSV/TXT intake upload fixed¶
Assistant commit afbce13 deployed: Intake now accepts CSV/TXT previous-order documents as well as existing photos/PDFs. Both picker and server validation updated, browser generic/Excel MIME variants supported only for text extensions, UTF-8/BOM UTF-16 decoded, binary rejected, originals retained. Review text bounded to 100k characters with explicit truncation marker; task excerpts remain shorter. Upload creates a review task, not a supplier-price or stock import. Choose Order acknowledgement / Supplier bill / Supplier quote, attach and add matching instructions. SOP-027 v1.1 published with usage steps. Mocked upload tests and independent mobile375px picker check passed; service healthy after restart. One gpt-5.6-sol worker; no CLI cost reported, no live upload test or ERP mutation.
2026-09-17 — Multi-file Intake deployed¶
Assistant 0112b57: document modes support cumulative multi-file/photo selection, removal, per-file results/task links, sequential 3.1s pacing, no fixed count cap, 25MB per file. Each file creates a task with snapshotted category and note (fixed previously omitted document note). Successful files excluded from retries; HTTP5xx/network/incomplete responses require checking register before manual retry. Task/idea/closeout remain explicitly single attachment; mode guard prevents silently losing queue. Browser mocked-upload suite independently passed including failures/retry and375px; existing text upload tests passed. SOP027v1.2 published. Static page loaded per request, no restart necessary; service healthy. One gpt-5.6-sol worker, no CLI cost reported. No live test documents created.
2026-09-17 — LCSC historical order reconciliation¶
Processed 16 uploaded CSVs: 9 unique documents, 47 lines and 42 SKUs. Removed 7 byte-identical duplicate files and attachments after verifying preserved original hashes. Duplicate tasks link to the originals. Listed line amounts total USD 4,474.57; all lines pass decimal cent-rounding checks.
Saved 20 exact-matched price observations against 18 ERP parts in lcsc_order_history metadata, including order ID, source hash and row, quantity, unit and extended USD prices, and task link. Sandbox import and unchanged rerun passed; every production record was read back. Current quotes, stock quantities/costs, receiving and payment status remain unchanged. Actual order dates are absent. Four manufacturer mismatches remain held (C918120, C97340, C118954, C55072776); 20 other SKUs remain unmatched.
Code: erp-config commit 0ac2258, importer cc08618. LCSC history (archived locally) is linked from BOM costs and shows only the matched subset: 20 rows, 18 parts, USD 1,116.47. The normalized 47-row CSV is attached to task 243. Private audit, deduplication receipts and import receipts: ~/.local/state/assistant/lcsc-orders/2026-09-17/. Task 243 remains open for unresolved matches and Chase files. SOP-027 v1.3 documents the process. Work used one gpt-5.6-sol worker at a time; no CLI cost was reported.
2026-09-17 — LCSC matching portal deployed¶
Match LCSC purchases is live and linked from Intake. Assistant commit b368624. The reviewed batch has 24 unresolved identities: 20 unmatched and 4 manufacturer conflicts, spanning 27 order rows. The page groups prices and source links by identity, searches live ERP IPN/name/MPN, and shows live manufacturer details. Administration explicitly confirms identity, reason and any manufacturer discrepancy. Purchasing and Engineering may inspect/search only. Matching preserves historical evidence; no stock, current quote, manufacturer or BOM qualification changes occur.
A private SQLite decision log records reviewer, reason, time and target. Prepared decisions precede ERP writes and bind retries to the same part. A global history check rejects source rows already mapped to another part. File locking serializes portal saves; metadata is read again before patch and after saving. InvenTree has no metadata compare-and-swap, so unrelated external metadata writers still have a narrow race window. Paths: assistant purchases.py, static/purchases-match.html, state ~/.local/state/assistant/lcsc-matches.sqlite (0600). Source batch path is configured by LCSC_AUDIT_PATH; new batches are not automatically ingested by this screen.
Validation: 11 matcher regressions, 45 existing unittest checks and an independent offline mobile browser search/select/acknowledge/save/filter test passed. Production service healthy; unauthenticated page/API requests return 401; source readback confirms 24 unresolved identities / 4 held conflicts. No real matches were saved during testing. SOP-027 v1.4 documents use. One gpt-5.6-sol worker handled implementation; no CLI cost reported. User decisions remain pending; task 243 stays open.
2026-09-17 — Verified LCSC alternatives applied¶
See lcsc-alternatives-2026-09-17 for all 24 dispositions and evidence. Applied Samsung capacitor EE-C-0195 as native substitute on Robot Cow/Remote, Nexperia BAT54S manufacturer option, and exact EBYTE supplier match. Three portal decisions saved; 21 identities remain unresolved. Grafana historical evidence now 23 rows / 21 parts / USD 1,283.84 with primary/alternative usage labels (erp-config aeea67b). SOP-008 v1.7 published. Primary BOM records, stock and current supplier price breaks unchanged by independent before/after comparison.
2026-09-20 — Inventory lookup that works on a phone (sandbox) — CANCELLED¶
Inventory lookup is on the sandbox assistant for review; not yet promoted to production. Assistant branch sandbox, commit e25c17f. Read-only: search parts, on-hand vs minimum, then allocated/available and stock by location per part, with a link out to the ERP record. Cards, never a wide table.
Why we built rather than adopted. InvenTree's Platform UI is explicitly not mobile-optimised upstream, and measured on the sandbox at 375px its Stock table lays out at 1691px (Parts 1192px) with 9px type behind a sidebar that eats half the width — unusable, and only a React UI plugin could relayout it. The official Flutter app was the other candidate: it needs an InvenTree username and password (against the "never a second password" rule) and a hole in Cloudflare Access, which was verified to 302 every request including /api/. So neither existing option fit; the exception to Hard Rule 1 is recorded here deliberately.
Shape. inventory.py (GET-only routes, INVENTORY_DEPARTMENTS = {production, admin} checked server-side on every route including the JSON ones), static/inventory.html, tests/test_inventory.py. The browser never talks to InvenTree — the existing ERPClient does, server-side — so the ERP API stays off the public internet and the existing Access gate is unchanged. The tile is granted to admin_work and production_work in ~/Notes/roles/departments.yml; the registry entry is in home.py and reuses the vendored package-search icon.
Verification. Playwright at 375px and 1440px: zero horizontal scroll, zero <table> elements, 14px minimum text, 16px inputs, Home link and the Light/Dark/Auto toggle on ee-theme, no JS errors, detail view opens. Full suite 385 passed; the two test_content_approval failures were confirmed pre-existing on a pristine checkout of the same commit. Live reads against the sandbox ERP returned 185 parts and resolved stock to Shop/Shelf A.
Revert. Additive only — the sole change to an existing file is two lines in app.py plus one tile entry in home.py. Restore point tagged pre-inventory-2026-09-20 on assistant main (c6974ef). Read-only routes mean a bad deploy cannot alter stock.
Gotchas. Light mode must use home.html's darker copper #a8682c and red #b8342a; brand copper #c8813f fails contrast on white. home.html's third theme button reads "System" where the internal-pages rule says "Auto" — the new page follows the rule, so the two now differ by one word. The dev bypass env var is ASSISTANT_DEV_EMAIL, not DEV_EMAIL.
Cancelled 2026-09-20 at Angel's call. He wanted the ERP we already run made phone-usable, not a second surface beside it; a follow-up attempt to restyle InvenTree itself was then cancelled too (see map/objects/containers/inventree). Never deployed to production. The tile grants were removed from departments.yml; the page survives unmerged on assistant branch sandbox (e25c17f).
Delegation: Codex gpt-6-astra wrote the three files to a written brief; this session reviewed, fixed the light-mode palette and the phone-keyboard autofocus, and added the tile. No CLI cost reported.