feat: the periodic reorder proposal, and what it cannot do

The periodic half of Approvisionnement: a 06:00 schedule that reads the catalog
and proposes purchase quantities for anything below the reorder threshold.
Verified on live data — 110 of 110 references scanned, S24_2000 at 15 on hand,
135 proposed.

Three things it deliberately does not do, each recorded rather than worked
around:

It halts without a mandate. No user session exists at 06:00, and the
offline_access mandate the design calls for does not exist yet, so the first
node stops before any token exchange and before reading anything. An agent
without an identity does nothing rather than inventing an authority.

It writes nothing. The schema has no purchase order and no supplier entity, and
the catalog's only write tool is create-product, which belongs to another agent.
So catalog:write was not granted, contrary to the design's scope list: a scope
with no reachable target is an authorisation waiting for a use.

It scans one page and says so. complete: false escalates, because a proposal set
computed from part of the catalog looks exactly like one computed from all of
it.

Also: check_availability now states its basis. Deducting quantities committed on
open orders would need the sales API to be filterable by product code — it is
not, so the honest alternative was paging 2996 rows per question.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Stéphane Tailland
2026-08-29 17:33:54 +02:00
parent 95b6e3a1c9
commit 06c102fb72
3 changed files with 348 additions and 1 deletions
+54
View File
@@ -88,3 +88,57 @@ Now:
and through the A2A facade, `escalate: true` becomes a task in state **failed**
rather than a completed one — the honest shape for "a human has to look at
this".
## approvisionnement-periodic-reorder
The periodic half. A schedule trigger at 06:00, deliberately periodic rather
than a batch standing in for events: grouping references into a purchase
proposal follows a buying cycle, while *detecting* a coming stock-out is
reactive and waits for Kafka.
Verified end to end on the live catalog:
```
scanned 110 of 110, complete
1 reference below the threshold of 50:
S24_2000 1960 BSA Gold Star DBD34 (Highway 66 Mini Classics)
on hand 15 → propose ordering 135
escalate: true
```
### It stops without a mandate, and that is the point
No user session exists at 06:00, so authority has to be pre-established. The
design calls for an `offline_access` mandate — the authorising user's refresh
token, stored encrypted, exchanged at trigger time, so `sub` stays the person
who mandated and `azp` says which agent acted.
**That does not exist yet.** The first node looks for
`APPROVISIONNEMENT_MANDATE_TOKEN` and, finding none, halts before any exchange
and before reading anything. Verified: the run completes, `Exchange token` never
executes, nothing is read. An agent without an identity does nothing rather than
inventing an authority.
### The proposal has nowhere to go
It is computed and escalated, never written, because there is nothing to write
it to: the `classicmodels` schema has no purchase order and no supplier entity —
`productvendor` is a string on the product — and the catalog's only write tool
is `create-product`, which is the Catalogue agent's business.
So `catalog:write` was **not** granted to this agent, contrary to the design's
scope list. A scope with no reachable target is an authorisation waiting for a
use, which is the wrong order.
Closing this needs a `reorders` entity in the domain, or a channel to a human —
there is no mail server in the cluster either. Same shape as the design's own
note that a Livraison agent needs a `shipments` domain first.
### It scans one page, on purpose
200 covers the 110 references, and the node reports `complete: false` and
escalates if it ever does not. A proposal set computed from part of the catalog
looks exactly like one computed from all of it, so the difference has to be in
the output. A catalog that outgrows one page needs a paging loop **and** the
read-volume cap this repo already lists as mandatory for autonomous agents.