> ## Documentation Index
> Fetch the complete documentation index at: https://docs.highailabs.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Orders & Receipts — Behavior Spec

> Every behavior the Orders feature must have — importing a receipt, reviewing it, saving it, the effects that follow, and how each behavior is verified.

## What this feature is

A user turns a dispensary receipt into a record in the app. They paste or dictate the
receipt text, or photograph it, and an AI parse returns line items. They review that
parse — fix a strain name, pick a different match, add a missing strain, remove a line,
correct a price or amount, set the shop and purchase date, mark it a personal purchase —
and then save it one of three ways: stats only, plus stash, or plus stash and a notebook.
Saved orders live in an archive they can filter, search, open, edit, add to their stash,
and delete. Receipts that arrive from Gmail enter this same review screen once a draft
exists. A dev-only Order Import Lab seeds realistic historical receipts so the whole path
can be exercised without a mailbox.

<Note>
  This page is the contract, not a coverage report. Every line below is true of the code
  on `main` today and is enforced by the cited tests. Behaviors that are deliberately
  unproven live in [Deliberate gaps](#deliberate-gaps-conditions), never in the tables.
</Note>

## The behavior contract

### Importing a receipt by text

| Behavior                                                                                                                                                                                                                       | Verified by                                   | Tier | Where                                                               |
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------- | ---- | ------------------------------------------------------------------- |
| Parsing pasted receipt text requires an active Pro subscription, and the gate holds on every deployment including the deployed backend.                                                                                        | ORD-102, ORD-213                              | A, C | `orders.test.ts`, `orders.integration.test.ts`                      |
| Text shorter than 10 or longer than 50,000 characters is rejected on the device, before any paid parse runs.                                                                                                                   | ORD-103, ORD-147                              | A, B | `orders.test.ts`, `receiptUpload.featuretests.test.tsx`             |
| A parse that returns zero line items never becomes a draft — the user is told the receipt could not be read and stays on the input screen.                                                                                     | ORD-144                                       | B    | `receiptUpload.featuretests.test.tsx`                               |
| A parse failure returns a message the screen can render; the action never throws, and it resolves the API host from configuration, canonicalizing a retired one.                                                               | ORD-106, ORD-107                              | A    | `orders.featuretests.test.ts`, `orders.test.ts`                     |
| Retrying the same receipt reuses the same attempt id so one receipt is never billed twice, while editing the receipt first mints a new id that the deployed parser distinguishes.                                              | ORD-146, ORD-146a, ORD-146b, ORD-214, ORD-215 | B, C | `receiptUpload.featuretests.test.tsx`, `orders.integration.test.ts` |
| Tapping retry while a parse is still in flight does not consume one of the three allowed retries.                                                                                                                              | ORD-145                                       | B    | `receiptUpload.featuretests.test.tsx`                               |
| A cancelled-order email must never become an order — every item is dropped when the source says the purchase was cancelled, while a confirmation that merely offers cancellation is kept.                                      | ORD-231, `rejectCancelledPurchase`            | C, A | `orders.integration.test.ts`, `receipt-parser.test.ts`              |
| An extraction carrying no price, subtotal, or total anywhere is emptied rather than presented as a free order; a zero price is not evidence of money; and extraction runs at temperature 0 so two parses of one receipt agree. | `enforceReceiptMonetaryEvidence`, `BUG-051`   | A    | `receipt-parser.test.ts`                                            |

### Importing a receipt by photo

| Behavior                                                                                                                                                          | Verified by          | Tier | Where                                                                |
| ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------- | ---- | -------------------------------------------------------------------- |
| Photo parsing is Pro-gated and locally validated on the same terms as text, including the paid-path header the text lane sends.                                   | ORD-104              | A    | `orders.featuretests.test.ts`                                        |
| A photo that parses to zero line items is rejected instead of pushing the user into an empty review screen.                                                       | ORD-143              | B    | `receiptUpload.featuretests.test.tsx`                                |
| An image above the 6 MiB client ceiling is rejected before the action, and the real edge refuses an oversized upload with a shaped, user-readable failure.        | ORD-147, ORD-216     | B, C | `receiptUpload.featuretests.test.tsx`, `orders.integration.test.ts`  |
| Omitting the media type produces an accurate message, and a failed picker resize falls back without misdeclaring the media type.                                  | ORD-105, ORD-148     | A, B | `orders.featuretests.test.ts`, `receiptUpload.featuretests.test.tsx` |
| A real JPEG carrying trailing camera metadata is accepted at its true media type, and a HEIC photo is refused with a clear error before a provider call is spent. | `BUG-052`, `BUG-053` | A    | `receipt-parser.test.ts`                                             |

### Reviewing and editing before save

| Behavior                                                                                                                                                                                      | Verified by               | Tier    | Where                                                                                               |
| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------- | ------- | --------------------------------------------------------------------------------------------------- |
| Every review action — edit name, pick an alternative match, add a missing strain, remove and restore, edit price and amount, decide cannabis for an unknown name — is handled by the reducer. | ORD-159                   | B       | `reviewItemsReducer.test.ts`                                                                        |
| Editing the product type can only ever produce one of the six supported literals.                                                                                                             | ORD-160                   | B       | `reviewItemsReducer.featuretests.test.ts`                                                           |
| Retrying strain matching sends the strain names the backend expects and needs only sign-in, not Pro.                                                                                          | ORD-136, ORD-100, ORD-217 | B, A, C | `useOrderReview.featuretests.test.tsx`, `orders.featuretests.test.ts`, `orders.integration.test.ts` |
| A cold deep link into review re-initializes its items once the signed-in user settles, and never renders an empty item list.                                                                  | ORD-135, ORD-165          | B       | `useOrderReview.featuretests.test.tsx`, `OrdersScreens.test.tsx`                                    |
| An expired draft and a missing draft show different copy.                                                                                                                                     | ORD-137                   | B       | `useOrderReview.featuretests.test.tsx`                                                              |
| The total-amount field rejects an invalid or over-cap entry visibly rather than silently discarding the keystroke.                                                                            | ORD-158                   | B       | `OrdersScreens.test.tsx`                                                                            |
| The payload the review screen builds satisfies the commit validator field for field, on the in-memory backend and on the deployed one.                                                        | ORD-138, ORD-200          | B, C    | `orderCommitPayload.featuretests.test.ts`, `orders.integration.test.ts`                             |
| Strain names are normalized and length-capped on the way in, and an item can never be marked as matched without a strain id.                                                                  | ORD-026, ORD-027          | A       | `orders.featuretests.test.ts`                                                                       |

### Saving an order

| Behavior                                                                                                                                                                                                                                                                     | Verified by                                                            | Tier    | Where                                                                                                             |
| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------- | ------- | ----------------------------------------------------------------------------------------------------------------- |
| The three save variants map to the correct add-to-stash and generate-notebook pair.                                                                                                                                                                                          | ORD-139                                                                | B       | `useOrderSaveFlow.featuretests.test.tsx`                                                                          |
| One save keeps one idempotency key across every retry, so a user tapping save twice gets one order — including two clients racing the deployed backend — and that key is scoped to its own user.                                                                             | ORD-140, ORD-005, ORD-006, ORD-071, ORD-202, ORD-230                   | B, A, C | `useOrderSaveFlow.featuretests.test.tsx`, `orders.test.ts`, `orderPostSave.test.ts`, `orders.integration.test.ts` |
| Replaying a key with a different immutable payload is rejected rather than silently overwriting, live as well as locally.                                                                                                                                                    | ORD-072, ORD-203                                                       | A, C    | `orderPostSave.test.ts`, `orders.integration.test.ts`                                                             |
| A saved order is `confirmed` and counts toward the user's stats immediately, without waiting for its effects.                                                                                                                                                                | ORD-070                                                                | A       | `orders.featuretests.test.ts`                                                                                     |
| A free user who asks for a notebook still gets the order — without one.                                                                                                                                                                                                      | ORD-074                                                                | A       | `orderPostSave.test.ts`                                                                                           |
| A cold remount after save recovers the outcome instead of re-committing, and the deployed backend reports that outcome.                                                                                                                                                      | ORD-141, ORD-201                                                       | B, C    | `useOrderSaveFlow.featuretests.test.tsx`, `orders.integration.test.ts`                                            |
| Waiting for a notebook shell reads, never writes, and stops after 8 attempts.                                                                                                                                                                                                | ORD-142                                                                | B       | `useOrderSaveFlow.featuretests.test.tsx`                                                                          |
| An order persists its items and computes a total, and a receipt-stated total wins over the item sum so tax and fees survive.                                                                                                                                                 | ORD-001, ORD-022, ORD-023                                              | A       | `orders.test.ts`, `order-confirm.test.ts`                                                                         |
| Every receipt field is bounded before it is stored: at least one item and no more than the item cap; finite, positive, under-cap quantities; no negative price or total; an integer purchase date inside the supported window; an http(s) receipt URL within its length cap. | ORD-002, ORD-003, ORD-018, ORD-019, ORD-020, ORD-021, ORD-024, ORD-025 | A       | `orders.featuretests.test.ts`, `order-confirm.test.ts`                                                            |
| All six product types and all ten stash units round-trip, an item with no product type commits as flower, and an unrecognised product type is refused by the validator.                                                                                                      | ORD-009 – ORD-017                                                      | A       | `order-confirm.test.ts`, `orders.featuretests.test.ts`                                                            |
| Saving with a shop name creates or revives that dispensary, a personal purchase creates none, duplicate shop identities never mint a competing key, and a dispensary id belonging to someone else is dropped rather than throwing the save away.                             | ORD-117, ORD-118, ORD-119, ORD-008                                     | A       | `orderPostSave.test.ts`, `orders.featuretests.test.ts`                                                            |

### What happens after a save — the effect chain

| Behavior                                                                                                                                                                                                                                             | Verified by                                          | Tier | Where                                                                                |
| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------- | ---- | ------------------------------------------------------------------------------------ |
| Effects are reserved and drained in one order: dispensary visit, then stash, then strain research, then notebook — and effects the user did not ask for are never reserved.                                                                          | ORD-076, ORD-077, ORD-209                            | A, C | `orders.featuretests.test.ts`, `orderPostSave.test.ts`, `orders.integration.test.ts` |
| A dispensary visit is counted exactly once across worker retries, and is skipped when the shop was deleted.                                                                                                                                          | ORD-078, ORD-079, ORD-205                            | A, C | `orderPostSave.test.ts`, `orders.featuretests.test.ts`, `orders.integration.test.ts` |
| Resolved lines project into the stash exactly once with a single audit event, and a twelve-line order schedules one projection rather than one per line.                                                                                             | ORD-081, ORD-069b, ORD-204                           | A, C | `orderPostSave.test.ts`, `orders.featuretests.test.ts`, `orders.integration.test.ts` |
| Two receipt lines that resolve to the same stash identity accumulate into one jar.                                                                                                                                                                   | ORD-082                                              | A    | `orders.featuretests.test.ts`                                                        |
| Strain research is reserved only for unresolved lines the user confirmed as cannabis, bound to the exact item and never re-matched by name; every terminal state reconciles idempotently and a late resolution re-projects that item into the stash. | ORD-083, ORD-084, ORD-085, ORD-086, ORD-087, ORD-207 | A, C | `orderPostSave.test.ts`, `orders.integration.test.ts`                                |
| A notebook shell is created before the readiness gate is evaluated, readiness degrades past the two-hour budget, a shell the user deleted cancels the effect, and a subject above the cap is normalized away rather than failing the save.           | ORD-088, ORD-089, ORD-090, ORD-075                   | A    | `orders.featuretests.part2.test.ts`, `orders.featuretests.test.ts`                   |
| A stale finalizer cannot overwrite a newer lease, a failed effect surfaces as attention and retries to completion, and an effect whose order was deleted mid-flight is skipped rather than retried forever.                                          | ORD-092, ORD-093, ORD-080                            | A    | `orderPostSave.test.ts`, `orders.featuretests.test.ts`                               |
| The reconciliation cron picks up due and lease-expired effects, taking at most 100 per status.                                                                                                                                                       | ORD-095, ORD-096                                     | A    | `orders.featuretests.test.ts`                                                        |
| Saving an order moves the user's achievement metrics on the deployed backend.                                                                                                                                                                        | ORD-206                                              | C    | `orders.integration.test.ts`                                                         |
| A Gmail-sourced order's source is decided from the owned import job, not from what the client sent.                                                                                                                                                  | ORD-120, ORD-225                                     | A, C | `orders.featuretests.test.ts`, `gmail.integration.test.ts`                           |

### Browsing and searching the order library

| Behavior                                                                                                                                                         | Verified by                         | Tier | Where                                                                         |
| ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------- | ---- | ----------------------------------------------------------------------------- |
| The library returns only the signed-in user's orders.                                                                                                            | ORD-028, ORD-221                    | A, C | `orders.test.ts`, `orders.integration.test.ts`                                |
| Pagination fills sparse filtered pages without skipping an eligible order, holds over a large live archive, and the recent-orders list honours its limit bounds. | ORD-029, ORD-039, ORD-219           | A, C | `orders.test.ts`, `orders.featuretests.test.ts`, `orders.integration.test.ts` |
| Contradictory status and review filters return an empty result rather than ignoring one of them.                                                                 | ORD-030                             | A    | `orders.test.ts`                                                              |
| The end-date filter actually filters, and each filter facet selects its intended index.                                                                          | ORD-031, ORD-033                    | A    | `orders.featuretests.test.ts`                                                 |
| Search returns at most 100 matches and says so rather than implying a total.                                                                                     | ORD-034, ORD-220                    | A, C | `orders.featuretests.test.ts`, `orders.integration.test.ts`                   |
| An empty search is rejected and a long one is clamped by one rule the client and server share, so a query the screen accepts is a query the backend accepts.     | ORD-035, ORD-036, ORD-036b, ORD-156 | A, B | `orders.featuretests.test.ts`, `OrdersScreens.test.tsx`                       |
| The library hook sends exactly the arguments the backend validates, skips every subscription while signed out, and switches to search on non-empty trimmed text. | ORD-130, ORD-131, ORD-132           | B    | `useOrders.featuretests.test.tsx`                                             |
| The library screen renders loading, empty, search-empty, error, and data, and requests one more page per end-reached.                                            | ORD-154, ORD-155                    | B    | `OrdersScreens.test.tsx`                                                      |

### Opening and editing a saved order

| Behavior                                                                                                                                                                                                                             | Verified by                                          | Tier    | Where                                                                                                                          |
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------ |
| Opening an order returns it with its items; a missing or foreign order throws rather than leaking.                                                                                                                                   | ORD-037, ORD-038                                     | A       | `orders.test.ts`                                                                                                               |
| Order detail shows a loading state while auth settles instead of "order not found", skips entirely when no order id was passed, and renders loading, empty, error, and data.                                                         | ORD-133, ORD-134, ORD-153                            | B       | `useOrders.featuretests.test.tsx`, `OrdersScreens.test.tsx`                                                                    |
| Editing shop, date, total, and items is atomic across the order and its items, round-trips on the deployed backend, and rejects duplicate item ids, a foreign item, or more than 100 item updates with no partial write.             | ORD-049, ORD-050, ORD-051, ORD-052, ORD-152, ORD-224 | A, B, C | `orders.test.ts`, `orders.featuretests.test.ts`, `useOrderDetailManagement.featuretests.test.ts`, `orders.integration.test.ts` |
| An edit cannot change an item's strain identity.                                                                                                                                                                                     | ORD-053                                              | A       | `orders.featuretests.test.ts`                                                                                                  |
| Renaming the shop detaches a stale managed-shop link.                                                                                                                                                                                | ORD-048                                              | A       | `orders.test.ts`                                                                                                               |
| Editing an item price recomputes the order total by delta, a non-price edit leaves the total alone, invalid numerics are refused, adding an item enforces the cap and adds its price, and removing one subtracts and floors at zero. | ORD-054, ORD-055, ORD-056, ORD-057, ORD-058          | A       | `orders.test.ts`, `orders.featuretests.test.ts`                                                                                |
| Item mutations reject an item owned by someone else, checking both the item and its parent order.                                                                                                                                    | ORD-059                                              | A       | `orders.featuretests.test.ts`                                                                                                  |
| The delete confirmation clears its loading state on success rather than spinning forever.                                                                                                                                            | ORD-151                                              | B       | `useOrderDetailManagement.featuretests.test.ts`                                                                                |

### Adding an order's items to the stash

| Behavior                                                                                                           | Verified by      | Tier | Where                                       |
| ------------------------------------------------------------------------------------------------------------------ | ---------------- | ---- | ------------------------------------------- |
| While the stash is still loading, the screen shows loading rather than an add button that could double-add.        | ORD-149          | B    | `useOrderDetailStash.featuretests.test.tsx` |
| Add-to-stash sends the plan the bulk mutation expects, tagged as an order upload.                                  | ORD-150          | B    | `useOrderDetailStash.featuretests.test.tsx` |
| Merging a single unmatched item is refused outright; a bulk merge merges what is matched and skips what is not.    | ORD-097, ORD-098 | A    | `orders.featuretests.test.ts`               |
| Completing an order marks every item stash-requested and projects once, leaving unmatched items pending for later. | ORD-067, ORD-068 | A    | `orders.featuretests.test.ts`               |

### Deleting an order

| Behavior                                                                                                                                             | Verified by               | Tier | Where                                                                    |
| ---------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------- | ---- | ------------------------------------------------------------------------ |
| Deleting an order deletes its items, cascades its durable attempt and pending effects, and decrements the achievement metrics the order contributed. | ORD-060, ORD-064, ORD-065 | A    | `orders.test.ts`, `orderPostSave.test.ts`, `orders.featuretests.test.ts` |
| A notebook generated from the order survives the delete; only its backlink is cleared.                                                               | ORD-061, ORD-223          | A, C | `orders.test.ts`, `orders.integration.test.ts`                           |
| Deleting the receipt does not un-purchase the inventory: stash quantities are untouched and their events are unlinked, not deleted.                  | ORD-062, ORD-063          | A    | `orders.featuretests.test.ts`                                            |
| Deleting another user's order is refused.                                                                                                            | ORD-066                   | A    | `orders.featuretests.test.ts`                                            |

### Dispensary order history and stats

| Behavior                                                                                                                                                                                                    | Verified by                                   | Tier | Where                                           |
| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------- | ---- | ----------------------------------------------- |
| Dispensary history paginates one owned shop without duplicates even when purchase dates tie.                                                                                                                | ORD-045                                       | A    | `orders.test.ts`                                |
| A malformed or foreign dispensary id returns an invalid or unavailable state instead of throwing or leaking, and a shop deleted while the screen is open becomes unavailable.                               | ORD-044, ORD-046, ORD-157                     | A, B | `orders.test.ts`, `OrdersScreens.test.tsx`      |
| The history header's order count is bounded rather than scanning the whole archive.                                                                                                                         | ORD-031b                                      | A    | `orders.featuretests.test.ts`                   |
| Lifetime stats aggregate exactly from the per-status buckets with no scan cap, a date-windowed query uses the range index and honours its cap, and an empty account returns zeros without dividing by zero. | ORD-040, ORD-041, ORD-041b, ORD-041c, ORD-042 | A    | `orders.test.ts`, `orders.featuretests.test.ts` |

### Historical import and the Order Import Lab

| Behavior                                                                                                                                                                                                 | Verified by               | Tier | Where                                                                           |
| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------- | ---- | ------------------------------------------------------------------------------- |
| The Lab is off on production even when its flag says enabled, and an authenticated caller cannot turn it on there.                                                                                       | ORD-108                   | A    | `orderImportLab.test.ts`                                                        |
| Lab configuration clamps its hours and receipt count, and the Pro grant it issues is self-only and bounded.                                                                                              | ORD-109, ORD-110          | A    | `orderImportLab.test.ts`                                                        |
| A Lab seed produces the requested number of ready-for-review drafts, chunks above 20 rows, and cleans up after itself — skipping jobs whose drafts were already committed.                               | ORD-111, ORD-112, ORD-113 | A    | `orderImportLab.test.ts`, `orders.featuretests.test.ts`                         |
| A caller holding only the Lab flag cannot list Lab jobs.                                                                                                                                                 | ORD-114                   | A    | `orders.featuretests.test.ts`                                                   |
| Historical import entitlement is enforced server-side at every call site, not by the screen, and while entitlement is still loading the client treats every historical-import capability as unavailable. | ORD-115, GML-040, ORD-162 | A, B | `gmail.featuretests.test.ts`, `useHistoricalImportAccess.featuretests.test.tsx` |
| The launch smoke-order fixture is idempotent and only seeds for the designated test identity.                                                                                                            | ORD-116                   | A    | `order-confirm.test.ts`                                                         |

### Gmail-sourced orders in this screen

| Behavior                                                                                     | Verified by      | Tier | Where                                                               |
| -------------------------------------------------------------------------------------------- | ---------------- | ---- | ------------------------------------------------------------------- |
| Discarding a Gmail draft calls both discard mutations in order and leaves no order behind.   | ORD-163, ORD-228 | B, C | `useOrderReview.featuretests.test.tsx`, `gmail.integration.test.ts` |
| Finalizing a Gmail draft passes the draft and the new order id after the save, never before. | ORD-164          | B    | `useOrderReview.featuretests.test.tsx`                              |

### Ownership and access

| Behavior                                                                                                           | Verified by      | Tier | Where                                     |
| ------------------------------------------------------------------------------------------------------------------ | ---------------- | ---- | ----------------------------------------- |
| Creating an order requires authentication, and outcome and resume are isolated to the owner of the attempt.        | ORD-004, ORD-094 | A    | `orders.test.ts`, `orderPostSave.test.ts` |
| Ownership and unauthenticated rejection are enforced by the deployed backend, using two real signed-in identities. | ORD-221, ORD-222 | C    | `orders.integration.test.ts`              |

## What the live tier proves end-to-end

Tier C drives the deployed backend with really-logged-in throwaway identities and cleans
up after itself.

* **A save is a save.** The exact payload the review screen builds is accepted by the
  deployed commit, its outcome is readable, a replay is idempotent, a divergent replay is
  rejected, and two clients racing the same key produce one order — ORD-200, ORD-201,
  ORD-202, ORD-203, ORD-230.
* **The effect chain actually runs on real infrastructure.** A committed order lands in
  the stash, increments the dispensary's visit, and moves achievement metrics — ORD-204,
  ORD-205, ORD-206.
* **Slow strain research still finishes the job.** An unresolved line goes out to the
  research pipeline, comes back, and re-projects into the stash; the whole chain runs in
  reservation order and a stuck attempt is driven to completion by resume — ORD-207,
  ORD-209, ORD-210.
* **Real receipts parse, and non-receipts do not.** Real text and a real photo parse end
  to end through the deployed API, a cancelled-order text yields zero items, and the Pro
  gate is enforced live — ORD-211, ORD-212, ORD-231, ORD-213.
* **Paid work is not billed twice.** The same attempt id replays instead of re-parsing, a
  different payload under the same id conflicts, and an oversized image is refused at the
  real edge — ORD-214, ORD-215, ORD-216.
* **Strain matching works against real strain data** for a signed-in free user, with
  confidence bands rather than exact floats — ORD-217, ORD-218.
* **Other people's orders stay other people's**, enforced by the deployment rather than
  the client — ORD-221, ORD-222.
* **Life after save.** A live delete leaves the notebook and the stash intact, an edit
  round-trips, and the Order Import Lab seeds, reviews, commits, and cleans up a realistic
  historical batch — ORD-223, ORD-224, ORD-225.

## Deliberate gaps & conditions

<Warning>
  A gap listed here is a decision, not coverage. Nothing below is evidence that the
  behavior works.
</Warning>

* **The large-archive cases are opt-in.** Live pagination over a 60-plus-order archive and
  its search truncation (ORD-219, ORD-220) run only with `TIER_C_PAID=1`, because every
  Lab-committed draft queues real notebook work. They skip by default.
* **The Lab's production hard-off is proven statically only.** ORD-226 would need a mutation
  invoked against the production deployment, which the programme forbids. The boundary is
  covered by ORD-108 instead, which asserts the same predicate over the production
  configuration.
* **Cron healing of a stranded effect is not run live.** ORD-229 needs a five-minute-plus
  wait for the reconciliation cron. The reconciliation logic itself is proven at Tier A by
  ORD-095 and ORD-096.
* **Four low-value assertions are deliberately unbuilt.** A start-after-end date range
  (ORD-032), an empty dispensary name (ORD-043), an empty retry list (ORD-101), and a
  parsed-float NaN already contained downstream (ORD-161) are single validator strings with
  no user-visible behavior of their own.
* **Unbounded stats at 17,000 orders is not simulated.** ORD-121 would need a bulk seed
  script; the bounded behavior that matters is proven by ORD-041b and ORD-041c.
* **Notebook writes are quarantined by product decision.** With V2 writes off, a notebook
  effect finalizes shell-only and records a durable marker for a later backfill. ORD-091
  asserts that quarantined behavior and flips the moment writes are enabled.
* **Deleting an order does not reverse stash inventory.** This is intentional, not a missing
  cascade — the user still physically holds what they bought. ORD-063 pins it.
* **Duplicate coverage lives with the primary.** The order-to-notebook journey is proven in
  the notebooks suite, the Gmail-sourced confirm-to-finalize journey in the Gmail suite, and
  draft orders not counting toward achievements in the achievements suite.

## Where to extend

A new Orders behavior gets its test in the tier that can see it: backend logic and
validators in `apps/mobile/convex/tests/orders.featuretests.test.ts` (its `.part2` sibling
holds the notebook-effect cases); hook and screen contracts in the co-located
`*.featuretests.test.tsx` beside the hook, or in
`apps/mobile/test/ui/features/OrdersScreens.test.tsx` for render states; deployed-backend
journeys in `apps/mobile/test/integration/suites/orders.integration.test.ts`.

Receipt-parser behavior — the monetary-evidence and cancelled-purchase gates, media-type
detection, determinism — belongs to the API suite at
`apps/api/src/services/ai-sdk/receipt-parser.test.ts`.

Give the case an `ORD-` id, add its matrix row, and follow the tier and naming rules on
[Mobile Feature Test Platform](/planning/testing/feature-test-platform).
