# Navagoo Live ← Navagoo 2.0 Gap-Bridging Plan (demo v0.14.0 → v0.20.0)

**Live repo:** `c:\Users\mmm_i\VS with Claude\Navagoo App (Live)` (Yii2 PHP + MySQL; Ahmad's production codebase; branch `tailwind-poc-ui-enhancements`)
**Reference:** `C:\Users\mmm_i\VS with Claude\Navagoo 2.0\navagoo-app` (React demo, currently **v0.20.0**, no backend — the desired-product guide)
**Plan doc to create on execution:** `ai_specs/05_PLANS/DEMO_SYNC_V14_V20_MASTER_PLAN.md` (follows the proven disposition-table pattern of `ai_specs/05_PLANS/DEMO_SYNC_MILESTONE_N_PHASE2_PLAN.md`)
**Governing playbook (all rules apply):** `ai_specs/00_SYSTEM_INSTRUCTIONS/AURORA_DEMO_PORTING_PLAYBOOK.md`

---

## ⚡ EXECUTION STATUS (updated 2026-07-12)

Work landed **out of plan-order** (two committers on `tailwind-poc`: MI/Claude + Ahmad). Real state per phase:

| Phase | State | Evidence |
|---|---|---|
| **P0** Money truth + security + audit | 🟡 **~70%** | **B1 double-pay** (unified `consumedBookingIds` = transfers ∪ `invoice.applied_booking_ids`) ✅ `edb75d8` + balance-neutral `invoiceNetting` unit-tested (33 green). **B2 transfer notif-netting** (`transfer_request.total_notif_fees`, `WithdrawalBundlingService`) ✅ `db318a6`. **`shopBalanceView()`** ✅ `db318a6`, now routed through unified `consumedBookingIds` ✅ `8e6b8f9`. **Audit seam** (`audit_log` + `AuditLogService`) ✅ `db318a6`. **REMAINING:** security T16 (withdrawal TOCTOU `SELECT…FOR UPDATE`)/T17 (txn-wrap)/T19 (CSRF-CORS)/T23; admin Shop-Balances **waterfall drawer** + Pending badge; **`FinanceBalanceIdentityTest`** (port demo halala pins); `shopBalanceView.runningBalance` is a `= withdrawableNow` **stub** (UI ignores it — not wired, no wrong number) → implement the pinned identity or drop the field; route `AdminFinanceController` balance through `shopBalanceView` (currently a parallel calc — agrees on components, no user-visible divergence). |
| **P1** Wages engine + Team 3-tab | 🟡 **Wave 1 done** | engine `WageEngineService` ported + 24 cent-locked tests (`4f16c17`); Waves 2–6 (schema/service/cron/UI/i18n) in **`WAGES_M3_IMPLEMENTATION_PLAN.md`**. Off-rail; `user_profile` compensation cols already live |
| **P2** Subscriptions & commercials + Finance 5-tab hub | 🟡 **partial** | Navagoo Plans shop page ✅ (`79e5c64`/`8dc3a45`); Finance hub E3 ✅ (Ahmad's `FINANCE_PARITY_WAVE_2026_07_05`); admin Offers/Payment tabs ✅ (`67b7720`). **REMAINING:** `EntitlementService`/feature-gating, offers engine w/ money pins, subscription lifecycle cron + dunning, Paymob **MIT** card rail, bank-slip rail. |
| **P3** RBAC / staff logins | ⬜ **not started** | — |
| **P4** Admin BI/P&L + Costs + Events log | ✅ **DONE** | `7989a86`·`3033b30`·`67b7720`·`26b3fde`·`9855d52` — Costs register, Platform P&L (margin 71.7% verified), Dashboard BI 5 tabs, Events audit UI, Users&Roles, turquoise off-CDN chrome, +12 unit tests. (Landed early, before P0-P3.) |
| **P5** Shop Analytics (11-widget) | ⬜ **not started** | needs P3 gating + a `BiQueryService` |
| **P6** Home landing pages | 🟡 **partial** | admin subscription-aware Home ✅ (`3033b30`/`9b28824`); shop Home balance-panel + attention-feed ✅ (via `shopBalanceView`); **REMAINING:** shop Home full v0.19 re-port (schedule-hero timeline + week-at-a-glance — the 42% item in `SHOP_PORTAL_DESIGN_PARITY_AUDIT`). |
| **P7** Msegat SMS + T2 WhatsApp + Paymob MIT | ⬜ **documented only** | `MessagingService` + adapters + DLR crons unbuilt (see `DEMO_SYNC_MILESTONE_N_PHASE2_PLAN`). |
| **P8** Sweep / reskin / ops closeout | ⬜ **not started** | incl. group-modal polish, P2 backlog, Firebase rotation. |

**Housekeeping done 2026-07-12:** removed 60 committed dev-scratch/junk files + `.gitignore` guards (`01b7725`). **Flagged (task_8273c3b2):** `db318a6` deleted `frontend/views/booking/*` but left `BookingController` view-actions rendering them → 500 on direct URL (live v2 `/booking-calendar` flow unaffected; JSON endpoints intact).

**Next recommended:** finish **P0** (security T16/T17 + FinanceBalanceIdentityTest + waterfall UI) → then **P1 Wages** (biggest MI concern, off-rail/safe). Never two in-flight edits to `FinanceLedgerService` — coordinate with Ahmad.

---

## Context

MI (product owner, non-engineer) builds the desired product as a working React demo ("Navagoo 2.0"). Ahmad ports it into the real Yii2 app that powers the live web portals and the Flutter mobile app. The live repo is at parity with the demo through **v0.13.0** (dynamic notifications — fully dispositioned and shipped). The demo has since shipped **seven more releases** (v0.14 → v0.20): subscriptions, staff roles, analytics dashboards, finance corrections, Home pages, and a full wages engine. This plan bridges that gap — and closes the parked security items — without breaking the mobile app or any third-party integration.

## Executive summary — plain English

**Where we are.** The good news first: your two biggest worries — the **bookings engine** and the **finance engine** — are already largely migrated. Ahmad's repo has a real port of the demo's finance math (`FinanceLedgerService`, an append-only charge ledger with fees, refunds, reversals, and settlement), and the full booking machinery (calendar, walk-ins, group bookings, status transitions, time-off) wired to the real database. What's missing is everything the demo shipped **after** June 28th.

**The most urgent item is a money bug, not a feature.** After the live port was made, the demo discovered and fixed **two real money leaks**: (1) earnings that were already paid out through an invoice could be counted again toward a transfer — a shop could effectively be paid twice for the same booking; (2) SMS/WhatsApp message fees weren't being deducted from transfer payouts, so shops were paid slightly more than they were owed. I verified the live code — **those fixes are not in it**. Phase 0 ports them first, together with the security items that were parked (safe database locking on wallet withdrawals, wrapping multi-step money writes in transactions, and rotating a leaked Firebase key).

**Then the features, in this order:**
1. **Wages** (your top concern) — the demo replaced its simple payroll table with a real engine: salaries accrue daily, commissions accrue per completed booking, the system automatically "captures" each pay period, and the shop pays per-capture, per-specialist ("Settle now"), or all at once — with tips handled separately but payable together. The live database already has the salary/commission columns; we build the engine, the payment history, and the screens.
2. **Subscriptions & plans** — Starter/Growth/Pro tiers, feature gating, offers with discounts and lock-ins, automatic billing with card or bank-transfer, upgrade/downgrade rules. This is how Navagoo earns; it's the biggest single phase.
3. **Staff logins & roles** — today one owner login runs the whole shop portal. The demo added managers and front-desk staff with configurable permissions (front desk can't see money). We map that onto the role system the live app's admin panel already uses.
4. **Analytics** — the admin profit & loss + business dashboard, then the shop's 11-widget analytics page, plus a proper audit log of everything that happens in the system.
5. **Home pages** — the "morning briefing" landing pages for admin and shops.
6. **Real SMS/WhatsApp delivery** — the notification engine already bills correctly, but messages don't actually leave the building yet. We wire the real providers (Msegat for SMS, T2 for WhatsApp) with delivery receipts, so a failed message automatically refunds the shop.

**What we keep that the demo doesn't have.** The live app is *ahead* of the demo in several areas and none of it gets regressed: the **marketing connector** (Upload-Post social posting), customer invitation campaigns, promo codes/ads/referrals, **reviews & ratings**, and the live **Paymob** payment integration. These move forward with us; the final phase reskins their screens to match the 2.0 look.

**What we deliberately don't build yet.** Demo milestones M4 (customer app + reviews), M5 (specialist app), M6 (admin breadth), M8, M9 are unbuilt or in-flight in the demo — we track them and disposition them in a successor plan. Note the demo is already working on M4; its "Review" data shape will need reconciling with the live app's existing `Rate` model when it lands.

**How safety is guaranteed.** Every phase: (a) never changes anything the mobile app reads (frozen: `notifications` table, booking/wallet/login API responses — new columns are always additive with defaults); (b) ports the demo's money tests to PHP so every formula is pinned to the cent; (c) ships in both Arabic and English; (d) goes to Ahmad as one reviewable branch with a commit-by-commit disposition table, exactly like the notifications sync that already worked.

---

## Phase plan (9 phases, strictly ordered, each independently shippable)

| # | Phase | Demo range | Size | Why here |
|---|-------|-----------|------|----------|
| 0 | Finance truth + gated security + audit seam | v0.18 (`5ca917d..e49940b`) + cherry `4df30f5` | M | Real money leaking now; all later phases build on trusted balances |
| 1 | Wages engine (M3) + Team 3-tab | v0.20 (`6f8b165..32777a2`) | L | Top MI concern; off-rail (touches no charges/transfers/invoices); schema exists |
| 2 | Subscriptions & commercials (M7a) + Finance 5-tab hub | v0.14 (`17b61d5..7be2c9a`, 81 commits) | XL | Revenue engine; works owner-only (demo shipped it before RBAC too) |
| 3 | RBAC / staff logins (M7b) | v0.15 (`7be2c9a..068df71`) | L | Before BI so money widgets arrive role-gated |
| 4 | Admin BI/P&L + Costs + Events log | v0.16 (`068df71..f834247`, 85 commits) | XL | Reads the now-correct ledger |
| 5 | Shop Analytics dashboard | v0.17 (`f834247..5ca917d`) | L | Needs P3 role-gating + P4 query layer |
| 6 | Home landing pages | v0.19 (`e49940b..6f8b165`) | M | Composes P0/P4/P5 selectors |
| 7 | Msegat SMS + T2 WhatsApp activation + Paymob MIT hardening | ② integration spec | L | DLR-driven billing needs the correct ledger |
| 8 | Sweep: marketing-connector aurora reskin, group-modal polish, backlog P2, ops closeout | parity backlog | M | Cleanup + live-only surface reconciliation |

**Ordering rationale (key dependency findings):**
- Entitlements do **not** need RBAC first: demo's `canAccess = hasFeature && hasRole` shipped with `hasRole` trivially true in the owner-only world. P2 stubs the role predicate to `user_type == SHOP_OWNER`; P3 swaps one function.
- BI does **not** need the events log: demo's P&L reads the charge ledger, not events. The events **table + emit chokepoint** land tiny in P0; the **UI + backfill** land in P4 (as the demo bundled them). Every phase in between adds emit calls to the write paths it touches — no 117-call-site big-bang.
- Paymob **MIT (saved-card recurring)** is pulled into P2, not P7 — it is half the subscription lifecycle (auto-renew + `charge_to_card` dunning recovery), and Paymob token infrastructure already exists live.

---

### Phase 0 — Money truth + security + audit seam (M)

**Verified premise:** `grep consumedBooking|totalNotifFees|shopBalanceView` over live `common/` → zero hits. The v0.18 fixes are absent.

- **B1 double-pay fix:** unified `consumedBookingIds` = bookings consumed by settled transfers ∪ bookings applied to invoices — into `common/components/FinanceLedgerService.php` running-balance/withdrawable math.
- **B2 transfer netting:** net booking-linked sms/whatsapp charges into transfer payouts; `transfer_request.total_notif_fees` column (additive).
- **One-truth `shopBalanceView()`** method with the pinned identity `runningBalance = withdrawableNow + feesInvoicedOrLockedOnA − feesOnHeldBookings − nonBookingFees − openInvoiceDue`. All shop/admin balance figures route through it.
- **UI:** admin Shop Balances breakdown drawer (waterfall) + "Pending" charge badge (aurora partials).
- **Security (gated items from `REFACTOR_AND_HARDENING_ROADMAP_2026_06_29.md`):** T16 wallet-withdrawal TOCTOU (`beginTransaction` + `SELECT … FOR UPDATE`); T17 transaction-wrap the 5 multi-table write paths; T19 ApiController CSRF/CORS (documented, additive-only — mobile smoke must pass); T23 `base/Shop.php` de-tiering. **Ops (needs Ahmad):** rotate leaked Firebase key + history scrub, `YII_ENV=prod`, commit untracked aurora JS.
- **Audit seam:** migration `create_audit_log` (actor, scope, shop_id, action, entity, before/after JSON, indexed) + `common/components/AuditLogService.php::emit()` mirroring demo `src/lib/eventDiff.ts` diffing. Emit only from paths touched this phase (withdraw, settle, invoice-apply).

**Demo sources:** `src/lib/finance.ts` diff over the range, `src/store/{balance-reconciliation,shopBalanceView,settlement-consumption}.test.ts`.
**Tests:** new Codeception `FinanceBalanceIdentityTest` porting the demo's halala-gate cases verbatim (same seed numbers, assert to 2dp); T16 concurrency test (two parallel withdrawals → one wins).
**API impact:** none structural. Flag to Ahmad: shop balances may *change value* — that is the fix.

### Phase 1 — Wages engine M3 + Team E1 (L)

- Port `src/lib/wages.ts` → new `common/components/WageEngineService.php`: daily fixed-salary accrual (weekly ÷7, biweekly ÷14, monthly ÷actual-days — a full month sums exactly to salary), commission per completed booking on completion date, `commission_basis ∈ {service_value, net_of_fees}` (net-of-fees base injected from FinanceLedgerService, read-only).
- `reconcileWages` catch-up capture cron (`console/controllers/WagesController.php`, daily, `withoutOverlapping`): walks **every** missed pay-cycle boundary; idempotency = unique `capture_key` (specialist|cycle|boundary) — cron frequency is a liveness knob, never correctness. **No sim clock in live: cron IS the clock; must be safe to run late/twice/after downtime.**
- Payment routes: per-capture / Settle-now (per specialist, optional tips toggle, itemized confirm) / Mark-all-paid (whole shop, irreversible-confirm) → single `wage_payment` history. Tip settlement integrated (standing balance, foldable or standalone).
- Deactivate freezes accrual (`wage_frozen_at` → banked `wage_carried_fixed` on reactivate). New shop setting "first workday of week" (existing settings hub).
- Detailed Charges **CSV + print export** (shop + admin).
- Team page → 3 tabs (Specialists | Payroll | Structure), replacing the current render-time payroll (which mirrors the demo's *superseded* trivial payroll).
- **Off-rail invariant enforced:** WageEngineService never references Charge/TransferRequest/Invoice — add an arch test.

**Migrations:** `wage_capture` (+ unique capture_key), `wage_capture_line` (per-booking commission audit), `wage_payment`, `wage_payment_capture`; `user_profile` +wage_frozen_at/+wage_carried_fixed; `shop` +first_workday.
**Tests:** port `wages.test.ts` pins to the cent — proration across month lengths, completion-date attribution, both bases, 3-skipped-cycles → exactly 3 captures → re-run → 0, freeze/carry, tips. Wallet balance unchanged on screen (off-rail proof).
**API impact:** none (mobile never reads wages).

### Phase 2 — Subscriptions & commercials M7a + Finance hub E3 (XL — split into 2a engine / 2b rails+UI for review)

- Tiers Starter/Growth/Pro on existing `NavagooSubscriptionPlan` (+tier, +features_json, +specialist_cap, +payment_methods_json).
- Port `src/lib/entitlements.ts` → `common/components/EntitlementService.php`: tier composition, FeatureKey catalogue, `canAccess = hasFeature && hasRole` (role stubbed owner-true until P3), `paymentMethodEnabled` gating booking payment timings (**additive default-allow when unconfigured — mobile booking flow unaffected**).
- `Offer` entity (rate discount + sub discount + lock-in) + offers storefront + admin Subscription Plans hub.
- `reconcileSubscriptions` lifecycle cron on existing `ShopSubscription` + Invoice billing lifecycle (`m260628_230000` already live): trial → charge → renewal → dunning retries → grace → lock. Idempotency key = subscription|period|attempt.
- Two rails: **card MIT** (Paymob saved-card tokenization, recurring charge, `charge_to_card` recovery — extend `PaymobPaymentHelper` + additive webhook branch) and **bank-transfer slip** (reuse Invoice verification lifecycle).
- Prorated upgrades, term-end downgrades, grandfathering, surplus-specialist lock on downgrade — **and fix the demo's parked bug**: bank-rail upgrade must clear `plan_cap_locked` (live goes AHEAD of demo here; note back to demo backlog).
- Shop Finance 5-tab hub (Earnings | Detailed Charges | Invoices | Settlement | Subscription) — the E3 epic; models all exist.

**Demo sources:** `src/lib/{entitlements,subscriptions,offers}.ts` + tests, `finance.offers.test.ts` (money pins — offers change charge amounts), `src/portals/admin/Subscriptions.tsx`, `src/portals/shop/finance/*`, plus demo spec `docs/superpowers/specs/2026-06-27-paymob-msegat-integration-design.md` (MIT rail).
**API impact: ADDITIVE + FLAGGED** — payment-timing gate touches shared booking paths; Paymob webhook gains a branch. Mobile book+pay smoke mandatory before merge.
**i18n:** largest of the plan (~250 keys); Arabic for plan/dunning/invoice wording needs MI review.

### Phase 3 — RBAC M7b (L)

- Map demo 7 roles onto **Yii RBAC** (backend module already exists): platform super_admin/admin/finance/support extend backend items; shop owner/manager/front_desk = new frontend-tier items. **`user_type` untouched** (mobile auth reads it) — assignments layer on top by user id.
- Configurable matrix: demo `src/lib/permissions.ts` keys → `auth_item`; matrix editor writes `auth_item_child`. Hide-vs-disable semantics via a `RbacHelper::can()` view helper + controller behavior.
- Shop staff CRUD + first-login set-password + account settings. Swap P2's role stub for real checks; retro-gate P0–P2 money surfaces (front_desk sees no money).
- **Lockout guards:** owner always seeded full matrix; editor refuses removing owner grants or self-demotion out of role management; break-glass console command `rbac/restore-owner`.

**API impact:** none if staff reuse `user_type=MANAGER` (decision D2 for Ahmad).

### Phase 4 — Admin BI/P&L + Costs + Events log (XL)

- P&L: revenue reconciled to `charge` ledger − editable `cost_register` table; Costs page; tabbed BI dashboard (Overview/Volumes/Performance/Financials/Flags) with RAG targets (`bi_target` config).
- **Performance strategy (live-specific; demo computes in-memory over toy data):** new `common/components/BiQueryService.php` — SQL aggregation with covering indexes, EXPLAIN-verified on prod-size dump, 300ms/widget budget; nightly `bi_daily_rollup` cron only where direct SQL misses budget.
- Events log UI over `audit_log` (P0 table): filters, before→after diffs, CSV. Port the 104-action taxonomy as PHP const catalogue; **backfill emit calls** across existing write paths via a checklist keyed by action (unbuilt features → DEFERRED rows); port the demo's coverage-gate test so future write paths can't merge without an emit or explicit deferral.

### Phase 5 — Shop Analytics (L)

- 11-widget shop Analytics page (hero KPIs, trend, top services/specialists, 3-way customer mix, status donut, on-time rings, peak hours, busiest-days heatmap), period selector, money widgets behind P3 permissions.
- `booking.origin` additive column (app|walkin|…) set at creation + migration backfill from walk-in markers — **flagged additive; mobile create must work with default**.
- Extend `BiQueryService` shop-scoped; charting per decision D4.

### Phase 6 — Home landing pages (M)

- Admin Home (digest strip, 6-KPI band, needs-attention feed, side panels, bottom band) + Shop Home (today's-schedule hero, needs-attention, balance panel via P0 `shopBalanceView`, week glance). Post-login landing for both tiers (frontend + backend redirects only; API login untouched). Role-adaptive via P3. Port `src/lib/{home,attention}.ts` onto `BiQueryService`.

### Phase 7 — Messaging activation + MIT hardening (L)

- New `common/components/MessagingService.php` with **MsegatAdapter** (send + poll-DLR cron every 5 min) and **T2WhatsAppAdapter** (JWT, send, push-DLR webhook — new additive API endpoint behind existing `WebhookIpFilter`). Wire into `NotificationDispatchService`, replacing the dormant `SMSHelper`/`WhatsAppHelper` path.
- Billing per **delivered** message per the ② spec (decision D5): failed/undelivered → automatic reversal via existing machinery; idempotent on `provider_msg_id` (duplicate DLR → no double reversal).
- Meta WhatsApp template approval registry (`wa_template` table); author `settlement_received` template (ar+en); migrate the existing WhatsApp invitation flow onto MessagingService **without behavior change**.
- Close remaining notif follow-ups: customer-audience `to_id` routing (needs mobile-feed sign-off — decision D7).
- Paymob MIT hardening from P2 (retry/backoff, failure telemetry).
- **`notifications` table schema stays frozen** — provider/DLR metadata lives in a new dispatch-log table + `charge.meta`.

### Phase 8 — Sweep (M)

- **Marketing connector aurora reskin** (Upload-Post social screens + Ads/PromoCode/Invitations/Referral) — reskin only, zero behavior change; write the reconciliation stub for demo's future N2 milestone (live is ahead — keep it that way).
- Sprint-3 group-booking focused modal polish; remaining P2 backlog items from `DEMO_PARITY_GAP_BACKLOG.md`; refresh stale `ai_specs/07_DEMO_PARITY/*`.
- Ops closeout audit: Firebase rotation confirmed, prod flags, server cron list == `schedule.php`.
- **v0.21+ watch note:** freeze this plan's scope at v0.20.0; demo is already on `feat/m4-customer-app` — when M4 lands, reconcile demo `Review` shape against live `common/models/Rate.php` (mapping layer or additive columns, NOT a new table) in a successor disposition doc.

---

## Cross-cutting rules (every phase)

- **Mobile app cannot break:** frozen surfaces = `notifications` schema, booking/wallet/payment/login API responses, `user_type` semantics. New columns always NULL/DEFAULT-safe. Per-phase API-impact statement (only P2 and P5 are "additive + flagged"). Curl smoke suite (login, booking list/create, wallet, notifications feed) before every merge.
- **Money pinned to the cent:** port demo test pins with the same seed numbers; prefer integer-halala/BC math in new services; P0 identity test runs in every later phase.
- **Cron replaces the sim clock:** every engine idempotent via DB unique keys; `withoutOverlapping()`; explicit run-twice test; additions to `console/config/schedule.php` in BOTH qc and prod blocks (P1 wages daily, P2 subscriptions hourly, P4 rollup nightly, P7 DLR poll 5-min).
- **Bilingual mandatory:** every `Yii::t` key lands in `common/messages/{ar,en}/*`; demo is EN-only so Arabic is authored fresh — glossary file for finance terms (accrual/capture/dunning); MI reviews Arabic on money/legal strings; RTL interaction pass per phase.
- **Playbook discipline:** run the demo (`npm run dev` in Navagoo 2.0/navagoo-app) and SEE the rendered screen before porting; new-route-locally; aurora-core helpers (`ngToast/ngConfirm/ngIframeModal`), never native popups; `npm run build:css` after class changes; interaction-test, never screenshot-only ("renders ≠ works").
- **Working with Ahmad:** one phase = one branch = one review packet (disposition table + migration list + API-impact statement + test output + screenshots); never two in-flight phases touching `FinanceLedgerService`; Ahmad merges to main; rebase before review.

## Top risks & mitigations

1. **PHP/TS money-math divergence** → same-seed cent-level pins; integer/BC math; P0 gate in CI forever.
2. **Mobile breakage** → frozen-surface checklist + smoke suite + additive-only columns + Ahmad flag on P2/P5.
3. **Dual-committer conflicts** (Ahmad commits concurrently) → phase-branch protocol, file-ownership ledger in HANDOVER.md.
4. **Cron double-billing** → DB unique idempotency keys, run-twice tests, withoutOverlapping.
5. **RBAC lockout** → owner-full seed, self-demotion guard, break-glass console command.
6. **Arabic quality** → glossary + MI review of money strings + per-phase RTL pass.
7. **Demo keeps moving (M4 collision with live `Rate`)** → scope frozen at v0.20.0; watch note + successor doc.
8. **BI performance on real MySQL** → `BiQueryService` + EXPLAIN budget + rollup escape hatch (demo's in-memory approach doesn't transfer).

## Verification (standing per-phase checklist)

1. `php console/yii migrate` up → down same count → up again, clean both ways.
2. Codeception: finance pin suite + phase pins green (`vendor/bin/codecept` — `composer install` dev deps if missing).
3. `npm run build:css` after any Tailwind class change.
4. Playwright interaction test of every new surface, **EN and AR**, per applicable role — click-through, not screenshots.
5. Mobile curl smoke: login, booking list/create, wallet, notifications feed — byte-compatible.
6. Money crons: manual double-run, assert idempotent (row counts unchanged on rerun).
7. Review packet to Ahmad.

## Decisions made in this plan (for MI/Ahmad to confirm or veto)

- **D1 — Phase order:** money fixes + security first (P0), wages second (P1), subscriptions before RBAC (P2→P3), RBAC before both dashboards, integrations second-to-last. Rationale: real money leaks outrank features; entitlements don't need roles (demo shipped in the same order); dashboards need roles for money-gating.
- **D2 — Staff logins reuse existing `user_type=MANAGER`** rather than adding a new type — safest for mobile auth. Ahmad to confirm mobile ignores MANAGER portal logins.
- **D3 — Events system = new `audit_log` table + emit-as-you-go** (small seam in P0, UI + backfill in P4) instead of a big-bang retrofit or reusing the `notifications` table (frozen for mobile).
- **D4 — BI on direct SQL first, nightly rollup table only where a widget exceeds 300ms** on prod-size data. Charting library chosen at P5 start (reuse what aurora already ships if any; else one lightweight lib, recorded in the playbook).
- **D5 — Message billing = charge on send, reverse on failed/undelivered DLR** (matches the machinery already live for notification reversals and the demo's model). If MI prefers bill-on-delivered, flag before P7.
- **D6 — Paymob MIT rides with subscriptions (P2)**, not with messaging (P7) — it's half the billing lifecycle and Paymob infra already exists. MyFatoorah stays out (effectively absent from the codebase today).
- **D7 — Customer-audience in-app notifications stay unrouted (`to_id=null`) until mobile-feed sign-off** — carried as an explicit P7 item, not silently dropped.
- **D8 — Live-only features are first-class:** marketing connector, invitations, promo/ads/referrals, reviews (`Rate`), Paymob — never regressed; reskinned in P8; demo reconciliation notes filed (N2, M4 reviews).
- **D9 — M7a ships split (2a engine, 2b rails+UI)** so Ahmad's review stays tractable on the 81-commit range.
- **D10 — Fix the demo's parked bank-rail `plan_cap_locked` bug in the live port** (go ahead of the demo) rather than reproducing it for parity.
- **D11 — Security scope:** land gated T16/T17/T19/T23 in P0 with focused integration tests; Firebase key rotation + history scrub scheduled with Ahmad (needs force-push window); `YII_ENV=prod` verified at P0 and re-audited at P8.
