Skip to content

Commit 5d7c83f

Browse files
dougborgclaude
andcommitted
docs(mcp): add /correct-shipped-build skill + document closed-record correction non-coverage
Two follow-ups to the closed-record correction tools shipped in #536: 1. New `/correct-shipped-build` skill orchestrates `correct_manufacturing_order` + `correct_sales_order` (and the future `split_via_bom` from #543) for the originating workflow: a customer received different items than Katana recorded, and the operator needs to walk MO → SO → optional inventory split with operator-judgment gates between phases. Includes a deferral fallback for the split phase so the skill is usable today even with #543 still open. 2. Help-resource note explaining why `correct_purchase_order` and `correct_stock_transfer` aren't in the pattern: PO receipts are append-only at the row level (#532 has the workaround design); stock transfers have no completion timestamp + immutable rows so the close- state pattern adds no value (#533 — operators should use `create_stock_adjustment` / `delete_stock_transfer` + recreate / `modify_stock_transfer` for header metadata instead). Skills under `.claude/skills/` are auto-discovered by name, so the skill is immediately usable in-repo. `.harness-lock.json` records its provenance as `source: local` for harness-kit drift tracking. Closes #533. Implements #534. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 786c149 commit 5d7c83f

4 files changed

Lines changed: 308 additions & 1 deletion

File tree

Lines changed: 282 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,282 @@
1+
---
2+
name: correct-shipped-build
3+
description: >-
4+
Resolve a shipped-order discrepancy — customer received different items than
5+
Katana recorded. Pull the customer-facing record, locate the linked Katana
6+
SO/MO, decide whether a BOM-driven split is needed (when an assembled good
7+
shipped as its component parts), then apply correct_manufacturing_order +
8+
correct_sales_order with operator-judgment gates between phases. Verify
9+
against the source of truth at the end.
10+
argument-hint: "[order id from source-of-truth system]"
11+
allowed-tools:
12+
- mcp__katana-erp__get_sales_order
13+
- mcp__katana-erp__get_manufacturing_order
14+
- mcp__katana-erp__get_manufacturing_order_recipe
15+
- mcp__katana-erp__list_sales_orders
16+
- mcp__katana-erp__correct_manufacturing_order
17+
- mcp__katana-erp__correct_sales_order
18+
- mcp__katana-erp__modify_manufacturing_order
19+
- mcp__katana-erp__modify_sales_order
20+
- mcp__katana-erp__check_inventory
21+
- mcp__katana-erp__create_stock_adjustment
22+
---
23+
24+
# /correct-shipped-build — shipped-order discrepancy resolution
25+
26+
## PURPOSE
27+
28+
When a customer received different items than Katana recorded — wrong
29+
ingredient on a finished good, wrong line item on a sales order, or a
30+
finished good that shipped as its components — restore Katana to ground
31+
truth without losing the original close-state metadata (`done_date`,
32+
`picked_date`, per-production timestamps, fulfillment tracking).
33+
34+
## CRITICAL
35+
36+
- **Ground truth is the customer-facing record, not Katana.** Pull the
37+
Shopify / e-commerce / fulfillment-system order first; treat it as
38+
authoritative. Don't assume Katana's recorded fulfillment is right —
39+
this skill exists because it isn't.
40+
- **Preview every correction before applying.** Both `correct_manufacturing_order`
41+
and `correct_sales_order` default to `preview=true` for a reason. The
42+
operator should read the planned action list and confirm before any
43+
mutations land.
44+
- **Both MO and SO may need correction, in this order.** When a finished
45+
good's recipe was wrong (consumed the wrong ingredient), fix the MO
46+
first — that's the upstream truth. Then fix the SO if its line items
47+
also drifted. SO line items can reference variants the MO produced, so
48+
MO-first is the safer order.
49+
- **Inventory side-effects belong to a separate decision.** If the
50+
finished good was disassembled mid-fulfillment — i.e. an assembled
51+
good was broken into its BOM components and shipped as parts — the
52+
MO/SO corrections alone don't reshuffle inventory. That's
53+
`split_via_bom`'s job. Don't conflate the two; decide explicitly
54+
whether a split is needed.
55+
56+
## STANDARD PATH
57+
58+
### Phase 1 — Pull the source-of-truth record
59+
60+
Ask the operator for the source-system order id (Shopify order, etc.).
61+
Read the customer-facing record and write down what actually shipped:
62+
SKUs, quantities, condition / assembly state. This is the **expected
63+
state** every later phase compares against.
64+
65+
If you have a Chrome tab or other tool already open on the order, use it.
66+
Otherwise the operator may need to open it manually and read off the
67+
relevant fields.
68+
69+
### Phase 2 — Locate the linked Katana SO and MO
70+
71+
```
72+
list_sales_orders(customer_id=..., delivered_after=..., delivered_before=...)
73+
```
74+
75+
Filter by customer name + delivery date window to find the candidate SO.
76+
Once you have the SO id:
77+
78+
```
79+
get_sales_order(order_id=...)
80+
```
81+
82+
Inspect:
83+
- SO line items — what Katana thinks shipped
84+
- `linked_manufacturing_order_id` on each row — the upstream MO, if any
85+
86+
If a row has a linked MO:
87+
88+
```
89+
get_manufacturing_order(order_id=...)
90+
```
91+
92+
Note the MO's `variant_id` (the finished good) and the status (`DONE` /
93+
`PARTIALLY_COMPLETED`).
94+
95+
`get_manufacturing_order` defaults to `include_rows="blocking"`, which
96+
hides recipe rows whose ingredients are in stock. To inspect the *full*
97+
recipe before deciding what to correct, fetch the dedicated recipe view:
98+
99+
```
100+
get_manufacturing_order_recipe(manufacturing_order_id=...)
101+
```
102+
103+
This returns every recipe row regardless of ingredient-availability
104+
status — that's what you want when figuring out which row consumed the
105+
wrong variant.
106+
107+
### Phase 3 — Decide whether a split is needed
108+
109+
A **split** means: the finished good produced by the MO was disassembled
110+
into its BOM components before shipment, and the customer received the
111+
components instead of the assembled good.
112+
113+
Operator-judgment criteria:
114+
115+
- The customer-confirmed item list (phase 1) lists individual component
116+
SKUs instead of the assembled SKU.
117+
- The MO produced a single assembled good, but the SO's line item now
118+
needs to reference component SKUs.
119+
- The warehouse / fulfillment notes confirm the split.
120+
121+
If yes, plan to use `split_via_bom` in phase 6. If no, skip phase 6.
122+
123+
If `split_via_bom` is not yet shipped (issue #543 still open at the time
124+
of writing), document the intended split — finished-good variant id,
125+
location id, quantity, and the timing — and proceed without it. The
126+
MO/SO corrections still land cleanly; the inventory reshuffling can be
127+
applied as a follow-up when the tool ships.
128+
129+
### Phase 4 — Correct the MO (if the recipe was wrong)
130+
131+
If the MO consumed the wrong ingredient (e.g., the wrong variant of a
132+
component, or an incorrect quantity per unit):
133+
134+
```
135+
correct_manufacturing_order(
136+
id=...,
137+
ingredient_changes=[
138+
{
139+
old_variant_id: <variant currently on the recipe row>,
140+
new_variant_id: <variant that was actually consumed>,
141+
planned_quantity_per_unit: <if changed; otherwise omit>,
142+
},
143+
...
144+
],
145+
preview=true,
146+
)
147+
```
148+
149+
Review the planned action sequence — revert → recipe edit → recreate
150+
production → re-close with backdated done_date / production_date.
151+
Confirm the captured close-state matches reality. Then re-call with
152+
`preview=false`.
153+
154+
The tool requires the MO to be in `DONE` or `PARTIALLY_COMPLETED`
155+
status. If it's still `IN_PROGRESS`, use `modify_manufacturing_order`
156+
directly — there's no close-state to preserve.
157+
158+
### Phase 5 — Correct the SO (if line items were wrong)
159+
160+
If the SO's line items don't match what the customer received:
161+
162+
```
163+
correct_sales_order(
164+
id=...,
165+
line_changes=[
166+
{
167+
old_variant_id: <variant currently on the row>,
168+
new_variant_id: <variant actually shipped>,
169+
quantity: <if changed; must be ≥ already-fulfilled qty>,
170+
price_per_unit: <if changed>,
171+
},
172+
...
173+
],
174+
preview=true,
175+
)
176+
```
177+
178+
Preview surfaces the planned action sequence — delete fulfillments →
179+
revert → row edits → re-add fulfillments with original `picked_date`
180+
re-close. Confirm and re-call with `preview=false`.
181+
182+
`correct_sales_order` only updates rows in place. If you need to **add
183+
or remove** a line item entirely, use `modify_sales_order` directly —
184+
the close-state restore would otherwise lose the row-id mapping the
185+
fulfillments depend on.
186+
187+
### Phase 6 — Apply the BOM-driven split (if decided in phase 3)
188+
189+
> **Status: deferred.** The `split_via_bom` tool isn't shipped yet
190+
> (issue #543). It's intentionally **not** listed in this skill's
191+
> `allowed-tools` so the skill can load and run today; when the tool
192+
> ships, add `mcp__katana-erp__split_via_bom` to the allowlist and
193+
> remove this status callout.
194+
195+
When `split_via_bom` ships, the call shape will be:
196+
197+
```
198+
split_via_bom(
199+
variant_id=<finished good>,
200+
location_id=<where the inventory transformation happened>,
201+
quantity=<how many were split>,
202+
stock_adjustment_date=<just before the customer's delivery date>,
203+
preview=true,
204+
)
205+
```
206+
207+
It generates a single mixed-sign stock adjustment: `-quantity` of the
208+
finished good, `+(quantity × planned_quantity_per_unit)` of each BOM
209+
ingredient, cost-balanced to net zero.
210+
211+
**Until then:** file a follow-up issue documenting the intended split
212+
parameters (finished-good variant id, location id, quantity, timing)
213+
and skip this phase. The MO/SO corrections still leave the records in
214+
the right shape; only the inventory reshuffling is deferred.
215+
216+
### Phase 7 — Verify
217+
218+
Re-fetch the corrected records:
219+
220+
```
221+
get_manufacturing_order(order_id=...)
222+
get_sales_order(order_id=...)
223+
```
224+
225+
Confirm the close-state restored cleanly: status back to its original
226+
value, dates backdated to the originals, no new "today" timestamps
227+
sneaking in.
228+
229+
Sanity-check inventory on the affected SKUs:
230+
231+
```
232+
check_inventory(skus_or_variant_ids=[...])
233+
```
234+
235+
Expected vs. actual stock levels should reconcile against the customer-
236+
confirmed shipment. Negative availability is a red flag — it means the
237+
correction over-corrected somewhere.
238+
239+
### Phase 8 — Summarize
240+
241+
Report to the operator:
242+
243+
- **Source-system order id** — what was being corrected.
244+
- **Katana SO and MO ids** — links to the corrected records.
245+
- **Changes applied** — MO recipe rows swapped (with variants), SO line
246+
items updated, split applied (yes/no, with parameters).
247+
- **Inventory delta** — affected SKUs and the qty change at each.
248+
- **Verification** — the inventory-check result and any flagged
249+
anomalies.
250+
- **Deferred work** — if `split_via_bom` wasn't available, the
251+
follow-up issue or manual adjustment that's still needed.
252+
253+
## EDGE CASES
254+
255+
- **No linked MO on the SO** — the SO is for a purchased finished good,
256+
not a manufactured one. Skip phase 4 entirely; only the SO needs
257+
correction.
258+
- **MO is `IN_PROGRESS`**`correct_manufacturing_order` will refuse.
259+
The MO hasn't shipped yet, so there's no close-state to preserve. Use
260+
`modify_manufacturing_order` directly.
261+
- **Same variant appears on multiple recipe rows / SO rows** — the
262+
correction tools refuse on ambiguity. Use `modify_manufacturing_order`
263+
/ `modify_sales_order` with the explicit row id, or split the
264+
correction into two passes that disambiguate by other means.
265+
- **New row quantity below the already-fulfilled quantity**
266+
`correct_sales_order` preflight refuses. Either keep the original
267+
quantity, or fix the fulfillment record manually first.
268+
- **Inventory goes negative after corrections** — the corrections
269+
over-shot. Likely cause: the MO/SO drift was only a partial
270+
representation of what shipped, and the rest needs a stock adjustment
271+
or a parallel correction on a sibling SO. Don't paper over with a
272+
goodwill stock adjustment without understanding the root cause.
273+
- **Customer received a superset (got extras)** — corrections alone
274+
won't capture this. Either confirm a picking-error / goodwill add-on
275+
with the warehouse, or treat the extras as a separate incident.
276+
277+
## RELATED
278+
279+
- PR #536`correct_manufacturing_order`, `correct_sales_order`
280+
- Issue #543`split_via_bom` (BOM-driven inventory split, pending)
281+
- Issue #523 — Closed-record corrections umbrella
282+
- Issue #532`correct_purchase_order` (separate workflow; not used here)

.harness-lock.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,10 @@
4444
"source": "local",
4545
"note": "Migrated from .claude/commands/pre-commit.md"
4646
},
47+
".claude/skills/correct-shipped-build/SKILL.md": {
48+
"source": "local",
49+
"note": "Orchestrates correct_manufacturing_order + correct_sales_order + split_via_bom for shipped-order discrepancies; tracked under #534. Migrate to plugin distribution when #525 lands."
50+
},
4751
".claude/skills/review/SKILL.md": {
4852
"source": "local",
4953
"note": "Migrated from .claude/commands/review.md; delegates to code-reviewer agent"

katana_mcp_server/src/katana_mcp/resources/help.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,27 @@
151151
- The same variant appears on multiple rows and you want to disambiguate
152152
with the explicit row ID.
153153
154+
**No `correct_purchase_order` or `correct_stock_transfer`** — those entity
155+
types don't fit the pattern:
156+
157+
- **Purchase orders** — Katana's receipt records are append-only at the row
158+
level. `modify_purchase_order` can patch a row's `received_date` (when
159+
already set), but it **cannot zero out a row's `received_quantity`** —
160+
there's no unreceive endpoint, and `quantity` / `variant_id` on a row are
161+
only updatable when `received_date` is null. So a clean reopen path
162+
doesn't exist (tracked in #532). For inventory-level recovery today:
163+
`create_stock_adjustment` at the original cost basis to undo the
164+
receipt's stock effect — this fixes inventory, but the PO record's
165+
received quantities remain historically incorrect until Katana exposes an
166+
unreceive / reset mechanism. Plan for the audit trail to show the
167+
original receipt + the compensating adjustment, not a corrected receipt.
168+
- **Stock transfers** — no completion timestamp on the model and rows are
169+
immutable, so the close-state pattern adds no value. For corrections:
170+
`create_stock_adjustment` at the destination location for quantity
171+
discrepancies; `delete_stock_transfer` + `create_stock_transfer` for wrong
172+
variants; `modify_stock_transfer` for header metadata (works on RECEIVED
173+
transfers as-is).
174+
154175
## Output Format
155176
156177
Every list/get/search and reporting tool accepts a shared `format` parameter:

uv.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)