Skip to content

feat(mcp): cache-backed name lookup for unchanged-party fields on modify cards (#722 follow-up) #759

@dougborg

Description

@dougborg

What

`build_po_modify_ui` renders unchanged party fields from `prior_state`. `RegularPurchaseOrder.to_dict()` (the source of `prior_state` via `serialize_for_prior_state`) carries the nested `supplier` object (with name) but ONLY a flat `location_id` — no `location_name`.

So when location is unchanged, the modify card renders:

```
Location ID: 1
```

…instead of the create card's:

```
Location: Main Warehouse (1)
```

Caught by Copilot review on #755. The same gap will apply to:

Why this is a follow-up, not in #722

The create path resolves `location_name` via `resolve_entity_name(services.typed_cache.catalog, ...)` in the foundation tool's response-building code (`modify_purchase_order` doesn't do this today). To fix:

  1. The modify-tool implementations (`modify_purchase_order`, `delete_purchase_order`, `correct_purchase_order`, and SO/MO/stock-transfer equivalents) would each need to enrich `prior_state` with looked-up names BEFORE returning the `ModificationResponse`.

  2. OR `run_modify_plan` and the correction-replay functions in `_modification_dispatch.py` would gain a cache-enrichment hook so the dispatcher does the lookup centrally before populating `prior_state`.

(2) is cleaner but threads `services.typed_cache` into the dispatcher signature. The renderer (`_normalize_po_prior_state`) should NOT do cache access directly — that breaks the layering between pure rendering and data fetching.

Either path is a meaningful piece of work touching multiple foundation tools; outside the scope of #722 (PO modify card + shared helpers). Track separately so the modify-card umbrella can stay focused on the rendering work.

Scope

  • Add a cache-enrichment helper in `_modification_dispatch.py` that walks `prior_state` and resolves linked-entity names from `services.typed_cache.catalog` (location_name from `location_id`, customer_name from `customer_id` if not already nested, variant.sku from `variant_id`, etc.).
  • Plumb it through `run_modify_plan` and the three corrections (`correct_manufacturing_order`, `correct_sales_order`, `correct_purchase_order`).
  • Update each per-entity normalizer (`_normalize_po_prior_state`, then SO/MO/stock-transfer/item equivalents from design(mcp): build_so_modify_ui — diff-decorated SO modify card (#721 child) #723-design(mcp): build_item_modify_ui — diff-decorated item modify card (#721 child) #726) to pick up the enriched values.
  • Tests pin the enrichment by passing a fixture cache and asserting the rendered Location line shows the looked-up name.

Priority

P2 — UX polish, not a correctness bug. The current rendering shows `Location ID: 1` which is unambiguous, just less rich than the create-card equivalent. Land alongside or shortly after #723 (SO modify) so the same enrichment infrastructure benefits all the modify cards.

Workstream: Cards.

Refs #755 (Copilot review surfacing the gap), #722 (parent PO modify PR), #721 (modify-card umbrella).

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions