docs(orders): order assistant runbook final pass

The runbook now matches the code as built.

- New section: filling the lines from the quote — which lines may be
  filled, what is written, the 5% price doubt, the purple review and
  how it ends, and the board badge.
- The "Fill lines from the quote" switch is no longer described as
  reserved for a later phase, and the tick's bounds now say what the
  code does: 10 orders matched, 10 orders filled, and an order with
  nothing to do does not use up that budget.
- Reading order_items.ai_fill is documented alongside orders.ai_quote_match.
- Live steps confirmed against the code: the migration is
  pb:migrate:order-assistant, ai_quote_match and ai_fill are both in
  IGNORED_FIELDS, the n8n tick is POST /api/v1/orders/assistant/tick,
  and the settings labels are "Order assistant enabled", "Run every
  (minutes)", "Auto-link when sure" and "Fill lines from the quote".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Dave 2026-10-24 15:34:00 +02:00
parent d3a52d97f2
commit 34d3422586

View file

@ -16,9 +16,13 @@ the candidate quotes the order came from, and then either:
- **links** the order to the quote itself (when it is sure), or
- **suggests** the quote, and a person accepts it.
The AI never touches prices, never changes a line's status, never creates a
purchase order and never sends anything. It only decides which quote an
order belongs to.
Once the order points at a quote, the assistant can also **fill the
order's lines** from that quote — the cost, the supplier, the SKU and the
part number — leaving them in **purple** until a person checks them.
The AI never changes a line's status, never creates a purchase order and
never sends anything. It decides which quote an order belongs to, and it
may pre-fill a line's prices from that quote.
## The auto-link rule, in plain words
@ -43,6 +47,86 @@ when a line on the order has already moved past `pending`, or when
another order already points at that quote. A refusal is **not** an error:
the assistant falls back to a suggestion.
## Filling the lines from the quote
With **"Fill lines from the quote"** on, the assistant prices the order's
lines from the quote they are paired with. This is a copy, not a new
price: no AI call is involved, the pairing is already on the lines.
A line may be filled only when **all** of these are true:
- It is still `pending`.
- It carries **no cost** of its own (`cost_price` is 0).
- Its `pricing_source` is not `manual` and not `quote` — a price a person
set is never overwritten.
- It has no **reviewed** AI fill on it.
- It is paired to a quote line (`quote_item` is set) that actually has a
cost.
What the fill writes:
| Field | Rule |
|---|---|
| `cost_price` | The quote line's cost |
| `selling_price` | Only when the order line has none of its own |
| `supplier`, `sku`, `matched_price_list_id` | Only when the order line has none of its own |
| `markup` + `markup_percent` | Written together, from cost and selling |
| `pricing_source` | `ai_quote` |
| `ai_fill` | The audit record of the fill (see below) |
Every line is **re-read immediately before** it is written, so a cost a
person typed in the meantime wins over the AI's.
If the customer's price and the quoted price both exist and differ by more
than **5%**, the fill records a doubt, e.g. `customer price R16.64, quoted
R18.20`. Doubts show on the line's marker.
### The purple review
A filled line is **pending review** while all three are true:
- `ai_fill.status` is `filled`,
- `ai_fill.reviewed_at` is empty, and
- `pricing_source` is still `ai_quote`.
While a line is pending it shows up **purple** on the order Process page,
with the bot marker next to it. Hover or focus the marker to see the quote
it was filled from, the cost, the selling price, the markup, the reason and
any doubts.
The purple ends the moment somebody touches the line:
- **Saving the line** records the review on `ai_fill` (`reviewed_at`,
`reviewed_by`, and whether the prices were accepted unchanged or
changed). The pricing source stays `ai_quote`.
- The one-click **"use this cost"** copy writes `pricing_source: "quote"`
— the operator chose the quote's cost themselves — so the AI's
provenance ends too.
- A line whose price the operator types by hand becomes `pricing_source:
"manual"`.
### The board badge
The Orders board shows an **"AI x/y"** badge on an order when the AI
filled some of its lines: `x` is how many lines still need a review, `y`
is how many the AI filled. The badge is hidden when nothing is pending.
### `order_items.ai_fill`
| Field | Meaning |
|---|---|
| `status` | `filled`, `skipped` or `error` |
| `source` | `quote` |
| `quote_id`, `quote_number`, `quote_item_id` | Where the numbers came from |
| `cost_price`, `selling_price`, `markup` | What was written |
| `supplier_id`, `sku` | What was written |
| `reason` | Why the line was paired with that quote line |
| `doubts` | Anything a person should check (e.g. the 5% price difference) |
| `model`, `attempted_at` | Which model, and when |
| `reviewed_at`, `reviewed_by` | Set when a person checked the line |
| `accepted_unchanged` | True when the person kept both prices |
| `supplier_changed` | True when the person changed the supplier |
## Going live (manual steps, in this order)
1. **Migrate PocketBase.** Run against the live PB:
@ -71,7 +155,8 @@ the assistant falls back to a suggestion.
5. **Activate the n8n clock** (below).
6. **Turn it on** in Settings, AI: the global "AI enabled" switch must be
on, then switch on **"Order assistant enabled"** in the Order assistant
section and click Save. Defaults: every 10 minutes, auto-link on.
section and click Save. Defaults: every 10 minutes, auto-link on, fill
lines on.
The tick does nothing until both switches are on, so steps 5 and 6 are
safe in either order.
@ -118,9 +203,15 @@ Click-by-click:
A 401 means the credential is wrong (it must send the app's
`AGENT_API_KEY` as a Bearer token). A tick that takes long is normal when
there is work. Each run is bounded: at most **10 orders**, oldest first,
and it stops starting new orders once 90 seconds have passed, so a run
normally answers inside the 120 s timeout; leftovers carry to the next run.
there is work. Each run is bounded: it matches **at most 10 orders** and
fills **at most 10 orders**, and it stops starting new orders once 90
seconds have passed, so a run normally answers inside the 120 s timeout;
leftovers carry to the next run.
An order the tick has nothing to do with does **not** use up that budget:
the run looks through up to 200 unlinked orders to find 10 it can work
on, and an order whose lines the fill could not write is skipped rather
than counted.
Runs never overlap. A run stamps its start time before it does any work and
writes its summary when it ends. While a start is newer than the last
@ -131,7 +222,8 @@ summary, the next tick after those 10 minutes takes over.
## Running it by hand
- **Order page** — the **"Match with AI"** button on the quote match card
runs the assistant for that one order and reloads the order.
runs the assistant for that one order and reloads the order. It also
fills the lines when the order ends up linked.
- **MCP tool** — `orders.assistant_match` with `{"orderId": "..."}`.
Both force a run past the "already looked at these lines" rule.
@ -161,7 +253,7 @@ about an order. Read it in the PB admin.
| Order assistant enabled | off | The tick does nothing while this is off |
| Run every (minutes) | 10 | How often the tick may run |
| Auto-link when sure | on | May the assistant link the order itself? |
| Fill lines from the quote | on | Reserved for the next phase — the assistant does not fill lines yet |
| Fill lines from the quote | on | May the assistant pre-fill the order's lines from the linked quote? |
| Prompt | empty | Overrides the built-in prompt when set |
`orders.ai_quote_match` and `order_items.ai_fill` are in `IGNORED_FIELDS`
@ -170,9 +262,9 @@ log.
## Not in this phase
- The assistant does **not** fill order lines from the quote yet — that is
the next phase. The `order_items.ai_fill` field and the "Fill lines from
the quote" switch are already in place for it.
- The assistant does not touch prices, suppliers or SKUs.
- The assistant does not price a line from scratch — it only copies the
cost, supplier, SKU and part number from the quote line the order line
is already paired with.
- It never creates a purchase order, never sends an email and never
changes a line's status.
- There is no POS fallback: the assistant fills only from a quote.