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.
| # | Role | Tier | Mobile primary? |
|---|---|---|---|
| 1 | qc_inspector | individual | yes |
| 2 | receiver | individual | yes |
| 3 | department_receiver | individual | yes |
| 4 | warehouse_worker (Box Man) | individual | yes |
| 5 | dock_worker | individual | yes |
| 6 | porter | individual | yes |
| 7 | driver (Delivery Driver) | individual | yes |
| 8 | foreman | team_lead | yes (truck/dock) |
| 9 | foreman_dispatch / supervisor | team_lead | yes |
| 10 | sales | individual | partial (desk + phone) |
| 11 | buyer | individual | partial (desk + phone) |
| 12 | compliance | team_lead | desk-primary |
| 13 | admin / executive | tenant | desk-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-dockfor 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 (perproject_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_entryfilter andcirculation_zoneoverlay. - 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.
5. Cross-link contracts (N.3)
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
commodityprofile: parents=[], siblings=[same family commodities], children=[varieties], projections=[lot_status_projection, drill_pattern_projection], live_state=[lot.received, sales.order_created, quality.inspection_completed].varietyprofile: parents=[commodity], siblings=[same-commodity varieties], children=[brand_variety_combos], projections=[lot_status_projection], live_state=[lot.received].vendorprofile: parents=[], siblings=[regional peers, certification peers], children=[brand], projections=[vendor_scorecard], live_state=[procurement.po_created, lot.received].lotprofile: 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
strict—validateCrossLinksthrows on missing required field (used for new profile ingestion at build time).warn— logs structured warning vialogger.warnand 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.roleactor.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
- N-01 (this document).
- N-02..N-06 (per-tool next_steps[]).
- N-07 (side-dock composers).
- N-08 (cross-link validator).
- N-09 (drill.step_taken event + projection migration 053).
- N-10 (UI components).
- 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
validateCrossLinksreturns 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_clickedto measure ambient-item lift per role. - Role-pair drill chains: e.g. foreman ↔ receiver hand-off — captured
via
signal.teams_messagetoday, would benefit from a dedicatedanalytics.role_handoffevent subtype.
Updated 2026-05-16. Owner: parity track N-01. Bumps tracked in
docs/handoffs/ per session-management protocol.