> ## 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.

# Order Activation: Import, Review, and Research

> Source-of-truth flow for adding orders by Gmail or text, reviewing every line, managing dispensaries, resolving unknown strains, and recovering safely from failures.

## Purpose and status

Adding a first order is High IQ's primary activation moment. This page is the
canonical product and engineering map for the Gmail and text-input paths, from
the first saved shop through order review, durable save, strain research, and
post-save management.

Photo and image receipt input are intentionally outside this document's scope.

<Note>
  This page describes the implemented text and Gmail paths. Photo and image
  receipt input remains intentionally out of scope. The post-sign-in onboarding
  action now opens the same Historical Import flow used elsewhere in the app.
</Note>

## Current implementation at a glance

| Capability                                 | Status                          | Current behavior                                                                                                                                                                                                                     |
| ------------------------------------------ | ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Text input and paste                       | Implemented                     | Validates length, parses the receipt, preserves a recoverable review draft, and opens one combined Review & Confirm screen.                                                                                                          |
| Text review and editing                    | Implemented                     | Users can edit order date, total, dispensary, item name, amount, unit, product type, price, selection, and strain match before saving.                                                                                               |
| Text strain re-matching                    | Implemented                     | Editing a name starts a fresh match; users can select alternatives or retry a failed match request.                                                                                                                                  |
| Gmail historical import                    | Implemented and feature-flagged | Defaults to the largest configured archive range, currently up to the past ten years, and targeted scanning of confirmed saved-shop receipt domains. Discovery is free; production import requires the one-time archive entitlement. |
| Gmail Quick Sync                           | Implemented                     | Requires one or more selected Gmail-ready shops, uses Gmail History when possible, and uses a bounded fallback when required.                                                                                                        |
| Gmail order-content editing                | Implemented                     | Each parsed receipt is staged separately and opens the shared editable Review & Confirm screen before any order is created.                                                                                                          |
| Shop prerequisite for first Gmail use      | Implemented                     | Post-sign-in onboarding offers **Find Past Orders**. The historical wizard requires or creates a real saved shop with a confirmed receipt domain before targeted search.                                                             |
| Large historical review                    | Implemented                     | Up to 2,000 candidates are kept in a paged child ledger; parsed outcomes and editable drafts are separate durable records.                                                                                                           |
| Safe bulk confirmation                     | Implemented                     | Only fully resolved, high-confidence historical drafts can commit in a bounded group. All other receipts require individual review.                                                                                                  |
| Orders archive                             | Implemented                     | **Stash → Orders** provides paginated history, native search, filters, active-import recovery, detail, edit, and delete entry points.                                                                                                |
| Main shop form to Quick Sync               | Implemented                     | Name is the only required field. Users can confirm, add, normalize, deduplicate, and remove up to ten receipt sender domains.                                                                                                        |
| Unknown-line preservation                  | Implemented                     | Every selected line is saved, including unmatched, ambiguous, and placeholder lines. Only resolved lines can enter the stash.                                                                                                        |
| Explicit “Is this a cannabis strain?” gate | Implemented                     | Every unresolved selected name requires Yes or No. Only explicit Yes answers enter the strain research pipeline.                                                                                                                     |
| Durable save and side effects              | Implemented                     | One idempotent Convex commit creates the order and reserves replay-safe dispensary, stash, research, notebook, and achievement work.                                                                                                 |
| Post-save order editing/deletion           | Implemented                     | Order detail supports atomic edits to purchase facts and explicit deletion while preserving Stash history and generated notebooks.                                                                                                   |

## End-to-end activation map

```mermaid theme={null}
flowchart TD
    A[Sign in or open Add Order] --> B{Choose activation}
    B -->|Find Past Orders| G0[Historical Import]
    B -->|Add One Order| C{Choose input}
    C -->|Text| T1[Paste, type, or dictate receipt text]
    C -->|Quick Sync| Q1[Choose Gmail-ready saved shops]

    G0 --> G1{Saved shop with confirmed receipt domain?}
    G1 -->|No| G2[Create or edit a real shop inline]
    G1 -->|Yes| G3[Choose shops and date range]
    G2 --> G3
    G3 --> G4[Free targeted Gmail discovery]
    G4 --> G5[Optional explicit broad search]
    G4 --> G6[Paged candidate review]
    G5 --> G6
    Q1 --> Q2[Gmail History or bounded fallback]
    Q2 --> G6
    G6 --> GM{Import mode}
    GM -->|Historical| G7{Historical Import access?}
    GM -->|Quick Sync| G10
    G7 -->|Production| G8[Restore or buy one-time entitlement]
    G7 -->|Staging or local bypass enabled| G9[Continue without purchase]
    G8 --> G10[Parse selected receipts in bounded concurrent chunks]
    G9 --> G10
    T1 --> T2[Parse receipt and match strain names]
    T2 --> R[Editable Review and Confirm]

    G10 --> L[Candidate and outcome ledgers]
    L --> D{Draft quality}
    D -->|Fully resolved and high confidence| SB[Confirm next safe bounded group]
    D -->|Incomplete, ambiguous, or unmatched| R
    SB --> O[One committed order per receipt]
    R --> O

    O --> X[Durable post-save effects]
    X --> DS[Create or link shop and record visit]
    X --> S[Merge resolved requested lines into stash]
    X --> Q[Queue user-confirmed unresolved strains for research]
    X --> N{Notebook policy}
    N -->|Historical receipt| NH[Request one notebook per order]
    N -->|Single order or Quick Sync| NP[Use reviewed notebook choice]
    X --> H[Check achievements]

    R --> V{User confirms unknown name is cannabis?}
    V -->|Yes| Q
    V -->|No| K[Keep line on order without research]
    Q --> P[Strain research pipeline]

    O --> M[Stash → Orders archive]
    M --> E[Search, filter, open, edit, or delete]

    G10 --> F{Interrupted or failed?}
    F -->|Remount| AR[Restore active job from durable state]
    F -->|Partial failure| RF[Retry failed messages only]
    F -->|Cancel| CA[Terminalize job and discard pending drafts]
    AR --> L
    RF --> G10
```

## Activation contract

The intended first-use contract is:

1. Ask where the user shops and save a real dispensary with at least one usable
   receipt email domain.
2. Offer a targeted Gmail scan for that shop before offering a broad search.
3. Parse candidates into editable orders; do not commit Gmail-derived dates,
   names, prices, or quantities until the user has reviewed them.
4. Preserve every line the user chooses to save.
5. Ask the user whether an unresolved name is actually a cannabis strain.
6. Persist the user's explicit answer and admit only “yes” lines to the strain
   research pipeline.
7. Commit the order once, then let durable workers finish replay-safe side
   effects without making navigation or animation part of save success.
8. Historical Import requests one notebook per confirmed receipt and exposes
   every committed order in the searchable **Stash → Orders** archive.

## Gmail: connection and targeted discovery

### Current connection states

```mermaid theme={null}
stateDiagram-v2
    [*] --> NoGoogle
    NoGoogle --> NeedsScope: Connect Google account
    NeedsScope --> ConnectedFresh: Grant gmail.readonly and initialize Convex state
    ConnectedFresh --> NeedsShop: Open Find Past Orders
    NeedsShop --> HistoricalScan: Save shop/domain and choose date range
    HistoricalScan --> HistoricalReview: Candidates classified
    HistoricalReview --> AccessCheck: Confirm candidate emails
    AccessCheck --> Importing: Entitled or exact non-production bypass
    Importing --> ReceiptReview: Parser finished; job remains in review
    ReceiptReview --> ReceiptReview: Confirm safe group, edit/save, or skip
    ReceiptReview --> ConnectedReturning: Every draft committed or discarded
    ConnectedReturning --> QuickSync: Open Gmail Sync
    QuickSync --> QuickReview: Selected shop domains find candidates
    QuickReview --> Importing: Confirm candidate emails
    HistoricalScan --> Cancelled: Cancel import
    AccessCheck --> Cancelled: Cancel import
    Importing --> Cancelled: Cancel import and discard pending drafts
    ReceiptReview --> Cancelled: Cancel remainder
    ConnectedFresh --> Disconnected: Revoke access
    ConnectedReturning --> Disconnected: Revoke access
    Disconnected --> NeedsScope: Reconnect

    HistoricalScan --> Error: Scan or classification fails
    Importing --> Error: Parse batch fails
    ReceiptReview --> Error: Save or finalization fails
    Error --> HistoricalScan: Retry historical flow
    Error --> QuickSync: Retry Quick Sync
    Error --> Importing: Retry failed messages only
```

Gmail access is read-only. OAuth tokens stay server-side; raw email bodies are
processed in background task memory and are not persisted. Candidate metadata
such as subject, sender, date, and snippet is temporary, owner-scoped review
data stored in a paged child ledger rather than on one growing job document.

### Targeted discovery contract

```mermaid theme={null}
flowchart TD
    A[User chooses Gmail] --> B{Saved shop with email domain?}
    B -->|Yes| C[Preselect most recent shop]
    C --> D[Search only selected shop domains]
    D --> E{Candidates found?}
    E -->|Yes| F[Classify and review candidates]
    E -->|No| G[Offer another shop or broader search]

    B -->|No| I[Ask where the user shops]
    I --> J[Create a real saved shop inline]
    J --> K[Confirm at least one receipt sender domain]
    K --> C

    G -->|User explicitly chooses broad search| H
    H --> L[Saved-shop domains plus community domains plus keyword queries]
    L --> F
```

Historical import and Quick Sync default to selected shops with confirmed
`emailDomains`. A user without a Gmail-ready shop can create or update one
inline. “Advanced Options” can start a broader historical scan only after the
user explicitly chooses it.

### Current Gmail background sequence

```mermaid theme={null}
sequenceDiagram
    participant U as User
    participant App as Mobile app
    participant C as Convex
    participant P as Purchase access
    participant T as Trigger.dev
    participant G as Gmail API
    participant API as Receipt parser

    U->>App: Start scan
    App->>C: Claim scan or incremental-sync job
    C->>T: Trigger job with stable idempotency key
    T->>G: Search selected or broad query scope
    G-->>T: Message metadata
    T->>C: Signed candidate chunks
    T->>T: Classify ambiguous candidates
    T->>C: Signed classification chunks
    C-->>App: ready_for_review subscription update
    App-->>U: Page through dispensary groups and emails
    U->>App: Import selected candidates
    App->>P: Verify one-time entitlement when historical
    P-->>App: Entitled or exact staging/local bypass
    App->>C: Claim batch import attempt
    C->>T: Trigger bounded message chunks

    par Bounded receipt children
        T->>G: Fetch full email body
        T->>API: Parse receipt text
        API-->>T: Order metadata and line items
        T->>C: Signed per-email progress result
        C->>C: Create or update one staged review draft
    and More bounded receipt children
        T->>G: Fetch and parse independently
        T->>C: Upsert one idempotent outcome
    end

    T->>C: Signed batch completion
    C-->>App: Move to review; do not mark historical import complete
    loop Each staged receipt
        App-->>U: Safe group confirm or editable Review & Confirm
        U->>App: Correct, verify, save, or skip
        App->>C: Idempotent order commit, then finalize staged review
    end
    C->>C: Complete only after every draft is committed or discarded
    App-->>U: Saved, skipped, duplicate, no-item, and failure summary
```

### Gmail selection and import rules

* A candidate is not an imported order.
* Users can select dispensary groups and individual candidate emails.
* Gmail message ID is the durable duplicate key.
* `totalImported` increments only after a staged receipt is actually confirmed
  and committed.
* Historical and Quick Sync imports do not add to the active stash.
* Every committed historical order requests its own notebook. Quick Sync uses
  the notebook preference selected before import.
* Safe bulk confirmation is limited to fully resolved, high-confidence
  historical drafts and commits at most one configured chunk per action.
* Candidate review is paged and each message has a separate durable outcome, so
  a large archive never depends on one unbounded job payload.
* Closing a staged Gmail review offers **Skip Receipt** and returns to the
  remaining receipts; it never silently creates an order.
* Failed orders can be retried without replaying successful message IDs.
* Reopening the flow restores the active job automatically. A recoverable error
  exposes **Resume Import**, which reuses the stored scan/import fingerprint and
  durable child outcomes rather than replaying successful work.
* Historical completion is recorded only after receipt parsing has finished and
  every staged draft is committed or explicitly discarded.
* Disconnect closes local write boundaries, cancels known background work when
  possible, and revokes the delegated Gmail token while preserving imported
  orders.

## Text input: parse, recover, and review

```mermaid theme={null}
flowchart TD
    A[Open Text Input] --> B{Order and receipt-parsing access allowed?}
    B -->|No| C[Unavailable or upgrade surface]
    B -->|Yes| D[Type, paste, or use voice input]
    D --> E{Input valid?}
    E -->|Empty| E1[Prompt for receipt text]
    E -->|Below minimum| E2[Ask for more receipt details]
    E -->|Above maximum| E3[Ask user to shorten input]
    E -->|Valid| F[Create unique parse attempt]
    F --> G[Convex parseReceipt action]
    G --> H{Parse result}
    H -->|Failed or network error| I[Preserve text and show retry]
    I --> F
    H -->|Match service failed| J[Create review draft with match-failed warning]
    H -->|Parsed| K[Create review draft]
    J --> L[Review and Confirm]
    K --> L
    L --> M{Save or discard?}
    M -->|Save| N[Durable idempotent commit]
    M -->|Discard| O[Delete local draft]
    N --> P[Delete local draft after commit]
```

Raw pasted text is kept only in the active in-memory review. The persisted
recovery projection is versioned and allowlisted; it intentionally excludes
the raw receipt text. Drafts expire after 24 hours, and the screen waits for
native draft-store hydration before declaring a cold-start draft missing.

## Review and edit contract

### Current Review & Confirm state machine

```mermaid theme={null}
stateDiagram-v2
    [*] --> Reviewing

    Reviewing --> EditingOrder: Change shop, personal-purchase flag, date, or total
    EditingOrder --> Reviewing: Apply value

    Reviewing --> EditingName: Edit strain name
    EditingName --> Rematching: Commit new name
    Rematching --> Reviewing: Exact, alternative, placeholder, ambiguous, or unmatched result
    Rematching --> MatchError: Match request fails
    MatchError --> Rematching: Retry matching
    MatchError --> Reviewing: Dismiss warning and keep lines

    Reviewing --> EditingLine: Change price, quantity, unit, or product type
    EditingLine --> Reviewing: Apply value
    Reviewing --> Reviewing: Select or deselect line
    Reviewing --> Reviewing: Add, remove, or restore line
    Reviewing --> StrainDetail: Open a resolved strain
    StrainDetail --> Reviewing: Return

    Reviewing --> SaveOptions: Save order
    SaveOptions --> MissingFields: Optional date, total, price, or quantity is missing
    MissingFields --> Reviewing: Go back and edit
    MissingFields --> Saving: Save anyway
    SaveOptions --> Saving: Fields accepted
    Saving --> Complete: Commit confirmed
    Saving --> Recoverable: Commit unknown or finishing step fails
    Recoverable --> Complete: Reconcile same idempotency key
    Reviewing --> DiscardConfirm: Close
    DiscardConfirm --> Reviewing: Keep editing
    DiscardConfirm --> [*]: Delete draft and dismiss
```

### What users can edit before a text order is saved

| Area                         | Editable now | Notes                                                                                  |
| ---------------------------- | ------------ | -------------------------------------------------------------------------------------- |
| Dispensary                   | Yes          | Choose a saved shop, create a name-only shop inline, or mark as personal purchase.     |
| Purchase date                | Yes          | Stored as a local calendar date converted to a timestamp.                              |
| Total amount                 | Yes          | Parsed total is preserved until selected item edits make the line total authoritative. |
| Strain name                  | Yes          | A committed edit triggers re-matching.                                                 |
| Alternative match            | Yes          | User selection can replace the proposed match.                                         |
| Price                        | Yes          | Zero/missing price warns but does not block save.                                      |
| Quantity, unit, product type | Yes          | Missing quantity warns when adding to stash.                                           |
| Include/exclude line         | Yes          | Deselected lines are not saved or researched.                                          |
| Add/remove/restore line      | Yes          | Every selected line can be preserved, even if unresolved.                              |

Gmail-derived and text-derived orders use this same editable review surface.

## Unknown strain confirmation and verification

### Implemented user and backend gate

```mermaid theme={null}
flowchart TD
    A[Selected line has no authoritative strain identity] --> B[Show exact observed receipt name]
    B --> C{User: Is this a cannabis strain?}
    C -->|No| D[Save line as unresolved]
    D --> E[Do not spend research credits]
    C -->|Yes| G[Persist confirmedCannabisStrain]
    G --> H[Canonicalize and deduplicate research request]
    H --> L[Queue full strain research pipeline]
    L --> P[Bind research run to exact order-item ID]
    P --> Q{Terminal result}
    Q -->|Resolved| O[Line becomes resolved]
    Q -->|No data, failed, or canceled| R[Keep durable unresolved evidence]
```

Changing a name or selecting a different match resets the answer. Save remains
disabled while any selected unresolved name is pending confirmation or being
re-matched. A No answer keeps the line in purchase history with research
canceled; it is never sent to the paid pipeline.

Identity and research readiness remain separate:

| Resolution state  | Meaning                                                | Can enter stash immediately? | Research behavior now                                  |
| ----------------- | ------------------------------------------------------ | ---------------------------: | ------------------------------------------------------ |
| `resolved`        | Authoritative strain ID confirmed                      |  Yes, if stash was requested | No research needed                                     |
| `placeholder`     | Catalog placeholder, not fully researched              |                           No | Queued only after explicit Yes                         |
| `ambiguous`       | Candidate exists but is not authoritative              |                           No | Queued only after explicit Yes                         |
| `unmatched`       | No candidate identity                                  |                           No | Queued only after explicit Yes                         |
| `research_failed` | Prior research reached a non-resolved terminal outcome |                           No | Evidence remains durable; any requeue must be explicit |

## Durable commit and post-save outbox

```mermaid theme={null}
sequenceDiagram
    participant U as User
    participant App as Review screen
    participant C as Convex commit
    participant O as Durable effects
    participant API as Hono research API
    participant T as Trigger.dev pipeline

    U->>App: Choose save option
    App->>C: commitOrder with stable idempotency key
    C->>C: Validate owner, date, totals, quantities, and every selected line
    C->>C: Create or reconcile orderSaveAttempt
    C->>C: Create order and order items once
    C->>O: Reserve dispensary visit when shop exists
    C->>O: Reserve stash merge when requested
    C->>O: Reserve research only for explicitly confirmed unresolved lines
    C->>O: Reserve notebook only when requested and enabled
    C->>O: Schedule achievement check
    C-->>App: Order committed plus current effect outcomes
    App-->>U: Order saved summary

    O->>O: Retry database effects with durable status
    O->>API: queue-batch with effect idempotency key
    API->>T: Start or join one run per canonical slug
    API-->>O: Return run handles
    O->>O: Bind each run to its exact order-item ID
    T-->>O: Authenticated terminal callback
    O->>O: Resolve exact line or record terminal failure
    O->>O: Merge a later-resolved requested line into stash once

    Note over App,C: Retrying the same draft reconciles the committed order; it does not create a duplicate.
    Note over O,API: A No answer never reserves or calls the research effect.
```

The save sheet currently offers:

* **Save for Stats Only**: no stash merge.
* **Save & Add to Stash**: resolved lines enter active inventory; unresolved
  lines keep their stash intent and may enter after successful research.
* **Save, Stash & Report**: available only when the code-owned notebook policy
  enables order notebook generation.

Historical Import is intentionally different from a single manual order: every
confirmed historical receipt uses the shared commit boundary with notebook
generation requested and active-Stash insertion disabled.

The save sheet distinguishes confirmed unknown strains, which will be
researched, from names marked “not a strain,” which remain on the order without
research.

## Dispensary lifecycle and Gmail readiness

```mermaid theme={null}
flowchart TD
    A{Shop source} -->|Main Add Shop form| B[Name plus optional details and receipt domains]
    A -->|Text order save| C[Exact-name find or name-only create]
    A -->|Gmail order commit| D[Exact-name find or name-only create]
    A -->|Gmail inline setup| E[Name plus confirmed receipt domain]
    A -->|Post-sign-in onboarding| F[Find Past Orders]

    B --> G[Convex dispensary]
    C --> G
    D --> G
    E --> G
    F --> E

    G --> H{Has emailDomains?}
    H -->|Yes| I[Quick Sync-ready]
    H -->|No| J[Visible in Shops but not Quick Sync-ready]

    B --> K[Email and website suggest domains]
    K --> L[User confirms or removes each suggestion]
    L --> I
    E --> I

    G --> M[Orders link by dispensaryId when available]
    M --> N[Durable visit effect increments visit count and last visit]
    G --> O[Favorite, view details, view order history, edit fields, or delete]

    B --> P[Manage up to ten normalized receipt email domains]
    P --> I
    D --> Q[Attach reviewed sender domain to the confirmed shop]
    Q --> I
```

### Shop-management rules

* Shop name is the only required field.
* Contact email and receipt sender domains are separate: entering an email or
  website may suggest a domain, but it does not become Gmail search scope until
  the user confirms it.
* Domains are normalized, validated, deduplicated, removable, and capped at
  ten per shop.
* Gmail defaults to strict selected-shop domain scope. Broad search is a
  clearly labeled secondary opt-in.
* Finalizing a reviewed Gmail order attaches its validated sender domain to the
  linked shop, making future Quick Sync targeted.
* Empty optional fields can be cleared, malformed email/URL/phone/ZIP/domain
  values are rejected, double submission is blocked, and dirty cancellation
  requires confirmation.
* Deleting a shop removes the shop record. Existing orders retain their saved
  dispensary name, but the detail link may no longer resolve.

## Recovery and error states

```mermaid theme={null}
flowchart TD
    A{Failure point} -->|OAuth canceled or scope missing| B[Stay disconnected and offer retry or text input]
    A -->|Gmail token revoked or expired| C[Reconnect Gmail]
    A -->|Scan scheduling fails| D[Retry same logical scheduling attempt]
    A -->|Scan or classification runtime fails| E[Error step with retry]
    A -->|No candidates| F[Empty state]
    F --> G[Try another shop]
    F --> H[Try explicit broad search or another date range]
    F --> I[Return to text input]

    A -->|Import partially fails| J[Keep successes and list failed, no-item, and duplicate results]
    J --> K[Retry failed message IDs only]
    A -->|User skips staged receipt| J2[Mark only that receipt skipped and return to remaining reviews]
    A -->|Disconnect during work| L[Invalidate connection generation and ignore late callbacks]
    A -->|App closes or remounts| L2[Restore the active job and continue from durable outcomes]
    A -->|User cancels historical import| L3[Terminalize job, discard pending drafts, and cancel known runs best-effort]

    A -->|Text parse fails| M[Keep input and show bounded retry]
    A -->|Strain match API fails| N[Keep all lines and show Retry Matching]
    A -->|Draft expired or missing| O[Return to upload; raw input cannot be restored after cold recovery]
    A -->|Save response unknown| P[Retry same idempotency key]
    P --> Q[Reconcile existing order]
    A -->|Post-commit cleanup or navigation fails| R[State that order is saved and offer safe finish]

    D --> S[No duplicate scan job]
    K --> T[No duplicate order by Gmail message ID]
    Q --> U[No duplicate order or side effects]
```

### Error-state truth rules

* “Found” never means “imported.”
* A failed or abandoned Gmail review must not increment imported totals.
* The Gmail History cursor advances only after a bounded discovery window is
  accepted; bounded failures leave the cursor unchanged.
* A committed order must never be reported as “Save failed” because local
  cleanup, refresh, or navigation failed afterward.
* Match failure is different from “no match.” A failed matcher keeps a visible
  retry action and preserves all order lines.
* Research queue acceptance is not research completion.
* A parser batch finishing means **ready for review**, not “historical import
  complete.” Completion requires every staged draft to be committed or
  discarded.
* A cancel result may report provider cancellation as incomplete, but the local
  terminal state and connection-generation checks still reject late writes.

## Post-save order management

```mermaid theme={null}
flowchart TD
    A[Open Stash → Orders] --> A1[Search and filter purchase archive]
    A1 --> B[Open order detail]
    B --> C[View shop, date, source, status, totals, and all lines]
    B --> D{Resolved items not already in stash?}
    D -->|Yes| E[Add resolved items to stash]
    D -->|No| F[Show stash status]
    B --> G{Notebook policy and state}
    G -->|Allowed| H[Generate, open, cancel, or upgrade notebook]
    G -->|Disabled or waiting| I[Truthful unavailable or research-waiting state]
    B --> J[Open linked shop detail and its order history]

    B --> K[Edit saved order]
    K --> L[Atomically edit shop, date, total, quantity, unit, and price]
    L --> M[Keep strain identity locked so prior verification stays trustworthy]
    B --> N[Delete order with explicit impact summary]
    N --> O[Remove receipt history and dangling backlinks]
    O --> P[Preserve Stash history and generated notebooks]
```

Order detail supports editing mutable purchase facts with native date input,
inline validation, unsaved-change confirmation, and double-submit protection.
Strain identity is intentionally locked because changing it requires the full
matching, user confirmation, research, and Stash reconciliation contract; users
are directed to add a corrected order for that case. Deleting an order preserves
generated notebooks and Stash history while clearing their stale order links.
The Orders archive paginates older purchases and filters by status, source,
review state, notebook state, date window, and shop. Native header search finds
shop names, and an active Gmail-import banner returns directly to the correct
Historical Import or Quick Sync flow.

## Static acceptance matrix

| Persona or condition                        | Supported path and guardrail                                                                                                                                                                                                                                              |
| ------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| New user, text                              | Feature and Pro gates resolve before entry; 10–50,000 trimmed characters are accepted; the original text survives retry and the parsed review survives a cold start for 24 hours without persisting raw input.                                                            |
| New user, Gmail, no Google grant            | The app explains read-only access, handles cancellation or missing scope, and cannot start a scan until the verified Gmail connection is operational.                                                                                                                     |
| New user, Gmail, no ready shop              | Targeted search cannot start. Inline setup saves a real shop and validated sender domain; broad search requires an explicit opt-in.                                                                                                                                       |
| New user, historical onboarding             | **Find Past Orders** opens the real historical wizard. Discovery and candidate preview are free; production verifies the one-time entitlement immediately before parsing selected bodies.                                                                                 |
| Staging or local historical QA              | When Historical Import is enabled and `HISTORICAL_IMPORT_STAGING_BYPASS` is not `false`, the server bypasses only the purchase requirement. The same scan, parse, review, commit, research, notebook, and archive path still runs. Production never receives this bypass. |
| Returning Gmail user                        | Quick Sync scopes Gmail History or its bounded fallback to the selected shops' deduplicated domains and resumes an active job after remount.                                                                                                                              |
| Large receipt                               | Receipt parsing, review, commit, and saved-order mutation all enforce the same 100-line ceiling and bounded names, quantities, and monetary values.                                                                                                                       |
| Large mailbox                               | Historical discovery is globally capped at up to 2,000 candidates. Paged candidate records, per-message outcomes, bounded import chunks, progress callbacks, connection generations, and idempotency keys prevent oversized job documents, duplicates, and stale work.    |
| Ambiguous or unknown strain                 | Fuzzy candidates never become authoritative automatically. Every selected unresolved name requires Yes or No; only Yes reserves research, and only a later authoritative result may enter Stash.                                                                          |
| Research pipeline callback                  | The terminal webhook is secret-authenticated, body-size bounded, and validates normalized run IDs, strain IDs, slugs, names, statuses, and failure codes before reconciling an order or Stash row.                                                                        |
| Double tap, timeout, or remount during save | Same-frame guards stop duplicate taps; a stable payload fingerprint and idempotency key reconcile the committed order and replay durable effects without duplicating it.                                                                                                  |
| Partial Gmail batch failure                 | Successful or staged receipts remain durable; only failed message IDs are eligible for retry. A receipt whose order already committed cannot subsequently be skipped.                                                                                                     |
| App kill or navigation drop-off             | Reopening the flow or tapping the Orders active-import banner restores the current durable job. Recoverable errors expose **Resume Import**; unfinished child work and outcomes are reused automatically.                                                                 |
| Historical cancel                           | The authenticated cancel action terminalizes the job, discards pending drafts, and requests cancellation for known Trigger runs. It reports whether provider cancellation was incomplete instead of claiming more than it can guarantee.                                  |
| Safe bulk confirmation                      | Only historical drafts with a valid shop/date and fully resolved lines above the server-owned confidence threshold qualify. One action commits at most one configured chunk; all needs-attention receipts stay editable.                                                  |
| Shop rename or deletion                     | Canonical duplicate names are rejected. Stable shop IDs keep history linked across renames; deletion detaches links while retaining historical shop labels on orders.                                                                                                     |
| Saved-order correction                      | Purchase facts update atomically with ownership and numeric/date bounds. Strain identity stays locked because changing it requires the full match, confirmation, research, and Stash reconciliation flow.                                                                 |
| External service outage                     | Input and staged review data remain available; retryable provider failures use bounded retries or durable outbox replay, while UI copy distinguishes pending work from completed work.                                                                                    |

Hard ceilings are intentional service-safety boundaries, not silent truncation:
100 items per order, 50,000 text characters, ten sender domains and five menu
URLs per shop, up to 2,000 historical Gmail candidates globally, and bounded
Gmail import chunks. Invalid overflow is rejected with recoverable input still
intact.

## Source-of-truth implementation map

| Concern                                   | Primary implementation                                                                                                                      |
| ----------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| Method hub                                | `apps/mobile/src/_screens/order-upload/OrderUploadContainer.tsx`                                                                            |
| Text input and parse                      | `apps/mobile/src/_screens/order-upload/TextInputScreen.tsx`, `hooks/useTextReceiptUpload.ts`                                                |
| Recoverable review draft                  | `apps/mobile/src/_screens/order-upload/orderFlowDraftStore.ts`                                                                              |
| Combined review and edit                  | `apps/mobile/src/_screens/order-confirm/OrderConfirmContainer.tsx`, `hooks/useOrderReview.ts`                                               |
| Staged Gmail receipt transformer          | `apps/mobile/src/_screens/gmail/gmailOrderReviewDraft.ts`                                                                                   |
| Idempotent client save                    | `apps/mobile/src/_screens/order-confirm/hooks/useOrderSaveFlow.ts`                                                                          |
| Durable commit and effects                | `apps/mobile/convex/orderPostSave.ts`, `apps/mobile/convex/orderResolution.ts`                                                              |
| Gmail wizard                              | `apps/mobile/src/_screens/gmail/hooks/useGmailWizardController.ts`, `useGmailWizard.ts`                                                     |
| Gmail state and jobs                      | `apps/mobile/convex/gmailSync.ts`, `apps/mobile/convex/schema.ts`                                                                           |
| Historical flags, entitlement, and limits | `apps/mobile/src/_config/historical-import.ts`, `apps/mobile/convex/utils/historicalImportConfig.ts`, `apps/mobile/convex/subscriptions.ts` |
| Gmail background tasks                    | `packages/trigger/src/tasks/gmail/`                                                                                                         |
| Receipt parsing boundary                  | `apps/mobile/convex/orders.ts`, Hono receipt routes                                                                                         |
| Research batch admission                  | `apps/api/src/routes/research/trigger-research.ts`                                                                                          |
| Research terminal reconciliation          | `apps/mobile/convex/http.ts`, `apps/mobile/convex/orderPostSave.ts`                                                                         |
| Cannabis verification service             | `apps/api/src/services/verification/`                                                                                                       |
| Dispensary management                     | `apps/mobile/convex/dispensaries.ts`, `apps/mobile/src/_screens/dispensary-form/`                                                           |
| Post-sign-in activation entry             | `apps/mobile/src/_screens/onboarding/OnboardingScreen.tsx`                                                                                  |
| Searchable Orders archive                 | `apps/mobile/src/_screens/orders-library/`, `apps/mobile/convex/orders.ts`                                                                  |
| Post-save detail and management           | `apps/mobile/src/_screens/order-detail/OrderDetailScreen.tsx`, `components/OrderManagementSection.tsx`                                      |

## Related guides

<CardGroup cols={2}>
  <Card title="Gmail Sync" icon="envelope" href="/help/features/gmail-sync">
    User-facing connection, scan, review, and privacy guidance.
  </Card>

  <Card title="Managing Shops" icon="store" href="/help/features/dispensaries">
    What shop fields do today and how they affect Gmail discovery.
  </Card>

  <Card title="Order Import Troubleshooting" icon="wrench" href="/help/troubleshooting/order-import">
    Recovery guidance without overstating unsupported editing features.
  </Card>

  <Card title="Gmail Architecture" icon="diagram-project" href="/guides/platform/gmail-sync">
    The current Convex, Trigger.dev, Gmail, and Hono boundaries.
  </Card>
</CardGroup>
