# Phase 2 correction report

**Status:** Correction pass complete. Phase 3 has not started.  
**Head at report time:** `main`. Implementation `3dc8862`. Sample/lifecycle correction `79f2835`. Statutory UI / mobile cards `b660f93`, plus the follow-up commit that lands this report.

This report is the Phase 2 revalidation package. It does not rewrite git history.

---

## 1. Corrected Vehicle / Manufacturer / Brand / Supplier matrix

Live development seed and PHPUnit seed now match the approved identities.

| Registration | Brand/Model | Manufacturer | Current open supplier |
|---|---|---|---|
| `MH12AB1234` | Signa | Tata Motors | Western Cement Suppliers |
| `MH14XY5678` | Dost | Ashok Leyland | Reliable Aggregates |

Evidence:

- Both Vehicle rows exist; both Manufacturers exist; Signa is under Tata Motors; Dost is under Ashok Leyland.
- Each Vehicle has **exactly one** open supplier assignment (`assigned_until_utc IS NULL`). Assignment history is preserved (history table is append-only; change-supplier closes the prior row).
- Registration numbers are stored uppercase with no spaces.
- `LPT` is not seeded. No authoritative BRD/TDD/FRIS/CSV row in this repository requires LPT in addition to the approved dataset.
- `Tata` as a manufacturer name (without “Motors”) is absent.
- VEH-SCR-001 is GET-only and shows **Western Cement Suppliers** and **Reliable Aggregates**. There is no Add/create control.
- VEH-SCR-002 shows Tata Motors and Ashok Leyland.
- VEH-SCR-003 shows Signa under Tata Motors and Dost under Ashok Leyland.
- VEH-SCR-004 `/vehicles/1` and `/vehicles/2` display the two identities above.

Tests: `AuthoritativeSampleDataTest::testApprovedVehicleManufacturerBrandSupplierMatrix`, `ApprovedSampleScreensTest::testVehicleScreensShowApprovedVendorsManufacturersBrandsAndIdentities`.

---

## 2. Full authoritative sample-data comparison

Mechanical comparison of development seed vs the approved dataset. Fabricated substitutes were removed, not retained.

| Domain | Approved | Seed / UI | Result |
|---|---|---|---|
| Companies | Apex RMC Pvt Ltd — 27AAACA1234A1Z5; Apex Aggregates Pvt Ltd — 27AAACA5678B1Z9 | Same | Match |
| Plants | Chakan Plant (shared), Talegaon Crusher Plant, Ranjangaon RMC Plant | Same; Chakan has two active `company_plant` rows | Match |
| Clients | Beta Constructions 27AAAAA0000A1Z5 Pune; Metro Infra 27AACCM8765D1Z7 Pune; Sunrise Developers 27AABCS4321C1Z2 Mumbai; Delta Supplies 27BBBBB1111B1Z3 Nashik | Same four; no extra clients | Match |
| Client sites | Hinjawadi Phase 2 — Beta; Wakad Tower Site — Metro Infra; Baner Road Project — Sunrise | Same; Delta has no site | Match |
| Vendors | Western Cement Suppliers; Reliable Aggregates | Same two; Sahyadri Cements removed | Match |
| Vehicles | Matrix in §1 | Matrix in §1; LPT removed | Match |
| Raw materials | OPC 53 Cement MT; 20 mm Aggregate MT; M-Sand MT | Same three | Match |
| Products | M25 RMC MT; M30 RMC MT; Crushed Sand MT | Same three | Match |
| Recipe | OPC 53 Cement 0.350; 20 mm Aggregate 0.700; M-Sand 0.650 MT | Seeded on **Beta Constructions + M30 RMC** with those quantities | Match |
| Absent substitutes | Horizon Builders, Sahyadri Cements, Pune Manufactured Sand, Chakan Tower Site, LPT | `AuthoritativeSampleDataTest::testFabricatedSubstitutesAreAbsent` | Match |

Phase 1 operational numbers were not rewritten. Transaction lookups now use `CL-BETA` / `SITE-HINJ` / `V-CEM-01` so FKs still point at the corrected Vehicle/Client rows.

---

## 3. Recipe Horizon — meaning, source, uniqueness

**What “Recipe Horizon + M30” meant:** it was a Phase 2 **plan placeholder** for a client identity paired with finished product M30, not a Recipe name, Company, Product, or scope. The only remaining mention is historical: `docs/PHASE2_PLAN.md` §5 notes that `client` was “renamed from the earlier Horizon placeholder” to Beta Constructions.

**Authoritative documents in this repository:**

- BRD, approved addendum, TDD, Database Architecture, and FRIS files are **not present** as source files here. They are cited only through the approved Phase 2 plan.
- Blueprint/Figma CSV (`uploads/RMC_Crusher_ERP_Phase1_Figma_Frame_Component_Manifest_v1.1_4e22.csv`) and the Figma prompt pack do not define a Recipe or Client named Horizon.
- Approved plan §12 and resolved conflict **R12**: one current recipe per **client + finished product**; overwrite lines for the same pair; `is_active` supports inactive view; no version table.

**Uniqueness (sources agree — no blocker):**

| Question | Answer |
|---|---|
| Scope | **Client + Finished Product**, implemented as `UNIQUE (client_company_id, finished_product_id)` |
| Not | Finished Product only; not Company + Finished Product as a pair of raw columns; not a Recipe name |
| DB constraint | `uq_recipe_scope` on `recipe` in `app/Database/Migrations/2026-09-12-200013_AddProductGstAndRecipe.php` |
| Plan | PHASE2_PLAN §5.2, §12, R12 |
| UI | RCP-SCR-001 shows **Client** and **Finished product** (e.g. Beta Constructions (Apex RMC Pvt Ltd) + M30 RMC). There is no recipe-name field |

Horizon was removed from seed, selectors, and screens. No Client/Recipe/Product/Company named Horizon was invented.

`client_company_id` already includes the company mapping, so two companies could each have a recipe for the same client+product if the client is mapped to both. That is the UNIQUE key, not `client_id` alone.

---

## 4. Lifecycle-aware deactivation matrix

Historical identity and deactivation are separate. Terminal history stays readable. A terminal reference does **not** keep a master Active forever, except where the approved **strict** rules say any reference blocks (Company–Plant, Unit).

Nonterminal challan statuses: `DRAFT`, `PENDING_APPROVAL`, `RETURNED_FOR_CORRECTION`, `APPROVED`. Terminal: `BILLED`, `CANCELLED_VOID`.  
Nonterminal invoice statuses: `DRAFT`, `PENDING_APPROVAL`, `RETURNED_FOR_CORRECTION`, `APPROVED_PRE_IRN`, `PENDING_IRN`, `IRN_FAILED`. Terminal: `IRN_GENERATED`, `CANCELLED`.

Normal Save/Edit remains available when only Deactivate is blocked.

| Entity | Blocking statuses / conditions | Tables queried | Company/plant scope | Locking | OV-12 | OV-13 |
|---|---|---|---|---|---|---|
| Company | Active plant mappings; user grants; document sequences; active client mappings; product mappings; any challan/invoice/wastage; vendor mappings | `company_plant`, `user_company_access`, `document_sequence`, `client_company`, `product_company`, `challan_header`, `invoice_header`, `wastage_record`, `vendor_company` | `company_id` | `company` FOR UPDATE | Any of those hits | No blocking deps |
| Plant | Active mappings, then each pairing’s `companyPlant()` | `company_plant` + pairing tables | Via `company_plant_id` | `plant` FOR UPDATE | Mapping or pairing txn | Unused plant |
| Company–Plant | **Strict:** any txn for the exact pairing | `inward_entry`, `material_movement`, `material_lot_balance`, `challan_header`, `wastage_record`, `invoice_challan_link`⋈`challan_header`, `rejected_dispatch`⋈lines, `allocation_trace_link`⋈lines | **Exact `company_plant_id`** | `company_plant` FOR UPDATE | Any txn | Unused pairing only |
| Unit | **Strict:** any product/inward/challan line/movement reference | `product`, `inward_entry`, `challan_line`, `material_movement` | Global unit id | `unit_master` FOR UPDATE | Any reference | Unused unit; never DELETE |
| Client | Active sites; **nonterminal** CH/INV; active rates; active recipes | `client_site`; `client_company` → `challan_header`, `invoice_header`, `rate_master`, `recipe` | Via `client_company_id` (no extra plant predicate) | `client` FOR UPDATE | Active/open deps | Isolated client with only terminal docs |
| Client Site | **Nonterminal** challans | `challan_header.client_site_id` + nonterminal statuses | Site id | `client_site` FOR UPDATE | Open CH | Terminal billed/cancelled CH only |
| Vendor | Current open vehicle assignment; open inward requiring an active company mapping (inward has **no** status — posted rows are historical, so `openInwardForVendorCompany` is 0) | `vehicle_supplier_assignment_history` (`assigned_until_utc` NULL); `vendor_company` | Vendor id; mapping `company_id` | `vendor` FOR UPDATE | Current assignment (or a true open inward if one existed) | Historical inward alone after assignment closed |
| Vehicle | **Nonterminal** challans | `challan_header.vehicle_id` | Vehicle id | `vehicle` FOR UPDATE | Open CH | Billed/cancelled CH only |
| Manufacturer | Active brand **or** active vehicle | `vehicle_brand`; `vehicle` | Global | `vehicle_manufacturer` FOR UPDATE | Active brand/vehicle | No active brand/vehicle |
| Brand | Active vehicle | `vehicle` `is_active=1` | Global | `vehicle_brand` FOR UPDATE | Active vehicle | Only inactive vehicles remain |
| Product / RM | Positive available/reserved/rejected qty; active recipe header/lines; nonterminal CH/INV lines; allocations on nonterminal CH | `material_lot_balance`; `challan_line`⋈header; `invoice_line`⋈header; `allocation_trace_link`; `recipe`; `recipe_line` | Product id | `product` FOR UPDATE | Stock/recipe/open docs | Terminal rows / unused FP (e.g. Crushed Sand) |
| Additional charge | Nonterminal invoice matrix above | `invoice_charge_line`⋈`invoice_header` | Charge id; company on master is selector-only | `additional_charge_master` FOR UPDATE | Nonterminal INV | Terminal IRN_GENERATED / CANCELLED snapshots |
| Statutory | Deactivation would leave the rule type without an **applicable** active rule (`IrnRecordingService`: company-specific or Universal). Unique `uq_stat_active_rule` allows only one active row per rule+company scope | `statutory_timing_configuration` | `rule_type` + `company_id` (NULL = Universal) | `SELECT … FOR UPDATE` | Last Universal (and company-specific with no Universal fallback) | Company-specific row while Universal remains; or successor replace in one tx |
| Recipe | Header deactivate is allowed (product deactivation is what recipe references block) | — | — | `recipe` FOR UPDATE | — | Reason required |

---

## 5. Historical-readability tests (and matching block tests)

| Entity | Terminal history remains readable after allowed deactivation | Active/open dependency blocks |
|---|---|---|
| Client Site | `LifecycleDeactivationMatrixTest::testClientSiteTerminalChallanDoesNotBlockAndRemainsReadable` — Hinjawadi name still on `CH-2026-000481` | `testClientSiteOpenChallanBlocksDeactivation` |
| Vehicle | `testVehicleTerminalChallanDoesNotBlockAndRemainsReadable` — `MH12AB1234` still on billed challan | `testVehicleOpenChallanBlocksDeactivation` |
| Vendor | `testVendorHistoricalInwardDoesNotBlockAfterAssignmentClosedAndRemainsReadable` — inward still shows Western Cement Suppliers; assignment history keeps the name | Same test asserts open assignment blocks first |
| Client | `testClientActiveSiteAndRecipeBlockWhileIsolatedTerminalClientCanDeactivate` — cancelled challan still shows the deactivated client | Same test asserts Beta is blocked; `testBetaClientRemainsEditableWhenOnlyDeactivateIsBlocked` |
| Brand | `testBrandActiveVehicleBlocksAndInactiveVehicleRemainsReadable` | Same |
| Manufacturer | `testManufacturerActiveBrandBlocksAndUnusedManufacturerCanDeactivate` | Same |
| Product | `testProductStockAndActiveRecipeBlockWhileUnusedFinishedProductCanDeactivate` | Same |
| Unit | `testUnitStrictReferenceStillBlocksAndUnusedUnitCanDeactivate` — row remains, `is_active=0` | Same |
| Company–Plant | `CompanyPlantDeactivationTest::testUnusedPairingCanBeWithdrawnWithoutDelete` | `testSeededChakanPairingIsBlockedWithCloseOnlyReasons` |
| Additional charge | `ChargeDeactivationMatrixTest::testTerminalIrnGeneratedReferenceAllowsDeactivationWithoutTouchingSnapshot` — invoice still `177592.00` | `testNonterminalInvoiceStatusesBlockAndCancelledAllows` |
| Statutory | `StatutoryTimingTest::testCompanySpecificRuleCanDeactivateWhileUniversalRemainsReadable` — IRN window still evaluates; history has DEACTIVATED | `testDeactivatingTheOnlyActiveRuleIsBlocked` |

Browser: Beta client Deactivate opens OV-12 with Close only (sites + rates + recipe). Seeded Universal statutory Deactivate opens OV-12 with Close only. Seeded vehicle `MH12AB1234` and site Hinjawadi expose OV-13 (not confirmed in QA).

---

## 6. Per-Screen-ID browser QA matrix

**Method (not HTTP 200 + Frame Name):** Chrome 148 via Selenium at 1280×900 and 375×812, scrolling to `.screen`, measuring `documentElement.scrollWidth > clientWidth`, clicking OV-12 (not OV-13 confirm), plus HTML inspection of operational pages and `?frame=` overlays. Screenshots live under `/opt/cursor/artifacts/qa_*.png`. They are not committed to git.

PHPUnit `Phase2FrameHttpTest` still checks that all **216** Frame Names appear in the response. That is **not** this visual matrix.

| Screen ID | Populated Default/Edit | Empty / NoResults / Error (if in manifest) | PermissionRestricted or deactivation-blocked | Mobile | Visual notes |
|---|---|---|---|---|---|
| CMP-SCR-001 | PASS — Apex RMC + Apex Aggregates, GSTINs | Empty overlay: empty copy, no rows. NoResults overlay present in HTTP frames | PermissionRestricted: Add company omitted | PASS — record cards, table hidden, no horizontal overflow after CSS fix | Labels Company/GSTIN/Status. Desktop table only. |
| CMP-SCR-002 | PASS — Apex RMC, GSTIN, Asia/Kolkata, INR, Save | Error overlay: banner + **field-error next to Company Name and GSTIN** | Deactivate OV-12 (company has txns). Save remains | M-Create-Default overlay exists | Sticky Save does not cover fields. |
| CMP-SCR-003 | PASS — Chakan shared, Talegaon, Ranjangaon | Empty/NoResults overlays | PermissionRestricted omits Add plant | PASS cards | Shared companies shown as chips. |
| CMP-SCR-004 | PASS — Chakan mapping | Error overlay | Used pairing OV-12 Close only | M frames | Sticky actions + padding-bottom 88px. |
| PTY-SCR-001 | PASS — four approved clients, no Horizon | Empty/NoResults | Overlay omits Add client; Open/Sites remain as VIEW | PASS cards | GSTINs match. |
| PTY-SCR-002 | PASS — Beta, 27AAAAA0000A1Z5, Pune, Save | Error field-error on GSTIN/name | **OV-12 Close only** (sites, rates, recipe). Edit still enabled | M-Edit-Error | Inspected overlay screenshot `qa_client_ov12.png`. |
| PTY-SCR-003 | PASS — Hinjawadi Phase 2 | Empty/NoResults | — | PASS cards | Parent client visible. |
| PTY-SCR-004 | PASS — Hinjawadi, Save | Error overlay | **OV-13** (terminal billed CH). Not confirmed | M frames | — |
| PTY-SCR-005 | PASS — both approved vendors, no Sahyadri | Empty/NoResults | PermissionRestricted omits Add | PASS cards | GSTIN optional (—). |
| PTY-SCR-006 | PASS — Western Cement | Error overlay | **OV-12** current vehicle assignment | M frames | Save remains. |
| PRD-SCR-001 | PASS — M25, M30, Crushed Sand | Empty/NoResults | PermissionRestricted omits Add | PASS cards | Units MT. |
| PRD-SCR-002 | PASS — M30 RMC | Error overlay | OV-12 recipe | M frames | Track by lot Yes read-only. |
| PRD-SCR-003 | PASS — OPC 53 Cement; AGG20 and M-Sand also present | Error overlay | OV-12 recipe/stock | M frames | Classification not switchable. |
| PRD-SCR-004 | PASS — MT, KG; MT Deactivate OV-12; KG OV-13 | Empty/NoResults | PermissionRestricted omits inline add/edit | **PASS cards** (`qa_units_mobile.png`) | Desktop table hidden on mobile. |
| RCP-SCR-001 | PASS — Client Beta (Apex RMC), Finished product M30, 0.350/0.700/0.650, no Horizon | Error: banner + field-error on Client/Finished product | Recipe deactivate is OV-13 | M-Edit-Error: long nav above form; after scroll, errors present; no table compression | Sticky Save/Deactivate; composition remains readable. |
| VEH-SCR-001 | PASS — both vendors, read-only, **no Add** | Empty overlay; **no M-NoResults frame** (manifest) | N/A create | PASS cards | Desktop table only after CSS specificity fix. |
| VEH-SCR-002 | PASS — Tata Motors, Ashok Leyland | Empty/NoResults | Tata OV-12 (active brand/vehicle) | PASS cards | Inline Save remains when Deactivate blocked. |
| VEH-SCR-003 | PASS — Signa/Tata, Dost/Ashok Leyland, no LPT | Empty/NoResults | Brands OV-12 while vehicles active | PASS cards | — |
| VEH-SCR-004 | PASS — MH12AB1234 Signa/Tata/Western; MH14XY5678 Dost/Ashok Leyland/Reliable; uppercase no spaces; history Open | Error overlay field-error on registration | Vehicle 1 **OV-13** (billed CH). Not confirmed | M-Edit-Error | Sticky Save does not cover history. |
| PRC-SCR-001 | PASS as **empty history** — approved dataset has **no universal rate**. Create form present for Super Admin; clients/products now load from all granted companies | Empty/Error/Loading overlays | PermissionRestricted omits create | M-Configure-Default: empty cards if no rows | Not a fabricated universal sample. |
| PRC-SCR-002 | PASS — history Apex RMC / Beta / M30 / 4966.6667 Active | Empty/Error | PermissionRestricted omits create | PASS cards for history | Successor form Client list includes Beta after RateController fix. |
| ACH-SCR-001 | PASS — Loading + Commercial Discount | Empty/Error (field-error on Code) | Blocked vs allowed Deactivate uses OV-12 vs OV-13 | PASS cards | Close only when blocked. |
| STAT-SCR-001 | PASS — IRN_RECORDING_WINDOW Universal 72 HOUR, history CREATED | Empty overlay; Error field-error on Limit value | **OV-12 Close only** on the only Universal rule (`qa_statutory_ov12.png`) | PASS cards | Replace-active-rule remains the successor path. |

**CSS defects found and fixed during this pass**

1. `.card-list { display:flex }` overrode `.mobile-only { display:none }`, so desktop lists showed tables **and** cards. Selector `.card-list.mobile-only { display:none }` restores desktop tables only.
2. Missing `box-sizing: border-box` plus sidebar padding contributed to mobile `scrollWidth` overflow. Global box-sizing + `html { overflow-x: hidden }` + the card fix: mobile 375px `overflow=False`.
3. Simulated Error frames only set `_form`; they now also set field keys so `.field-error` sits next to GSTIN/name/registration/limit/rate/charge fields.
4. Rate selectors used `plants[0]` (Apex Aggregates), leaving Client empty on PRC-002. Selectors now union all granted companies (same pattern as Recipe).

**Not claimed:** HTTP 200 with a Frame Name is not visual QA. `Phase2FrameHttpTest` remains the Frame Name contract only.

---

## 7. Corrected Figma vs application validation statement

- Application tests verify **216 expected Frame Names** and **server-rendered visual states** (empty/loading/error/permission overlays, labels, approved identities) via PHPUnit HTTP tests (`Phase2Frames` / `Phase2FrameHttpTest`) plus the browser QA in §6.
- Application tests **do not** verify Figma component attachment or detachment.
- Application tests **do not** inspect or preserve Figma prototype reactions.
- The **57 reactions** belong to the separately approved Figma P28 handoff. They are not measured by `FrameCatalog` or `Phase2Frames`.
- This implementation **did not modify the Figma file**.
- Application component-partial rendering (CI4 views OV-07/11/12/13/14, sticky bars, record cards) is **separate** from Figma component integrity.
- Do **not** read “Detached/broken UI components: 0 measured” as a Figma measurement.
- Do **not** read “57 prototype reactions preserved” from PHP `FrameCatalog`.

---

## 8. Production seed / probe route count

**0.**

`ProductionRouteAbsenceTest` asserts Routes.php contains no `seed` / `fixture` / `demo` / `probe` paths, FrameCatalog routes do not point at those shortcuts, and former paths return 404.

---

## 9. Full test and assertion count

**154 tests, 2980 assertions, OK** (`vendor/bin/phpunit --no-coverage`).

Suites: unit + integration + feature for Phase 0, Phase 1, and Phase 2 (including `Phase2FrameHttpTest` 216 frames, `AuthoritativeSampleDataTest`, `LifecycleDeactivationMatrixTest`, `ApprovedSampleScreensTest`, `StatutoryTimingTest`, `ChargeDeactivationMatrixTest`, `LedgerReconciliationTest`).

---

## 10. Phase 1 reconciliation results

Unchanged on the live development database and in PHPUnit seed:

| Figure | Result |
|---|---|
| Challan `CH-2026-000481` | BILLED |
| Invoice `INV-2026-000119` | IRN_GENERATED, grand total **177592.00**, round-off **0.00** |
| Inward `INW-2026-000212` | Present |
| Allocation on billed challan | 18.000 + 12.000 = 30.000 (`LOT-2026-00212` / `LOT-2026-00230`) |
| `LOT-2026-00230` remaining | **13.000** |
| Disposition | 3 + 7 + 2 = 12 Fully Disposed |
| Material ledger closing M30 / Chakan / Apex RMC | **116.000 MT** |
| Invoice approval wording | Separate action from Challan approval; Challan must be Approved first |

---

## 11. Files changed (correction pass)

Relative to Phase 2 implementation `3dc8862`:

- Seed/migrations: `MasterSeeder`, `Phase2MasterSeeder`, `SampleDataCorrectionSeeder`, `TransactionSeeder`, `200014` vehicle catalogue, `200016` vehicle catalogue correction, `DatabaseSeeder`
- Deactivation: `DeactivationGuard`, `VendorService`, `VendorCompanyAccess`, `BrandService`, `ManufacturerService`, `VehicleService`, `ChargeService`, `StatutoryTimingService`
- HTTP/UI: Vehicle manufacturer/brand deactivate routes; statutory deactivate route; Recipe/Rate selectors across granted companies; unit/rate/charge/statutory mobile cards; OV-12/13 wiring; `MasterController::fieldErrors`; CSS card/overflow fixes
- Tests: `AuthoritativeSampleDataTest`, `LifecycleDeactivationMatrixTest`, `ApprovedSampleScreensTest`, `StatutoryTimingTest`, `SeededDocuments`, `VehicleAssignmentTest`
- Docs: `PHASE2_PLAN.md`, this report, README sample masters
- `.gitignore`: `/vendor/` so `app/Views/pages/vendor/*` is tracked

No history rewrite. Checkpoints A–E remain a single commit.

---

## 12. Remaining risks (non-blocking)

- Seeded **MH12AB1234** and **Hinjawadi Phase 2** are now **OV-13 allowed**. Do not confirm Deactivate in a shared demo database.
- Approved dataset has **no universal rate**. PRC-SCR-001 Default is an empty history plus create form, not a fabricated sample row.
- Mobile chrome still places the full sidebar **above** page content; users scroll to cards. After the CSS fix this does not create horizontal overflow.
- PermissionRestricted overlay omits Add; VIEW links such as Open/Sites can remain. Direct URL without VIEW still 403.
- Figma file is not in the repo; application tests cannot prove Figma component integrity.
- `s.patil` (Store Keeper) does not have `CLIENT.LIST`; real permission-denied for that role is 403, not the list overlay. Overlay PermissionRestricted was used for the list-without-Add state.

No uniqueness or FRIS/plan conflict blocker. Phase 3 not started.

---

## 13. Updated Phase 2 exit-criteria matrix

| Criterion | Result |
|---|---|
| 216 CSV frames render with exact Frame Names | Met (`Phase2FrameHttpTest`) |
| Additive migrations only; Phase 1 tables not rebuilt | Met |
| Chakan shared; timezone Asia/Kolkata | Met |
| VEH-SCR-001 GET-only | Met |
| Unit/manufacturer/brand inline; no unit delete; no Capacity/fleet fields | Met |
| Recipe sample quantities; View without Add/Remove/Save on view-only; no stock impact | Met |
| Rate history immutable; calculator 177,592.00 | Met |
| Ordinary charges fixed; discount before GST | Met |
| Statutory UTC; missing config blocks; history append-only | Met |
| OV-12 Close only; OV-13 only when allowed | Met (browser + tests) |
| Selectors exclude inactive; historical view includes inactive | Met |
| 216 frame tests + Phase 0/1 regression | Met after this pass |
| No React/Next/Nest/Postgres/JWT/localStorage/mock API | Met |
| Approved sample identities (no LPT, no Horizon, both vehicles) | Met |
| Lifecycle-aware deactivation (strict Company–Plant/Unit; terminal history does not permanently block others) | Met |
| Figma claims corrected | Met |
| Browser QA per Screen ID (not HTTP-only) | Met as documented in §6 |
| Production seed/probe routes | 0 |
| Checkpoints A–E process deviation recorded | Met — see below |
| Phase 3 | **Not started** |

---

## Task 7 — Checkpoint process deviation

Checkpoints A–E were delivered as **one commit** `3dc8862` (*Implement Phase 2 master-data screens, 216 frames, and additive schema.*) rather than the five requested commits.

This is a **process deviation with no functional impact**. History was **not** rewritten and separate commits were **not** fabricated after the fact.

---

## Task 5 remainder

`docs/PHASE2_PLAN.md` §15 already states the Figma vs application rules. This report is the operator-facing correction.
