From a3b5996d1519da89f7b3cb984b67e302ad4186da Mon Sep 17 00:00:00 2001 From: Dave Date: Fri, 6 Nov 2026 05:35:26 +0200 Subject: [PATCH] fix(giellie): correct the stock tool actor, audit and quantity pointers --- .agents/skills/wfdash-giellie/SKILL.md | 4 ++-- src/app/api/mcp/route.test.ts | 5 ++++- src/app/api/mcp/route.ts | 4 ++-- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/.agents/skills/wfdash-giellie/SKILL.md b/.agents/skills/wfdash-giellie/SKILL.md index 2480264..d735ce8 100644 --- a/.agents/skills/wfdash-giellie/SKILL.md +++ b/.agents/skills/wfdash-giellie/SKILL.md @@ -258,8 +258,8 @@ REST mirror at `/api/v1/quote_items` (GET list + POST create) and `/api/v1/quote | Tool | Purpose | |------|---------| | `stock_items.list`, `stock_items.get`, `stock_items.create`, `stock_items.adjust` | Inventory CRUD + manual adjustment. | -| `stock_items.update`, `stock_items.set_pricing` | Patch an item's catalogue fields, or set cost / selling price / markup in a batch. `set_pricing` audits the change in `stock_ledger` with the `actor`. Neither one touches the quantity. | -| `stock_items.set_quantities` | Set or change the stock quantity on up to 100 pasted lines. `mode: "set"` (new quantity on hand) or `mode: "add"` (change to add). Each line resolves by exact SKU, then by a description match (flagged). Unresolved lines are reported, never guessed. `dry_run: true` previews the before/after plan. Quantities may be negative — the till oversells on purpose. Writes a `stock_ledger` row with the acting user and mirrors `price_list.stock_qty`. **Required:** `actor.{id,name}` — `actor.id` must be a 15-char WFDASH user id (see `users.lookup`); it is stamped on every ledger row. A repeated SKU rejects the whole batch — split the paste. | +| `stock_items.update`, `stock_items.set_pricing` | Patch an item's catalogue fields, or set cost / selling price / markup in a batch. Neither one touches the quantity. `set_pricing` writes a `stock_ledger` row **only for a `cost_price` change on an item that already had a cost** — a selling-price or markup edit writes no ledger row, and neither does a first-time cost. | +| `stock_items.set_quantities` | Set or change the stock quantity on up to 100 pasted lines. `mode: "set"` (new quantity on hand) or `mode: "add"` (change to add). Each line resolves by exact SKU, then by a description match (flagged). Unresolved lines are reported, never guessed. `dry_run: true` previews the before/after plan. Quantities may be negative — the till oversells on purpose. Writes a `stock_ledger` row stamped with the acting user and mirrors `price_list.stock_qty`. **Actor:** the route requires `actor.id` (a 15-char WFDASH user id); `actor.name` is optional and defaults to the id. Giellie injects the signed-in operator's own actor after the model's arguments, so it wins over anything the model passed — never supply an actor yourself and never go looking for one. Every ledger row is already attributed. A repeated SKU rejects the whole batch — split the paste. | | `stock_purchases.list`, `stock_purchases.get`, `stock_purchases.create`, `stock_purchases.next_number` | Stock-PO CRUD. | | `stock_purchases.send`, `stock_purchases.receive` | Send to supplier / receive goods. | | `stock_purchases.send_email`, `stock_purchases.retry_email` | Outbound email for stock POs. | diff --git a/src/app/api/mcp/route.test.ts b/src/app/api/mcp/route.test.ts index 6798db0..de838a5 100644 --- a/src/app/api/mcp/route.test.ts +++ b/src/app/api/mcp/route.test.ts @@ -2025,7 +2025,10 @@ const toolNames = body.result.tools.map((t: { name: string }) => t.name); expect(tool.inputSchema.properties.actor).toBeTruthy(); expect(tool.description).toMatch(/selling_price/); expect(tool.description).toMatch(/stock_ledger/); - expect(tool.description).toMatch(/stock_items\.adjust/); + // Quantities are NOT changed by this tool — the description must point at + // the tool that does change them, not the superseded stock_items.adjust. + expect(tool.description).toMatch(/stock_items\.set_quantities/); + expect(tool.description).not.toMatch(/stock_items\.adjust/); }); it("maps snake_case to the service's camelCase and forwards the actor", async () => { diff --git a/src/app/api/mcp/route.ts b/src/app/api/mcp/route.ts index 739ce86..9ee7a96 100644 --- a/src/app/api/mcp/route.ts +++ b/src/app/api/mcp/route.ts @@ -2147,7 +2147,7 @@ const tools = [ { name: "stock_items.update", description: - "Patch catalogue fields on a master stock_item: sku, description, notes, unit, selling_price, markup. For inventory qty use stock_items.adjust. Returns {ok: true, item, previous} on success or {ok: false, reason, message, field?} on rejection. reason is one of: not_found (404), forbidden_field (400), validation_error (400), pb_error (422).", + "Patch catalogue fields on a master stock_item: sku, description, notes, unit, selling_price, markup. For inventory qty use stock_items.set_quantities. Returns {ok: true, item, previous} on success or {ok: false, reason, message, field?} on rejection. reason is one of: not_found (404), forbidden_field (400), validation_error (400), pb_error (422).", inputSchema: { type: "object", required: ["id", "patch"], @@ -2172,7 +2172,7 @@ const tools = [ { name: "stock_items.set_pricing", description: - "Set cost price / selling price / markup on existing stock_items in one batch (max 100 per call). The POS till reads `selling_price` (NOT markup), so set both consistently: selling = cost × (1 + markup/100). Cost changes are audited in stock_ledger with the `actor`. Quantities are NOT changed here (use stock_items.adjust). Returns {results: [...], summary: {total, succeeded, failed}}; each row is {id, ok: true, item, previous} or {id, ok: false, reason, message} (reason: not_found | validation_error | forbidden_field | pb_error) — one bad row does not stop the others.", + "Set cost price / selling price / markup on existing stock_items in one batch (max 100 per call). The POS till reads `selling_price` (NOT markup), so set both consistently: selling = cost × (1 + markup/100). Cost changes are audited in stock_ledger with the `actor`. Quantities are NOT changed here (use stock_items.set_quantities). Returns {results: [...], summary: {total, succeeded, failed}}; each row is {id, ok: true, item, previous} or {id, ok: false, reason, message} (reason: not_found | validation_error | forbidden_field | pb_error) — one bad row does not stop the others.", inputSchema: { type: "object", required: ["updates"],