PASS Modules\Production\tests\Feature\BatchLifecycleTest ✓ it Wave-B/A1: a received FG batch carries mfg_date + shelf-life ex… 13.04s ✓ it Wave-B/A1: no shelf life → FG batch has a mfg_date but NULL expi… 6.90s ✓ it Wave-B/A1: a QC-released order mints its FG batch RELEASED (QC r… 6.59s ✓ it Wave-B/A1: FEFO auto-allocation SKIPS a quarantine lot, picks th… 5.81s ✓ it Wave-B/A1: explicitly picking a quarantine lot is a 422 naming t… 4.41s ✓ it Wave-B/A1: an explicit mfg/expiry override on the receive input… 8.78s ✓ it Wave-B/A1: releaseBatchesForOrder flips quarantine FG batches →… 4.31s ✓ it Wave-B/A1: rejectBatchesForOrder flips quarantine FG batches → r… 4.08s ✓ it Wave-B/A1: released/quarantined scopes filter by lifecycle statu… 4.61s ✓ it Wave-B review: FEFO SKIPS an expired released lot, picks the in-… 5.60s ✓ it Wave-B review: explicitly picking an expired lot is a 422 4.44s PASS Modules\Production\tests\Feature\BatchRecallTest ✓ it Agent2: forward trace finds the shipped customer via batch_id 3.57s ✓ it Agent2: forward trace follows genealogy to a descendant lot ship… 3.28s ✓ it Agent2: the delivery-note write path stamps batch_id from batch_… 3.19s ✓ it Agent2: the REAL Sales delivery-note store endpoint stamps batch… 4.71s ✓ it Agent2: recall cascades Recalled to root + held descendants but… 3.52s ✓ it Agent2: a recalled lot is not allocatable 3.41s ✓ it Agent2: the affected report lists customers and lots 3.33s ✓ it Agent2: a batch can only have one open recall at a time 3.39s ✓ it Agent2: a foreign-company batch and recall are 404 4.96s ✓ it Agent2: the recall endpoints are gated by production.recall per… 10.07s PASS Modules\Production\tests\Feature\ConsignmentControllerTest ✓ it F1: POST receipts records the ledger and returns 201 with ledger… 3.26s ✓ it F1: POST receipts validates the body (missing lines → 422) 3.36s ✓ it F1: POST receipts rejects a foreign-company warehouse (tenant-sc… 6.32s ✓ it F1: POST receipts rejects a non-consignment warehouse (is_consig… 3.51s ✓ it F1: GET ledger returns the running balances for the company 3.20s ✓ it F1: GET ledger filters by customer_id 3.68s ✓ it F1: GET borrows lists the company borrows and GET borrows/{id} s… 4.31s ✓ it F1: GET borrows filters by status 7.71s ✓ it F1: POST settle (buy) settles the borrow and returns the settled… 6.29s ✓ it F1: POST settle (replenish) requires a warehouse and clears the… 5.28s ✓ it F1: POST borrows opens a borrow via the operator endpoint (DR WI… 6.31s ✓ it F1: POST settle rejects an invalid type → 422 4.21s ✓ it F1: a foreign-company borrow id 404s on show + settle 7.39s ✓ it F1: GET ledger only returns the acting company rows (tenancy) 6.33s ✓ it F1: a user without consignment.receive cannot POST receipts (403… 3.29s FAIL Modules\Production\tests\Feature\ConsignmentFoundationTest ✓ it receipt records the ledger, creates a consignment batch with exp… 3.58s ✓ it borrow posts DR WIP / CR Materials Due to Customer at qty × decl… 4.30s ✓ it borrow requires a warehouse and cannot exceed the available cons… 3.00s ✓ it settle-buy creates a PurchaseBill from the customer and nets the… 6.02s ⨯ it settle-buy under DIRECT grn_mode does NOT double-increase physic… 5.47s ✓ it settle-buy for a NON-inventory product credits the Expense accou… 7.11s ✓ it settle-replenish clears the liability against Inventory, records… 5.70s ✓ it keeps the ledger balance invariant across receive/issue/borrow/s… 5.19s ✓ it is idempotent: re-settling an already settled borrow does not do… 6.21s ✓ it is idempotent: a borrow JE keyed consignment_borrow:{id} is neve… 4.10s ✓ it scopes ledger + borrow + GL per company (tenancy) 9.49s PASS Modules\Production\tests\Feature\ConsignmentLotBorrowReturnTest ✓ it borrow draws down the CUSTOMER own lot (owner=customer) and flag… 4.39s ✓ it a consignment return decrements the customer lot, the physical s… 3.11s ✓ it a consignment return honours a specific lot_balance_id (manual p… 3.31s ✓ it rejects a return that exceeds the customer consigned balance (42… 3.38s PASS Modules\Production\tests\Feature\ConsignmentLotSeedTest ✓ a consignment receipt seeds a customer-owned, off-book lot balance 3.31s PASS Modules\Production\tests\Feature\ConsignmentReconcileTest ✓ book_vs_actual is the ledger balance minus the live customer on-han… 2.19s ✓ book_vs_actual is zero when the ledger balance matches physical on-… 2.09s ✓ the ledger resource exposes physical_on_hand and book_vs_actual 2.29s PASS Modules\Production\tests\Feature\ConsignmentReportTest ✓ it reports ledger balances per customer/product with values and a t… 4.13s ✓ it sums ledger totals across multiple customer/product slots 4.76s ✓ it reports open vs settled borrow counts/values and ages the open… 10.42s ✓ it buckets a borrow opened more than 90 days ago into 90_plus 4.19s ✓ it filters the report by customer_id, product_id and borrow status 4.45s ✓ it gates the report behind production.consignment.view 5.76s ✓ it scopes the report to the acting company (tenancy) 7.60s PASS Modules\Production\tests\Feature\ConsignmentReturnHttpTest ✓ it POST returns 200 and decrements the customer lot, stock, and led… 3.32s ✓ it POST returns 422 when returning more than the consigned balance 3.30s ✓ it a user without consignment.return cannot POST returns (403) 3.09s FAIL Modules\Production\tests\Feature\CostAiAnomalyVarianceTest ✓ it F1 anomaly: grounding carries the REAL alert + order cost-delta… 4.21s ✓ it F1 anomaly: only ONE LLM call is made and it carries the groundi… 4.30s ✓ it F1 anomaly: degrades gracefully (ai_not_configured) but still re… 4.52s ✓ it F1 anomaly: endpoint is gated by production.cost_ai.run 5.59s ⨯ it F1 variance: grounding carries the REAL decomposed variance legs… 6.93s ⨯ it F1 variance: the single LLM request embeds the real leg amounts… 4.27s ⨯ it F1 variance: endpoint runs over HTTP, perm-gated + 404s a foreig… 4.72s ✓ it F1 variance: an order with no variance records grounds an empty… 4.79s PASS Modules\Production\tests\Feature\CostAiFoundationTest ✓ it Foundation: complete() parses an Anthropic-shaped response (x-ap… 1.06s ✓ it Foundation: complete() parses an OpenAI-shaped response (bearer… 1.10s ✓ it Foundation: complete() returns ai_not_configured when disabled o… 0.08s ✓ it Foundation: complete() degrades to ai_not_configured (no 500) on… 0.08s ✓ it Foundation: analyze() persists an insight and echoes the groundi… 1.24s ✓ it Foundation: analyze() caches by grounding hash (no re-bill) unle… 1.21s ✓ it Foundation: analyze() degrades gracefully (ai_not_configured) bu… 1.11s ✓ it Foundation: analyze() tolerates a code-fenced JSON response 1.00s ✓ it Foundation: validateNumbers flags an invented number and passes… 0.09s ✓ it Foundation: validateNumbers honours the 1% relative tolerance an… 0.07s ✓ it Foundation: validateNumbers scans numbers inside NON-SCALAR reco… 0.07s ✓ it Foundation: validateNumbers scales magnitude suffixes + reads ra… 0.07s ✓ it Foundation: a numeric IDENTIFIER string in grounding does not wh… 0.07s ✓ it Foundation: insights list is company-scoped + filterable by feat… 1.23s ✓ it Foundation: insight show returns the verbatim grounding + AI blo… 1.39s ✓ it Foundation: insights endpoints are gated by production.cost_ai.v… 2.23s PASS Modules\Production\tests\Feature\CostAiMarginOptimizationTest ✓ it MarginOptimization: grounds verbatim on CVP + margin + customer… 1.17s ✓ it MarginOptimization: validator flags a hallucinated margin number 1.11s ✓ it MarginOptimization: nulls in CVP (unviable break-even) survive i… 1.10s ✓ it MarginOptimization: degrades gracefully when AI is not configure… 1.10s ✓ it MarginOptimization: GET analyze endpoint returns the grounded in… 1.21s ✓ it MarginOptimization: GET index reads the latest stored insight wi… 1.18s ✓ it MarginOptimization: index returns null when no insight exists ye… 1.09s ✓ it MarginOptimization: endpoints honour the run/view permission spl… 1.23s PASS Modules\Production\tests\Feature\CostAiPriceScrapTest ✓ it PriceDemand: grounds the suggested price + margin + supplier pri… 2.00s ✓ it PriceDemand: endpoint returns the deterministic data block (perm… 2.47s ✓ it PriceDemand: 404s an unknown product or one with no rolled stand… 1.12s ✓ it PriceDemand: a foreign product is not reachable (tenant-scoped 4… 1.11s ✓ it PriceDemand: endpoint is gated by production.cost_ai.run 2.30s ✓ it PriceDemand: degrades gracefully (ai_not_configured) but still g… 2.20s ✓ it PriceDemand: flags an AI number that is not in the grounding 2.28s ✓ it ScrapPrediction: grounds the scrap series + monthly trend and pe… 1.20s ✓ it ScrapPrediction: endpoint returns the qualitative risk band over… 1.30s ✓ it ScrapPrediction: endpoint is gated by production.cost_ai.run 1.01s ✓ it ScrapPrediction: degrades gracefully (ai_not_configured) but sti… 1.43s ✓ it ScrapPrediction: flags an AI number absent from the grounding (n… 2.48s ✓ it ScrapPrediction: reuse cache returns the same insight; fresh for… 2.89s PASS Modules\Production\tests\Feature\CostAiSupplierRiskTest ✓ it SupplierRisk: grounds verbatim on the deterministic scorecard su… 0.99s ✓ it SupplierRisk: validator flags a hallucinated supplier number 1.20s ✓ it SupplierRisk: degrades gracefully when AI is not configured (sti… 1.00s ✓ it SupplierRisk: GET analyze endpoint returns the grounded insight 1.22s ✓ it SupplierRisk: GET index reads the latest stored insight without… 1.52s ✓ it SupplierRisk: index returns null when no insight exists yet 1.17s ✓ it SupplierRisk: insight does not leak across tenants 2.30s ✓ it SupplierRisk: endpoints honour the run/view permission split 1.43s PASS Modules\Production\tests\Feature\LaborCostVarianceTest ✓ BE: department cost center is stamped onto posted payroll JE lines 3.47s ✓ BE: unmapped employee department leaves payroll JE cost_center null… 3.62s ✓ BE: labor-cost-variance = actual (payroll) − applied (production_la… 6.67s ✓ BE: favorable variance when actual is below applied 4.50s ✓ BE: labor-cost-variance is company-scoped — foreign company sees no… 5.78s ✓ BE: labor-cost-variance endpoint is gated by production.labor_varia… 3.31s PASS Modules\Production\tests\Feature\ManualIssueAgainstOrderTest ✓ it WP1: approving a MANUALLY-raised issue against a production orde… 5.71s ✓ it WP1: the same manual issue with the approval setting ON applies… 5.80s ✓ it WP1: the inline production-screen path with the approval setting… 6.58s ✓ it WP1: the gated production-screen path (setting ON, keeper approv… 7.73s ✓ it WP1: $applyingInline still keeps the listener out of an IN-FLIGH… 4.54s ✓ it WP1: re-firing InventoryIssueApproved for an already-applied man… 5.45s ✓ it WP1: approving a manual issue against an order that can no longe… 5.18s ✓ it WP1: a manual issue whose reference_id points at ANOTHER compan… 14.02s ✓ it WP1: a NON-production issue is completely untouched 7.20s ✓ it WP1 FIX1: a MANUAL issue entered in a NON-BASE unit posts base-u… 9.39s ✓ it WP1 FIX1 / WP1b: the INLINE production-screen path with a NON-B… 11.69s ✓ it WP1 FIX1: with a conversion factor of 1.0 the effects and the st… 7.35s ✓ it WP1 FIX4: a MULTI-LINE manual issue pairs every line with its OW… 7.27s ✓ it WP1: an automatic BACKFLUSH posts exactly ONE mfg issue — the ap… 5.99s ✓ it WP1: a keeper-TRIMMED issued_quantity is what the effects are ke… 8.49s ✓ it WP1: a CONSIGNMENT line on the MANUAL path keeps its per-item co… 8.72s ✓ it WP1 FIX2: POST /api/inventory/issues rejects a production_order… 6.89s ✓ it WP1 FIX2: POST /api/inventory/issues rejects a production_order… 16.79s ✓ it WP1 FIX2: POST /api/inventory/issues accepts an in-company produ… 6.73s ✓ it WP1 FIX2: PUT /api/inventory/issues/{id} cannot re-point a draft… 8.41s ✓ it WP1b: issuing 1 box against a 2-BOX plan releases 1 — it does no… 5.77s ✓ it WP1b: on the INLINE path the entered unit IS the plan unit, so t… 5.50s ✓ it WP1b: a plan already in the product BASE unit is completely unto… 5.30s ✓ it WP1b: the OVER-ISSUE gate measures the request in the PLAN unit… 4.39s ✓ it WP1b: CANCELLING a non-base-unit issue restores the plan EXACTLY… 6.90s ✓ it WP1b M1: batch GENEALOGY is held in the BASE unit, matching the… 6.40s ✓ it WP1b M1: an EXPLICIT batch pick is scaled to base by the SAME fa… 7.83s ✓ it WP1b M2: a BACKFLUSH permit with two explicit lines for the SAME… 5.48s ✓ it WP4 CASE 1: an order with NO material plan issues FREELY even wi… 5.59s ✓ it WP4 CASE 2: an uncovered over-issue is rejected and the message… 4.42s ✓ it WP4 CASE 2: the same over-issue passes when an APPROVED allowanc… 5.39s ✓ it WP4: a material that is NOT on the order's plan passes FREE — th… 6.42s ✓ it WP4: with production.over_issue_requires_approval OFF, an over-p… 5.56s ✓ it WP1: a production-TYPED issue with no reference_id applies nothi… 4.12s PASS Modules\Production\tests\Feature\MrpLowLevelCodeTest ✓ it C2: a component shared by two parents nets the SUM of both depen… 5.23s ✓ it C2: a 3-level deep make chain nets every level exactly once and… 5.57s ✓ it C2: a DIAMOND nets the deep shared leaf ONCE for the combined de… 6.29s PASS Modules\Production\tests\Feature\OrderCockpitTest ✓ it returns the WIP journal entry after issue-materials 8.70s ✓ it journal-entries is company-scoped (403 on a foreign order) 6.21s ✓ it flags a never-stocked make-component as missing with its supply_… 8.28s ✓ it flags a fully-stocked material as available / overall ready 6.70s ✓ it judges factory readiness on the own portion of a split material 6.81s ✓ it never flags a pure-customer material as a factory shortage 8.58s ✓ it treats legacy (0,0) toll-order materials as customer-supplied (o… 8.10s ✓ it still reports a factory shortfall on the own portion of a split… 5.22s ✓ it keeps a plain own material backward-compatible (own_required ==… 6.62s ✓ it returns the sales-order demand row and component/parent orders b… 5.46s