Wake Approvisionnement on a stock event instead of a clock
The reactive half of the agent. It consumes product.stock_changed from the catalog topic and reports only a crossing of the threshold — every further order line on an already-low reference would otherwise raise the same alarm, and an agent that repeats itself is one a human learns to ignore. A stock-out is treated as its own fact even when the reference was already low. The detection lands on a watchlist readable over a webhook, because a proposal that reached only an execution log would be lost between two nightly runs. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -147,3 +147,75 @@ 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.
|
||||
|
||||
|
||||
## approvisionnement-stock-watch
|
||||
|
||||
The reactive half, and the one the design had been waiting on. It is woken by
|
||||
`product.stock_changed` on `classic-models.catalog` rather than by a clock, so a
|
||||
reference that runs low at 14:32 is noticed at 14:32 instead of being reported
|
||||
at 06:00 the next morning.
|
||||
|
||||
Verified end to end against the live catalog:
|
||||
|
||||
```
|
||||
S12_1099 68 → 38 detected: "fell from 68 to 38, below the threshold of 50"
|
||||
38 → 30 not detected — already below, figures refreshed instead
|
||||
30 → 0 detected: "is out of stock. Nothing can be promised."
|
||||
0 → 68 not detected, entry refreshed (a correction, not demand)
|
||||
POST {"clear":["S12_1099"]} → watching: 0
|
||||
```
|
||||
|
||||
### Only a crossing is news
|
||||
|
||||
`before >= threshold && after < threshold`. Without that test, every further
|
||||
order line on an already-low reference raises the same alarm again, and an agent
|
||||
that repeats itself is one a human learns to ignore.
|
||||
|
||||
A stock-out is treated as its own fact even when the reference was already below
|
||||
the threshold: *low* and *nothing left to promise* are different problems.
|
||||
|
||||
A movement that changes nothing newsworthy still **refreshes** an entry already
|
||||
being watched. Otherwise the watchlist would show the figure from the moment of
|
||||
detection, which is the number least likely to still be true.
|
||||
|
||||
`reason` carries `order:<n>` for demand and `manual-adjustment` for someone
|
||||
correcting the books. Both can justify a purchase order; only demand says
|
||||
anything about the future, so the distinction is recorded rather than filtered
|
||||
on.
|
||||
|
||||
### The detection has somewhere to go
|
||||
|
||||
`$getWorkflowStaticData('global')`, keyed by reference, readable over
|
||||
`GET /webhook/stock-watchlist` and cleared with
|
||||
`POST {"clear": ["S24_2000"]}`. The purchase *proposal* still has nowhere to be
|
||||
written — the domain has no purchase order and no supplier entity — but a
|
||||
detection that only reached an execution log would be lost between two nightly
|
||||
runs, which would make the reactive half pointless.
|
||||
|
||||
The obvious next consumer is `check-availability`: "can we promise X?" is a
|
||||
better answer when it knows X crossed the threshold at 14:32.
|
||||
|
||||
### Kafka details worth keeping
|
||||
|
||||
- **The offset moves only on success** (`resolveOffset: onCompletion`,
|
||||
`allowedStatuses: ["success"]`). A failed execution replays its event rather
|
||||
than losing it — for an autonomous agent a detection dropped in silence is
|
||||
worse than one made twice.
|
||||
- **`fromBeginning: false`.** Otherwise every restart replays the whole
|
||||
retention window and re-detects crossings dealt with days ago.
|
||||
- Most messages on the topic are not `stock_changed`, and the workflow says so
|
||||
explicitly and succeeds. A workflow that threw on an uninteresting event would
|
||||
replay it forever.
|
||||
- The consumer has its **own** SCRAM account,
|
||||
`classic-models-agent-approvisionnement`: Read on that one topic and that one
|
||||
consumer-group prefix. It cannot read sales or payments, and it cannot write —
|
||||
so a compromised workflow can raise no event another agent would act on.
|
||||
|
||||
### Two traps
|
||||
|
||||
- **n8n's Kafka credential has a `ca` field.** The cluster CA goes there, so
|
||||
there was no need to touch `NODE_EXTRA_CA_CERTS` or to disable verification.
|
||||
Not obvious from the node's UI, which shows it only once `ssl` is on.
|
||||
- **A webhook declaring several methods has one output per method**, in the
|
||||
order declared. Wiring only the first silently drops every POST: the execution
|
||||
reports **success** and n8n answers `No item to return was found`.
|
||||
|
||||
Reference in New Issue
Block a user