Role-Augmented Drill-Through Design — 2026-05-16

Authoritative spec for Part N of the FIN Production Parity Tracker (§15, §14 N.2, §14 N.3). Drives implementation of N-02..N-11.

1. Purpose

The agent must feel like an ambient produce expert with perfect memory. Every drill-through (search hit → entity → linked entity → linked record …) should accumulate insight, not act as plain navigation. This document specifies the contract for:

  • Per-role drill matrix (default entry, drill chain, ambient items, live ops, live market) for the 13 Nathel WMS roles.
  • Canonical inventory drill-through chain that every role’s explore-* tool honors when the entity is a commodity or lot.
  • Cross-link contracts (parents/siblings/children/projections/live_state) every entity_profile document must declare so drill-through is symmetrical.
  • next_steps[] shape returned by every explore_* tool so the agent can proactively suggest the next drill.
  • drill.step_taken event subtype + projection so we can learn the actual drill graph per role.

2. Roles in scope (13)

Drawn from the Nathel role hierarchy + sales/buyer/exec personas. See project_nathel_warehouse_roles_and_door_map.md and existing role-emphasis ladder in packages/mcp-server/src/tools/explore-commodity.ts:449-595.

#RoleTierMobile primary?
1qc_inspectorindividualyes
2receiverindividualyes
3department_receiverindividualyes
4warehouse_worker (Box Man)individualyes
5dock_workerindividualyes
6porterindividualyes
7driver (Delivery Driver)individualyes
8foremanteam_leadyes (truck/dock)
9foreman_dispatch / supervisorteam_leadyes
10salesindividualpartial (desk + phone)
11buyerindividualpartial (desk + phone)
12complianceteam_leaddesk-primary
13admin / executivetenantdesk-primary

3. Per-role drill matrix (N.2)

Columns:

  • Default entry — the surface the role lands on after auth (their rail/queue tile or pulse).
  • Drill chain — the most common path; first hop renders the explore card, later hops are accumulated insight.
  • Ambient — the side-dock items composed by packages/agent/src/side-dock for this role.
  • Live operational — projections this role reads on every render.
  • Live market — pricing / vendor / customer signals this role gets surfaced proactively.

3.1 qc_inspector

  • Default entry: Work Threads rail filtered to qc_case. The rail item IS the queue (per project_qc_rail_eq_queue.md).
  • Drill chain: qc_case_thread → load → lot[*] → inspection_history → vendor_profile → commodity_market_position.
  • Ambient: latest 3 holds on same vendor, inspector’s defect-rate trend, recent reinspection triggers fired for this commodity.
  • Live ops: lot_status_projection (block reasons), open holds, today’s inbound count.
  • Live market: vendor quality trend (degrading?), commodity defect signal from signal.quality_observation.

3.2 receiver

  • Default entry: today’s inbound queue (calendar-day aperture; see project_qc_queue_aperture_calendar_day.md).
  • Drill chain: load → dock_face_assignment → pallet_arrival → po_lines → lot.created → handoff_to_qc.
  • Ambient: yard occupancy, dock-face availability, late-arrival probability.
  • Live ops: lot_status_projection, actor_pulse_aggregate, dock map.
  • Live market: vendor ETA observations, weather impact for inbound region.

3.3 department_receiver

  • Default entry: dept-scoped inbound queue (Veg / Tomato / Fruit / Trucking).
  • Drill chain: same as receiver + dept_primary_entry filter and circulation_zone overlay.
  • Ambient: dept-specific staffing, dept turnover rate, dept’s open holds.
  • Live ops: dept’s storage occupancy, dept’s pending QC.
  • Live market: dept’s commodity peak-month + competing inbound.

3.4 warehouse_worker (Box Man)

  • Default entry: pick-ticket / putaway queue scoped to current zone.
  • Drill chain: pick_ticket → lot → storage_area → pallet_position → confirm_put → next_ticket.
  • Ambient: FEFO conflict alerts (buried pallet), zone capacity, neighbour pallet age.
  • Live ops: storage_area_occupancy, pallet_position_history, dwell-time alerts.
  • Live market: none (operational-only).

3.5 dock_worker

  • Default entry: dock-face queue.
  • Drill chain: dock_face → assigned_load → lot[*] → temperature_band → conditional_release.
  • Ambient: temperature exception spike, current dwell, upcoming arrivals.
  • Live ops: dock occupancy, conditional-release queue.
  • Live market: carrier on-time trend.

3.6 porter

  • Default entry: pallet-move queue.
  • Drill chain: move_ticket → pallet → from_zone → to_zone → confirm.
  • Ambient: high-priority moves, congestion forecasts, low-pallet vans.
  • Live ops: van slot occupancy, yard occupancy, frozen-lot watchers.
  • Live market: none.

3.7 driver (Delivery Driver)

  • Default entry: today’s route + load assignments.
  • Drill chain: route → load → customer[*] → drop_sequence → confirmation.
  • Ambient: traffic predictions, customer access windows, weather.
  • Live ops: out-the-door projection, route state machine.
  • Live market: customer order pattern (early/late).

3.8 foreman

  • Default entry: cross-team rollup of receiver + dock + porter queues.
  • Drill chain: team_rollup → bottleneck_zone → load → lot → escalation.
  • Ambient: under-staffed areas, late-arrival rollups, open escalations.
  • Live ops: full pulse, all team projections.
  • Live market: market-wide arrival anomalies (e.g. holiday surge).

3.9 foreman_dispatch / supervisor

  • Default entry: dispatch rollup with out-the-door projection.
  • Drill chain: out_the_door → load_balance → carrier → lot_allocation.
  • Ambient: SLA risk, route exceptions, customer escalation count.
  • Live ops: dispatch wave planning, route state machine, carrier scorecards.
  • Live market: customer-demand-vs-stock gap, late-PO impact.

3.10 sales

  • Default entry: book / customer rail.
  • Drill chain: customer → commodity → lots_available → top_3_lots → lot_detail → vendor → margin → today's_market_price.
  • Ambient: customer last-order recency, market price delta, competitor pricing watch, customer credit posture.
  • Live ops: pickable inventory (get_pickable_inventory_with_gate), open quotes, today’s allocations.
  • Live market: USDA report deltas, Blair / market-chatter feed, brand quality trend for top brand.

3.11 buyer

  • Default entry: PO rail + procurement queue.
  • Drill chain: commodity → market_position → vendor_options → vendor_lot_history → quote → po_draft.
  • Ambient: vendor variance, seasonal-curve risk, current cost-basis vs market, vendor quality.
  • Live ops: open POs, lot freshness on hand, vendor confirmations pending.
  • Live market: USDA + competitor + signal-market-chatter, brand quality.

3.12 compliance

  • Default entry: open-hold rail + FSMA exposure rollup.
  • Drill chain: hold → lot → KDE_completeness → cte_chain → release_or_dispose.
  • Ambient: missing KDE alerts, FSMA 204 24-hr trace probes, recall lineage.
  • Live ops: pulse_admin_active_holds, KDE envelope status per lot.
  • Live market: certification expiry feed, regulatory bulletin feed.

3.13 admin / executive

  • Default entry: tenant-wide rollup pulse.
  • Drill chain: tenant_rollup → bottleneck_dept → load → lot → vendor → market.
  • Ambient: 7-day P&L delta, top-3 risk loads, top-3 opportunity loads, vendor scorecard movers.
  • Live ops: every projection (read-only); platform-admin gets cross-tenant read.
  • Live market: full market intelligence + USDA + competitor + signal.

4. Canonical inventory drill-through chain (N.2)

Every role-tailored chain above that touches inventory must collapse into the same canonical chain at the lot tier, so cross-role hand-offs are seamless:

case_count (cases-on-hand by commodity, aggregated)
  ↓ explore_inventory(commodity)
lots (lot rows for that commodity, sorted by urgency + FEFO)
  ↓ explore_lot(lotCode)
lot_detail (full lot context — receiving, QC, temperature, storage, orders)
  ↓ branch (parallel siblings, not sequential)
  ├── inspection_history (qc events on this lot)
  ├── vendor_profile (explore_vendor for lot.vendor_id)
  ├── lot_sales (sales.order_allocated events touching lot)
  └── commodity_market_position (explore_commodity → pricing + signals)

This chain is implementation-level: explore_inventory already supports the top hops; explore_lot already wires drillThrough.vendor and drillThrough.commodity. N-02..N-06 add next_steps[] for each hop so the agent proactively suggests the next move.

Every entity_profile document (knowledge.kind = entity_profile) MUST declare its position in the entity graph so the agent can drill in either direction without re-querying the world. Contract is enforced by packages/shared/src/cross-link/index.ts (N-08) in either strict (fail) or warn (log) mode.

type CrossLinkContract = {
  // Required for every entity_profile.
  parents:    EntityRef[];   // ancestors (e.g. variety → commodity)
  // Siblings — entities at the same tier connected through a shared parent.
  siblings:   EntityRef[];   // e.g. cucumber Beit Alpha ↔ cucumber Persian
  // Children — entities one tier finer.
  children:   EntityRef[];   // e.g. commodity cucumber → variety Beit Alpha
  // Read-side projections this profile is most often joined with.
  projections: ProjectionRef[];   // e.g. lot_status_projection, actor_pulse_aggregate
  // Live operational state references (event types the profile expects).
  live_state:  LiveStateRef[];    // e.g. ['lot.received','quality.inspection_completed']
};
 
type EntityRef = {
  entityType:
    | "commodity"
    | "variety"
    | "lot"
    | "load"
    | "vendor"
    | "customer"
    | "po"
    | "order"
    | "actor"
    | "department"
    | "zone"
    | "dock_face"
    | "yard_slot"
    | "van";
  entityId: string;
  label: string;
  relation: "parent" | "sibling" | "child" | "associated";
};
 
type ProjectionRef = {
  table: string;
  joinKey: string;
  notes?: string;
};
 
type LiveStateRef = {
  eventType: string;
  cadence: "real_time" | "near_real_time" | "batch";
};

5.1 Examples per entity type

  • commodity profile: parents=[], siblings=[same family commodities], children=[varieties], projections=[lot_status_projection, drill_pattern_projection], live_state=[lot.received, sales.order_created, quality.inspection_completed].
  • variety profile: parents=[commodity], siblings=[same-commodity varieties], children=[brand_variety_combos], projections=[lot_status_projection], live_state=[lot.received].
  • vendor profile: parents=[], siblings=[regional peers, certification peers], children=[brand], projections=[vendor_scorecard], live_state=[procurement.po_created, lot.received].
  • lot profile: parents=[commodity, variety, vendor, load, po], siblings=[same-load lots, same-vendor recent lots], children=[], projections=[lot_status_projection], live_state=[lot.received, quality.inspection_completed, qc.hold_placed, inventory.lot_frozen, sales.order_allocated, lot.shipped].

5.2 Validation modes

  • strictvalidateCrossLinks throws on missing required field (used for new profile ingestion at build time).
  • warn — logs structured warning via logger.warn and returns { ok: false, missing: ... } (default; used in live read paths so legacy profiles don’t break the runtime).

6. next_steps[] contract (N-02..N-06)

Every explore-* tool returns:

type NextStep = {
  /** Tool name the agent should call next. */
  action: string;
  /** Human-readable target ("lot ABC123", "vendor Sunkist", "commodity cucumber"). */
  target: string;
  /** Why this step makes sense — surfaced in the next_steps strip and in
   *  drill.step_taken when the agent acts on it. */
  reason: string;
  /** Float in [0,1]; ranks ordering when the strip is space-constrained. */
  weight: number;
  /** Optional pre-filled params for the action. */
  params?: Record<string, unknown>;
};

Each tool’s next_steps[] is sorted by weight descending and clipped to 6 entries. The drill matrix in §3 drives weighting:

  • Steps the active role’s drill chain enumerates: weight ≥ 0.6.
  • Steps surfaced by the canonical inventory chain: weight ≥ 0.5.
  • Ambient steps (proactive surface, not drill-chain): weight ≥ 0.3.
  • All other suggestions: weight < 0.3.

actorRole is read from the tool input; when absent we fall back to a balanced default ordering (back-compat with existing callers).

7. Side-dock composition (N-07)

packages/agent/src/side-dock/index.ts reads:

  • actor.memory.context.role
  • actor.memory.context.zone (optional — for warehouse roles)
  • actor.memory.context.current_task (optional)

…then composes 3-5 ambient items. The composer file calls per-role functions:

side-dock/
├── index.ts
├── composers/
│   ├── sales.ts
│   ├── buyer.ts
│   ├── qc-inspector.ts
│   ├── receiver.ts
│   ├── warehouse-worker.ts
│   ├── dock-worker.ts
│   ├── porter.ts
│   ├── driver.ts
│   ├── foreman.ts
│   ├── compliance.ts
│   ├── admin.ts
│   └── default.ts

Projections used (placeholder stubs while Part J projections land):

  • lot_status_projection (live; 041 migration applied)
  • actor_pulse_aggregate (live; 042 migration applied)
  • drill_pattern_projection (added by N-09; default behavior = empty rows)
  • vendor_scorecard_projection (stub — Part J)
  • customer_signal_projection (stub — Part J)

When a projection is unavailable, the composer returns the static rule’s fallback message (“Vendor scorecard projection warming — using last 7-day window from events”).

8. drill.step_taken event subtype + projection (N-09)

Event family: signal.* is closed (15 subtypes; see packages/shared/src/events/payloads/signal/registry.ts). Per the project memory note on signal-family closure, we use a NEW namespace:

analytics.drill_step_taken

Schema:

type AnalyticsDrillStepTaken = {
  actor_id: string;
  actor_role: string;
  from_entity_type: string;
  from_entity_id: string;
  to_entity_type: string;
  to_entity_id: string;
  link_type: "drill_chain" | "ambient" | "side_dock" | "next_step" | "breadcrumb_back";
  via_tool: string;
  reason?: string;
};

Projection: drill_pattern_projection table, migration 053. Row per (tenant_id, actor_role, from_entity_type, from_entity_id, to_entity_type, to_entity_id, link_type). count is incremented on every event; observed_at is bumped to latest. RLS required.

Use:

  • Tune next_steps[] weight (steps actually taken get a boost).
  • Tune ambient composer ordering.
  • Detect missing entity_profile cross-links (e.g. an observed drill not represented in any contract).

9. UI surfaces (N-10)

Three new components — React + Tailwind, mobile-first:

9.1 DrillBreadcrumbCard

Renders the cumulative breadcrumb path. Two display modes:

  • Mobile: chevron-separated single line with overflow ellipsis.
  • Desktop: full path with action buttons (Go Back, Go Home).

9.2 SideDockCard

Renders ambient items from §7. Mobile: bottom-sheet collapsible. Desktop: right-rail column. Tone styling matches AgentSuggestsSideDock: info olive / warning amber / opportunity teal.

9.3 NextStepsStripCard

Renders 3-6 next_step[] suggestions as a horizontal strip below the explore card. Each is a tap target; tap dispatches action with params. On dispatch, emit analytics.drill_step_taken with link_type=next_step.

10. E2E budget (N-11)

Sales rep canonical drill: home → cucumber → cases → top-3 lots → lot detail → vendor → margin → today's market price must complete in:

  • ≤6 clicks (counted: 1=cucumber, 2=cases tab/section, 3=top lot, 4=vendor drill, 5=margin drill, 6=market price drill).
  • <10s total wall-clock from first click to last surface rendered.

Tested by packages/workspace/e2e/parity-drill-throughs.spec.ts.

11. Implementation order

  1. N-01 (this document).
  2. N-02..N-06 (per-tool next_steps[]).
  3. N-07 (side-dock composers).
  4. N-08 (cross-link validator).
  5. N-09 (drill.step_taken event + projection migration 053).
  6. N-10 (UI components).
  7. N-11 (Playwright drill spec).

Each task lands as a self-contained commit so we can bisect drill-quality regressions. Verification at every step is pnpm exec vitest run over the package’s contracts + lint + typecheck.

12. Open items / future tasks

  • Drill-pattern-driven prompt tuning (read drill_pattern_projection, rewrite the agent’s drill suggestions when observed reality diverges from contract).
  • Cross-link auto-fix: when validateCrossLinks returns missing parents/ siblings, propose patches to the entity_profile via a tool that the KB authoring surface can approve.
  • Side-dock A/B harness: instrument analytics.side_dock_item_clicked to measure ambient-item lift per role.
  • Role-pair drill chains: e.g. foreman ↔ receiver hand-off — captured via signal.teams_message today, would benefit from a dedicated analytics.role_handoff event subtype.

Updated 2026-05-16. Owner: parity track N-01. Bumps tracked in docs/handoffs/ per session-management protocol.