# Dinex KB — Update Brief for Claude Code

> **Audience:** Claude Code, working in the Dinex Laravel repo with code access.
> **Author:** Product Owner (Eslam). **Date:** 2026-09-24.
> **Goal:** Make the per-module KB files the complete, correct, timeless business reference, and generate the combined `BUSINESS_KNOWLEDGE_BASE.md` from them so it can be used as the single search file.
> **How to use this brief:** Work top to bottom. Every task has an ID, the file, a short **Find** string you can grep, the **Fix**, and where needed a **Verify in code** hint. Do not re-audit the whole KB; this list is the audit.

---

## 0. Ground rules (apply to every task)

1. **Code is the truth.** For any task marked **Verify**, read the code at `HEAD` and write what the code does. If the code disagrees with the Fix text in this brief, the code wins. Record the outcome in the report (section 9).
2. **Timeless text.** Module files describe what the system does now, in present tense. Remove inline history from rules:
   - `🚩 SPRINT 1 · 09-08-2026`, `🚩 Draft · <date>` flags
   - `~~strike-through~~` items, and phrases like "fixed 2026-07-07", "since 2026-08-10", "previously …", "now …"
   Before deleting, **move each removed history note into `KB_CHANGELOG.md`** (create it, format in section 1.4). No information may be lost.
3. **Draft vs reviewed:** behaviour from the unreviewed period (the old "Draft" flags) is still written as a normal rule, but the module header lists it under `Pending PO review:` (section 1.3) so the PO can confirm it.
4. **Business language first.** Rules say what happens and why it matters. Class/method names may stay in *Database Understanding*, *APIs* and a final `## Evidence` list, not inside Business Rules sentences where avoidable.
5. **Keep the 13-heading template, in this order:** Module Overview · Business Workflow · Business Rules · Hidden Rules · Decision Tree · Database Understanding · State Machine · APIs · Edge Cases · Assumptions · TODOs · Open Questions · Evidence (new, last).
6. **Never renumber or delete IDs** (AB-, OD-, BL-, HL-, DC-). Closed items change status.
7. **Do not invent business decisions.** If a fix needs a decision, add it to Open Questions and to the report; do not decide it.
8. Keep tables well-formed: every row must have the same number of cells as the header.

---

## 1. Structure and tooling (do first)

### 1.1 Source of truth and generated file
- **The per-module files (`00-…` to `10-…`) are the only files anyone edits.**
- `BUSINESS_KNOWLEDGE_BASE.md` becomes a **generated** file: concatenation of the module files, with a "generated, do not edit" banner. It is the file uploaded to the Claude project for search.
- Add `scripts/build-kb.sh`:

```bash
#!/usr/bin/env bash
# Regenerates BUSINESS_KNOWLEDGE_BASE.md from the per-module KB files.
# Do not edit the output by hand. Edit the module files, then run this script.
set -euo pipefail
KB_DIR="${1:-<KB_DIR>}"            # set to the folder holding 00-…md to 10-…md
OUT="${2:-BUSINESS_KNOWLEDGE_BASE.md}"
{
  echo "<!-- GENERATED by scripts/build-kb.sh from ${KB_DIR}/ — DO NOT EDIT BY HAND -->"
  echo
  echo "# Dinex — Business Knowledge Base (combined)"
  echo
  echo "> Generated from the per-module files listed in ${KB_DIR}/README.md. Edit those files, not this one."
  for f in "${KB_DIR}"/[0-9][0-9]-*.md; do
    echo; echo "---"; echo
    cat "$f"
  done
} > "$OUT"
echo "Wrote $OUT"
```

- Replace `<KB_DIR>` with the real folder (see the README location). Do not put dates or commit hashes into the generated output (keeps the diff check stable; versions live in each module header).
- Add a check (pre-commit hook or CI step): `scripts/build-kb.sh && git diff --exit-code BUSINESS_KNOWLEDGE_BASE.md`. It fails if someone edited the combined file by hand or forgot to regenerate.
- Update `CLAUDE.md` → "Knowledge Base Maintenance": edit module files only; run `scripts/build-kb.sh`; add a `KB_CHANGELOG.md` entry; never edit the generated file.

### 1.2 README fixes (`README.md` in the KB folder)
- **R-01** Find: `SPRINT_2_UPDATES_DRAFT_2026-09-08.md`. The file that exists is `SPRINT_1_UPDATES_DRAFT_2026-09-17.md`. Rename the file to `SPRINT_2_UPDATES_DRAFT_2026-09-17.md` (it covers 2026-08-25 → 2026-09-17, i.e. after Sprint 1) and update every reference in the repo.
- **R-02** Find: `kept at the repo root as a combined historical reference`. Replace with: the combined file is generated by `scripts/build-kb.sh`, is the search copy, and must not be edited.
- **R-03** Add columns to the index table: `Status` (Reviewed / Pending PO review) and `Code baseline` (commit).
- **R-04** Add links to `KB_CHANGELOG.md`, `ISSUES_TRACKER.md`, and the sprint registers.

### 1.3 Standard header for every module file
Replace the current `**Last updated:** … **Source:** Split from …` lines with:

```markdown
# Module N — <Name>

> **KB version:** 2.0 · **Code baseline:** <short commit at HEAD> · **Last reviewed:** <YYYY-MM-DD>
> **Epic:** EP-N · **Status:** Reviewed | Pending PO review
> **Pending PO review:** <bullet list of rules that came from the unreviewed Draft period, or "none">
> **Related flags:** AB-xx, … · **Open decisions:** OD-xx, …
```

### 1.4 `KB_CHANGELOG.md` format (new file, newest first)

```markdown
## 2026-09-17 — Draft period (2026-08-25 → 2026-09-17)
- M08: Webhook handler reads `order_nr` (previously read non-existent `mp_task_nr`; all webhooks were dropped).
- …
## 2026-08-25 — Sprint 1
- …
## 2026-07-07 … 2026-07-15 — Review cycle fixes
- …
```
Every history note removed from a module file lands here with its date and module number.

---

## 2. P0 — Correctness points to verify in code (report findings before editing docs)

| ID | Issue | Verify in code | Action |
|---|---|---|---|
| **V-01** | **Possible day-close deadlock.** The day-close guard added in the Draft period refuses the close while any in-scope order has external data with `is_delivered = 0`, "regardless of financial status". Rejected orders, orders cancelled via `POST /pos/orders/mobile/cancel`, and cancelled delivery orders never get `is_delivered = 1`. If the guard does not exclude `cancelled`, one rejected mobile order blocks the branch's day close with no override. | Day-close service / `DailySalesRepository`: the query behind "There are many mobile orders that have not been marked as delivered" | Report the exact condition. If cancelled orders are not excluded, add `BL-12` to `ISSUES_TRACKER.md` (🔴 OPEN) and document the actual behaviour in 06. Do **not** change code unless the PO asks. |
| **V-02** | `POST /pos/orders/mobile/cancel`: does it (a) recall a Noon courier if already dispatched, (b) respect the closed-day freeze, (c) write the status activity log, (d) fire the platform callback? | Handler of `mobile/cancel` in `OrderController` / `OrderService` | Document each answer in 05 Business Rules + Edge Cases. |
| **V-03** | Manual handover (`deliverExternalOrder`) for Noon **delivery** orders: a PO decision says it is disabled. Is it disabled in code? | `OrderService::deliverExternalOrder`, `OrderController::deliverExternalOrder` | Document actual behaviour in 05. If not enforced, report "decision not implemented". |
| **V-04** | `external_orders_data.seen_at` column: decided by PO. Does the migration exist? What writes it? | migrations, `change-is-seen-external-order` handler | Document in 05 Database Understanding, or list under Open Questions as "decided, not built". |
| **V-05** | Invoice PDF: rendered **inside** the order-create transaction (KB + `HL-2` OPEN) or **after** commit (As-Built Spec claims post-commit printing)? | `OrderService::createOrder` / `orderUpdate` | Write the truth in 04 (workflow steps 12–13, Edge Cases). Update `HL-2` status in `ISSUES_TRACKER.md`. |
| **V-06** | Auto-deliver on Noon `delivered` webhook reuses the inbound webhook `Request()` object (mutated with `->id`). | `ChangeOrderStatusNoonWebhookJob` / `changeOrderStatusNoonWebHook` | Confirm; add a `DC-2` (or `BL-13`) DEBT entry in `ISSUES_TRACKER.md`; keep one neutral sentence in 08 Hidden Rules. |
| **V-07** | Ingredient creation from platform: an unknown unit code produces an undefined-array-key warning and may store a null unit; no field validation. | Handler of the ingredient-creation route (confirm the route path — KB says `POST /pos/ingredient/cc/ingredient-creation`) | Confirm and record the risk in 03 + 10 risk register. |

---

## 3. P1 — Contradictions and stale text, per file

Legend: **Find** = grep string (short, from the current text). **Fix** = what the text must say. **Verify** = check code before writing.

### 00-system-context.md
- **C00-01** Core business flows list. Add: mobile app orders by SKU; platform can create items/ingredients and cancel its own orders; Noon `delivered` status auto-completes handover; a running queue worker (`default` and `noon` queues) is a production dependency.
- **C00-02** Add a one-line pointer to each module file and to `KB_CHANGELOG.md`.

### 01-authentication.md
- **C01-01** Find: `so platform orders are attributed to that cashier and that cashier's branch rules apply`. Fix: platform orders are attributed to the related user, but the **branch is taken from the request's `branch_slug`** (see 02). Cross-reference 02.
- **C01-02** Hidden Rules, Find: `CustomRoleMiddleware`. Move the substance into Business Rules as a timeless rule: "Role and permission gates also apply to platform-authenticated routes." History → changelog.
- **C01-03** APIs table, row `listWithUsers`, Find: `branches (id, name, display_name) + users`. Fix: `+ branch staff only (admin/superadmin excluded)`.
- **C01-04** **Verify** which role/permission gates protect `items-creation`, the ingredient-creation route, `mobile/cancel`, `check-mapper-slugs`. Add them to the permission rules.

### 02-branches.md
- **C02-01** Entry Points, Find: `(⚠ unauthenticated — outside the auth group)`. Fix: admin/superadmin token required.
- **C02-02** Business Workflow step 3 (BranchScope). Rewrite with the modes: explicit branch id (platform path) applies to everyone; admin/superadmin unscoped by design; other users scoped to `users.branch_id`; users with no branch unscoped.
- **C02-03** Decision Tree, Find: `IF request has 'branch' (set by auth middleware)`. Replace with the multi-mode tree above.
- **C02-04** Hidden Rules, Find: `the unauthenticated test endpoints see ALL`. Fix: console jobs, queued jobs and webhooks see all branches; admin/superadmin sessions see all branches by design. (Test endpoints are now authenticated.)
- **C02-05** **Verify** `UpdateBranchSetingRequest`: validation commented out and nulls overwrite (KB) vs partial validated update (As-Built Spec). Write the truth in Business Rules, APIs table (`Validation` cell) and Edge Cases.
- **C02-06** TODOs: remove the struck `Add auth to the dashboard branch list` (→ changelog).

### 03-menu-catalog.md
- **C03-01** Module Overview, Find: ``platforms (reference items by `mapper_slug`)``. Fix: platforms order items by `sku`; `mapper_slug` is only the SKU-sync matching key and the MYOM placeholder key.
- **C03-02** Entry Points: add `POST /pos/items/mobile/check-mapper-slugs`, `POST /pos/items/mobile/items-creation`, the ingredient-creation route (**Verify** exact path).
- **C03-03** **Broken table.** APIs (summary) has 4 columns (`Endpoint | Purpose | Key validation | Side effects`) but the three 🚩 rows have 7 cells. Rewrite those rows to 4 cells.
- **C03-04** Items-creation: one consistent statement — the category is resolved by `categories.code` (not `slug`). Remove remaining `category_slug` mentions except in the changelog.
- **C03-05** MYOM: keep in 03 only the catalog facts (10 placeholder items `myom-1…10`, price 0, category 49, seeded by `TempMYOMMapping`, must not be deleted/renumbered). Move the ordering behaviour (platform price/name, `custom_name`, group numbering, >10 rejected) to **05**, and cross-reference.
- **C03-06** Ingredients: units `{pcs, gram, kg, ml, liter}`; `ingredients.sku` nullable and **not unique** (state as rule + risk). Add the unit remap for platform codes (`gm→gram`, `ltr→liter`).
- **C03-07** Business Rules → Item fields: make `sku` and `preparation_time` normal timeless bullets (remove flags).

### 04-order-management.md
- **C04-01** Workflow step 4, Find: ``rejects the request unless `total_price` matches exactly``. Fix: matches within ±0.01 (numeric). Add exception: MYOM lines from the mobile app carry their own price (see 05).
- **C04-02** Order update workflow, Find: `a **new** discount row is added`. Fix: the existing discount is deleted and replaced; updating without a discount clears it. (Matches Hidden Rules.)
- **C04-03** Workflow: add the missing steps in order: (a) order-number allocation under row lock; (b) preparation-time estimate computed and stored (`orders.preparation_time`); (c) status activity log written by `OrderObserver` (2 rows at create, 1 per change).
- **C04-04** Workflow steps 12–13 + Edge Case `PDF generation failure … rolls back the whole order`: rewrite per **V-05** result.
- **C04-05** Status workflows: add that platform callbacks, Noon dispatch and Noon recall run as **queued jobs after commit** (3 tries, 30/60/120 s), and that setting a Noon-dispatched order to `cancelled` recalls the courier unless the tracker is already `cancelled`/`delivered`. **Verify** whether *reject* of an already-dispatched order also recalls (KB versions disagree).
- **C04-06** `pending` → `ready` everywhere in 04:
  - Listing, Find: `operation_status ∈ {inProgress, pending}` → `{inProgress, ready}`
  - APIs, Find: `inprogress + pending buckets` → `inProgress + ready buckets`
  - Preparation section, Find: ``latest `inProgress → pending` kitchen transition`` → `inProgress → ready`
  - Keep one note: the kitchen-status API still accepts `pending` as an input alias for `ready`.
- **C04-07** Decision Tree: (a) `after := max(0, net − discount_value)`; (b) MYOM line pricing (`unit price := line.price`, `custom_name := line.name`); (c) prep-time computation; (d) observer rows after commit.
- **C04-08** Edge Cases: remove the `in:` string-comparison bullet (fixed) and the struck race bullet (→ changelog).
- **C04-09** Database Understanding → `orders`: add `preparation_time` (minutes, nullable). → `order_items`: add `custom_name`. → `active_order_status_activity_logs`: add the TIME-column limit (> 24 h misreports) and `updated_by` null for queue writes.
- **C04-10** **Verify** that the POS **update** path re-reads catalog prices and drops `custom_name` for MYOM lines. Document the result.
- **C04-11** TODOs: move all struck items to changelog. Keep open: `is_subscription` dormant; legacy dead code; order edits on day-closed orders not guarded (**add to Open Questions:** "Is the unguarded edit acceptable because the POS only edits active orders? PO decision needed").
- **C04-12** Business Rules: add the discount floor as a rule (subtotal, VAT, total floored at 0; entered discount value kept, so reported discount may exceed sales).

### 05-external-platform-orders.md (largest drift — do carefully)
- **C05-01** Workflow step 1, Find: ``items by `slug` (= item `mapper_slug`) + qty``. Fix: items by `sku` + qty; lines with a `group` are MYOM lines carrying `price` and `name`; plus `package_name`, `points`, `payed_with_loyalty_points`, delivery instructions.
- **C05-02** Workflow step 2, Find: `items exist by mapper_slug`. Fix: each non-MYOM line's `sku` exists; MYOM lines must carry `price` and `name`.
- **C05-03** Business Rules, Find: `No item found with slug`. Fix: `No item found with SKU: …`.
- **C05-04** Add a full **MYOM** rule block (moved from 03): placeholder `myom-N` by `mapper_slug`, N = composed-meal count within the group; platform supplies price and name; stored as `"<placeholder>-<line name>"` in `order_items.custom_name`; the server-side price check does **not** apply to these lines; >10 meals in a group rejects the order; temporary mapping.
- **C05-05** Business Rules, Find: `failures only logged (fire-and-forget — **no retry**)`. Fix: queued job, 3 tries with 30/60/120 s backoff, permanent failure logged; lost after retries or if no worker runs.
- **C05-06** Routing: `platform_payment_type` types are `branch-order`, `branch-plan`, `loyalty-points`; `payed_with_loyalty_points = true` overrides the type to `loyalty-points`.
- **C05-07** `pending` → `ready`, Find: ``which only shows `inProgress`/`pending` ``.
- **C05-08** TODOs, Find: ``Point callbacks at `live_url` per environment.`` Remove (done) → changelog.
- **C05-09** State Machine, Find: `Platforms can never change status directly.` **This is now false.** Add the transition `any → cancelled/cancelled` via `mobile/cancel` (platform), with the V-02 findings. Also add auto-handover: Noon `delivered` → `is_delivered = 1` + platform "completed" callback.
- **C05-10** Entry Points: add `POST /pos/orders/change-is-seen-external-order`, `POST /pos/orders/mobile/cancel`.
- **C05-11** Decision Tree: add loyalty override, SKU lookup, MYOM branch, platform cancel path, auto-handover from Noon.
- **C05-12** Database Understanding → `external_orders_data`: JSON fields now include `plan`, `delivery_instructions`, `type`, `points`; columns `is_delivered`, `is_seen`, and `seen_at` (per V-04).
- **C05-13** **Verify** dedup: validation-only (KB) vs DB unique `orders.external_dedup_key` (As-Built Spec). Write the truth in Business Rules and TODOs.
- **C05-14** Business Rules: add the rule from V-03 (manual handover for Noon delivery orders).
- **C05-15** Edge Cases: add `mobile/cancel` silent `false` when the order is not found (platform gets no error detail).
- **C05-16** Open Questions: add "Should a platform cancel of an already-dispatched delivery recall the courier?" (unless V-02 shows it does) and "What happens to a paid mobile order when Noon cancels the delivery? (no refund concept)".

### 06-day-close-dsr.md
- **C06-01** Renumber workflow steps (`2`, `2b`, `2c` → clean sequence). Present the three guards as one list: (1) pending `inReview` orders — not overridable; (2) external orders not delivered — not overridable (with V-01 result); (3) unpaid active orders — overridable by `close_orders`.
- **C06-02** TODOs, Find: `Branch-filter the open-at query (cross-branch window bug).` Remove (already hardened, see Hidden Rules) → changelog.
- **C06-03** **Verify** concurrency, Find: `Two cashiers closing simultaneously → double DSR rows and double stamping (no lock).` The As-Built Spec claims a lock with "The day has already been closed". Write the truth.
- **C06-04** **Verify** the `close_orders` bypass (`close_orders=0` also bypasses). If still true, keep as Hidden Rule + risk; if fixed, move to changelog.
- **C06-05** Open Questions: keep the escape-hatch question for the non-overridable delivered guard; link to V-01.

### 07-reports-charts-settings.md
- **C07-01** Workflow step 2, Find: `today's not-yet-closed orders are additionally included`. Replace with the reworked rule: `day` = business day (related_date today + the still-open day); the open day (identified by `closed_at IS NULL`, `day_closed = 0`, `related_date IS NULL`) is added to **every** period including custom, only for branches whose open day's opening order falls in the window.
- **C07-02** Decision Tree: replace the old `created today` clause with the rule above.
- **C07-03** Edge Cases, Find: `custom periods miss today's live orders`. Remove/rewrite (open day now included in custom ranges).
- **C07-04** Workflow step 4: add top-selling **item** to the charts payload; add Order History report and invoice ZIP as workflow step 5.
- **C07-05** Hidden Rules, Find: `` `ReportsRepository2` is an older unused copy (kept in repo).`` and TODO ``Delete `ReportsRepository2`.`` Both obsolete (deleted, DC-1) → changelog.
- **C07-06** **Verify** `totalSales` KPI (still the delivered count?). **Verify** whether a shared `ReportPeriodRequest` validates all reports or only Order History. Update Hidden Rules, TODOs, Edge Cases (`Custom period without dates…`, `Branch filter values are raw request input…`).
- **C07-07** **Verify** top branch rule, Find: `excludes branch_id 0`.
- **C07-08** APIs table: add 422 validation errors for Order History; mark the ZIP as public-disk + customer PII.

### 08-noon-delivery.md
- **C08-01** Entry Points, Find: `(⚠ unauthenticated)`. Fix: staff token required (`auth_api`).
- **C08-02** Workflow step 1: dispatch runs in a queued job (`SendOrderToNoonJob`, queue `noon`) after commit.
- **C08-03** Decision Tree (dispatch), Find: `ELSE (failed) → log; NO tracker row`. Fix: any non-success response stores a tracker with status `error` and `error_msg` parsed from Noon's body.
- **C08-04** Decision Tree (webhook): `mp_task_nr` → `order_nr`; `local/testing` → `local/test`; regression rejection stays.
- **C08-05** State Machine, Find: `Trigger: Noon webhooks only (cashier polling does not update state`. Fix: polling also syncs the tracker; `delivered` also triggers auto-handover (05).
- **C08-06** Business Rules: add "status regressions are rejected; `cancelled` is terminal" (currently says only duplicates are ignored).
- **C08-07** **Verify** the dispatch `catch` block, Find: ``the `catch` returns an undefined `$response` `` vs `BL-9(b)` FIXED. Keep one truth; update TODOs (`Fix undefined $response in the catch — still open`) accordingly.
- **C08-08** **Verify** recall scope, Find: `wired into the reject/cancel flow`. An earlier KB version said cancel only, not reject. Keep one truth (same as C04-05).
- **C08-09** APIs table, row `GET /api/v1/test/noon-integration/task/{ref}`, Find: `none (sync is dead code)` → polling syncs.
- **C08-10** Clarify identifiers once: `provider_reference_number` stores Noon's `mp_task_nr` from create-task; webhooks send it as `order_nr`. **Verify** this mapping.
- **C08-11** Hidden Rules: add V-06 (request-object reuse) as one neutral sentence; details go to the Issues Tracker.

### 09-commercial-config.md (not updated since 2026-07-07)
- **C09-01** Hidden Rules, Find: ``A promo code of type `amount` larger than the order net produces a **negative subtotal** (no floor).`` Fix: subtotal floored at 0; entered discount value kept.
- **C09-02** Decision Tree, Find: `(may exceed net; no clamp)`. Fix: `subtotal floored at 0`.
- **C09-03** **Verify** payment-type delete: FK restrict → raw 500 (KB) vs usage pre-check → 409 (As-Built Spec).
- **C09-04** Business Rules: seeded external tenders are three: "Asparagus Mobile App" (`branch-order`), "Branch plan" (`branch-plan`), "Loyalty Points" (`loyalty-points`); the platform seeder uses `firstOrCreate` for the platform (credentials not rotated on re-seed).
- **C09-05** TODOs, Find: `Discount cap / negative-total guard.` → `Discount cap` only (negative total solved).

### 10-cross-cutting-reference.md
- **C10-01** Glossary, Find: `Sub total + mobile delivery fee + VAT`. Fix: `Final price = Sub total + VAT` (delivery fee is already inside net/sub total).
- **C10-02** Glossary: separate the two "make your own meal" concepts — POS custom-item flow (`is_custom`) vs mobile MYOM placeholders. Add terms: Loyalty points order, Seen/unseen, `day` period (business day).
- **C10-03** Domain Dictionary updates:
  - `ingredients` — units pcs/gram/kg/ml/liter; nullable non-unique `sku`.
  - `categories` — add `code` (platform identifier).
  - `items` — add `sku`, `preparation_time`.
  - `orders` — add `preparation_time`.
  - `order_items` — add `custom_name`.
  - `external_orders_data` — add plan, delivery instructions, routing type, points, `is_seen`, `seen_at` (per V-04).
  - `platform_payment_type` — types incl. `loyalty-points`.
  - `order_trackers` — add `error_msg`.
  - add `active_order_status_activity_logs`.
- **C10-04** Constants: split-payment tolerance applies to **create and update** (Find: `| Split-payment tolerance | 0.01 | orderUpdate |`); add item `preparation_time` bounds 1–1440; MYOM placeholders (10); Order History page size (default 25, max 500).
- **C10-05** Feature flags: add `items.sku`, `external_orders_data.is_seen`, mobile line key `group`, `payed_with_loyalty_points`.
- **C10-06** Environment table, Find: ``bypassed in `local`/`testing` ``. Fix: `local`/`test`. Add a row: queue workers (`default`, `noon`) required in production.
- **C10-07** Risk register: remove struck items (→ changelog); remove `ReportsRepository2` from item 12; **verify** item 5 (`close_orders` bypass). Add:
  - queue worker is a hard production dependency;
  - MYOM lines priced by the platform;
  - `items-creation` overwrites live items by SKU without review;
  - Order History invoice ZIPs public with customer PII;
  - day-close deadlock (V-01), if confirmed;
  - `ingredients.sku` not unique; ingredient-creation unit handling (V-07);
  - `mobile/cancel` silent false;
  - auto-deliver request-object reuse (V-06);
  - `add_custom_name_to_order_items_table.php` has no timestamp prefix.
- **C10-08** Consolidated Open Questions: remove #6 (resolved) → changelog. Align numbering with the As-Built Spec OD register (OD-01…OD-16) and add:
  - OD-17 Loyalty-points balance owner and reconciliation.
  - OD-18 MYOM traceability and price validation.
  - OD-19 May the platform create/overwrite live items without review?
  - OD-20 Is the prep-time formula (slowest × 1/2/2.5) the kitchen's real rule; chef override?
  - OD-21 Retention of status activity logs.
  - OD-22 Paid mobile order when Noon cancels the delivery (no refund concept).
  - OD-23 Escape hatch when a delivery order can never be marked delivered (day close blocked).

---

## 4. P1 — Other project files

- **ISSUES_TRACKER.md** (keep in repo and in the Claude project): add `BL-12` (V-01, if confirmed), `DC-2`/`BL-13` (V-06), update `HL-2` per V-05; `BL-6` stays OPEN. Rows are never deleted.
- **Sprint registers:** rename the draft register per R-01. Each entry that is now in the KB gets a line "Folded into KB module NN".
- **Decisions to record** (create `DECISION_LOG.md` if the PO wants it; otherwise list them in the report): kitchen status `pending` renamed `ready`; `seen_at` column; Noon `delivered` sets `is_delivered` automatically without touching `orders.status`/`operation_status`; manual handover disabled for Noon delivery orders; free live-day status transitions (AB-24); uncapped manual discounts (AB-31); fixed order-type IDs 1/2/3 (AB-57); delivery fee VAT-exclusive and discountable (BL-11).
- **DINEX_AS_BUILT_SPEC.docx** is **out of scope** for this pass. After the KB is done, produce `AS_BUILT_SPEC_CHANGES.md` listing each story/AC/AB that no longer matches the KB (for example US-5.01/5.02 slug→SKU; AB-23/33/34/35/50/51/55 now resolved; AB-32 and AB-49 narrowed; US-7.01 `day` period; new Sprint 1 and Draft-period stories).

---

## 5. P2 — Improvements (after P0/P1 are done)

- **I-01** Rule IDs: number each bullet in *Business Rules* and *Hidden Rules* as `M<module>-R<nn>` (e.g. `M05-R04`). IDs are permanent; new rules append.
- **I-02** Add `## Evidence` at the end of each module: one line per rule ID → `file::method` or migration name.
- **I-03** Every module's Open Questions references OD IDs instead of free text.
- **I-04** Each module's TODOs references Issues Tracker IDs where one exists.

---

## 6. Suggested order of work

1. Section 1 (structure, script, README, headers, changelog file).
2. Section 2 (verify V-01…V-07, write findings to the report).
3. Module 05, then 04, 08, 06, 09, 07, 02, 03, 01, 00, 10.
4. Section 4 files.
5. Run `scripts/build-kb.sh`; run the diff check.
6. Section 5 if time allows.

Commit per module (`docs(kb): M05 reconcile with code at <commit>`), so each change is reviewable.

---

## 7. Cross-file consistency checks (run at the end)

- `grep -rn "pending" <KB_DIR>` → in kitchen contexts only the alias note may remain.
- `grep -rn "mapper_slug" <KB_DIR>` → only SKU-sync matching and MYOM placeholders.
- `grep -rn "no retry\|fire-and-forget" <KB_DIR>` → none.
- `grep -rn "🚩\|~~\|fixed 20\|since 20\|Draft ·" <KB_DIR>` → none in module files (all in the changelog).
- `grep -rn "mp_task_nr" <KB_DIR>` → only the identifier-mapping explanation (C08-10).
- `grep -rn "testing" <KB_DIR>` → no environment-name references.
- Every table: header cell count equals row cell count.
- `README.md` index lists every module file, and every referenced file exists.

---

## 8. Definition of done

- [ ] Module files are the only edited sources; the combined file is generated and passes the diff check.
- [ ] All P0 verifications reported; all P1 items done or explicitly reported as blocked.
- [ ] No sprint flags, strike-throughs or dated fix notes inside module files; all history in `KB_CHANGELOG.md`.
- [ ] Every module has the standard header with the code baseline commit.
- [ ] Section 7 checks pass.
- [ ] Issues Tracker and sprint registers updated.
- [ ] Report delivered (section 9).

---

## 9. Report format (return this to the PO)

```markdown
# KB update report — <date>, code baseline <commit>

## Verifications (V-01…V-07)
| ID | Finding in code (file::method) | Doc change made | Needs PO decision? |

## Items done / blocked
| ID | Status (Done / Blocked / Changed approach) | Note |

## Code says something different from this brief
- <ID>: brief said X, code does Y, doc now says Y.

## New issues added to ISSUES_TRACKER.md
## Decisions the PO must take (new or confirmed ODs)
## As-Built Spec changes needed (summary; full list in AS_BUILT_SPEC_CHANGES.md)
```
