[INFRA-180] Remove broken state-listing path from wo-po-lookup #92

Merged
amoussa1229 merged 1 commit from bug/INFRA-180-remove-state-listing into main 2026-07-15 22:51:54 +00:00
3 changed files with 35 additions and 66 deletions

View file

@ -50,7 +50,7 @@ EventBridge (daily 02:00 UTC)
- **Payment & Invoice Status** — check if a vendor has been paid, if an invoice is scheduled, look up by check number - **Payment & Invoice Status** — check if a vendor has been paid, if an invoice is scheduled, look up by check number
- **Work Order Lookups** — status, assignments, comments, and full history by WO number - **Work Order Lookups** — status, assignments, comments, and full history by WO number
- **Purchase Order Lookups** — status, line items, supplier, and ship-to details by PO number - **Purchase Order Lookups** — status, line items, supplier, and ship-to details by PO number
- **Site Lookups** — Amazon facility address and details by site code, or list all sites in a state - **Site Lookups** — Amazon facility address and details by site code
- **Company Policies & SOPs** — employee handbook, SA8000 compliance, and operational procedures - **Company Policies & SOPs** — employee handbook, SA8000 compliance, and operational procedures
### Vendor Query Priority ### Vendor Query Priority
@ -93,10 +93,10 @@ Owner-side changes to any of these tables must be coordinated with this repo bef
| `WorkOrders` | `procurement-ingest` / `WorkorderIngestStack` (`cdk/wo_stack.py`) | PK `work_order_id` (S). `GetItem` by id; full-table `Scan` in `workorder-sync`. | `work_order_id`, `description`, `wo_status`, `customer`, `site_code`, `building`, `address`, `severity`, `priority`, `assigned_to`, `date_reported`, `scheduled_start`, `due_date`, `updated_at` | Shared CMK (`/seahaven/dynamodb/cmk-arn`) | | `WorkOrders` | `procurement-ingest` / `WorkorderIngestStack` (`cdk/wo_stack.py`) | PK `work_order_id` (S). `GetItem` by id; full-table `Scan` in `workorder-sync`. | `work_order_id`, `description`, `wo_status`, `customer`, `site_code`, `building`, `address`, `severity`, `priority`, `assigned_to`, `date_reported`, `scheduled_start`, `due_date`, `updated_at` | Shared CMK (`/seahaven/dynamodb/cmk-arn`) |
| `WorkOrderComments` | `procurement-ingest` / `WorkorderIngestStack` (`cdk/wo_stack.py`) | PK `work_order_id` (S), SK `comment_id` (S). `Query` by `work_order_id`. | `work_order_id`, `created_at` (used for sort), `commenter`, `text` | Shared CMK (`/seahaven/dynamodb/cmk-arn`) | | `WorkOrderComments` | `procurement-ingest` / `WorkorderIngestStack` (`cdk/wo_stack.py`) | PK `work_order_id` (S), SK `comment_id` (S). `Query` by `work_order_id`. | `work_order_id`, `created_at` (used for sort), `commenter`, `text` | Shared CMK (`/seahaven/dynamodb/cmk-arn`) |
| `purchase-orders` | `procurement-ingest` / `po-ingest` (`cdk/po_stack.py`) | PK `po_number` (S). `GetItem` by number; full-table `Scan` in `po-sync`. | `po_number`, `po_status`, `email_type`, `cancelled_at`, `total_amount`, `currency`, `supplier.name`, `source_system`, `order_date`, `revision_date`, `payment_terms`, `requisition_number`, `department`, `submitted_by`, `on_behalf_of`, `ship_to.{name,address,location_code,attn}`, `line_items[].{description,amount,currency,need_by,category}` | Shared CMK (`/seahaven/dynamodb/cmk-arn`) | | `purchase-orders` | `procurement-ingest` / `po-ingest` (`cdk/po_stack.py`) | PK `po_number` (S). `GetItem` by number; full-table `Scan` in `po-sync`. | `po_number`, `po_status`, `email_type`, `cancelled_at`, `total_amount`, `currency`, `supplier.name`, `source_system`, `order_date`, `revision_date`, `payment_terms`, `requisition_number`, `department`, `submitted_by`, `on_behalf_of`, `ship_to.{name,address,location_code,attn}`, `line_items[].{description,amount,currency,need_by,category}` | Shared CMK (`/seahaven/dynamodb/cmk-arn`) |
| `verified-sites` | `procurement-ingest` / `po-ingest` (`cdk/po_stack.py`) | PK `siteCode` (S). `GetItem` by code, **and `Query` on GSI `by-state`** for state listings. | `siteCode`, `address`, `fullAddress`, `city`, `state`, `zip`, `latitude`, `longitude`, `notes` | AWS-managed (NOT CMK) | | `verified-sites` | `procurement-ingest` / `po-ingest` (`cdk/po_stack.py`) | PK `siteCode` (S). `GetItem` by code only. | `siteCode`, `address`, `fullAddress`, `city`, `state`, `zip`, `latitude`, `longitude`, `notes` | AWS-managed (NOT CMK) |
| `PaymentsDashboard` | `payments-dashboard` / `payments-dashboard` (`template.yaml`) | PK `pk` (S), format `payment#<check_number>`. `GetItem` by pk; full-table `Scan` filtered `begins_with(pk, "payment#")`. | `pk`, `check_number`, `payee`, `amount_usd`, `method`, `status`, `send_payment_on`, `clear_status`, `cleared_date`, `invoice_numbers`, `company_subsidiary`, `bank_reference` | CMK (shared) | | `PaymentsDashboard` | `payments-dashboard` / `payments-dashboard` (`template.yaml`) | PK `pk` (S), format `payment#<check_number>`. `GetItem` by pk; full-table `Scan` filtered `begins_with(pk, "payment#")`. | `pk`, `check_number`, `payee`, `amount_usd`, `method`, `status`, `send_payment_on`, `clear_status`, `cleared_date`, `invoice_numbers`, `company_subsidiary`, `bank_reference` | CMK (shared) |
**Known broken contract (verified-sites `by-state` GSI):** `lambda/wo-po-lookup/index.ts` still queries `IndexName: 'by-state'` for state-based site listings, but the owner removed that GSI on 2026-06-03 (procurement-ingest audit M-20, "0 reads in 30d"). State listings (`lookup_site` with a `state` arg) therefore fail at runtime today. Point-lookups by `siteCode` are unaffected. This needs either the GSI restored owner-side or the state-listing path removed from the bot. **Resolved contract break (verified-sites `by-state` GSI, INFRA-180):** the owner removed the `by-state` GSI on 2026-06-03 (procurement-ingest audit M-20, "0 reads in 30d"), which broke the bot's state-based site listings at runtime. Per the owner's decision the state-listing path was removed from the bot entirely (query code, `lookup_site` `state` parameter, and agent instructions) rather than restoring the GSI. `lookup_site` now supports `siteCode` point lookups only.
## Documentation ## Documentation

View file

@ -192,60 +192,28 @@ async function lookupPurchaseOrder(poNumber: string): Promise<string> {
// ── Site lookup ───────────────────────────────────────────────────────────── // ── Site lookup ─────────────────────────────────────────────────────────────
async function lookupSite(siteCode: string, state?: string): Promise<string> { async function lookupSite(siteCode: string): Promise<string> {
// Exact site code lookup const res = await dynamo.send(
if (siteCode) { new GetItemCommand({
const res = await dynamo.send( TableName: process.env.SITES_TABLE!,
new GetItemCommand({ Key: { siteCode: { S: siteCode.toUpperCase() } },
TableName: process.env.SITES_TABLE!, }),
Key: { siteCode: { S: siteCode.toUpperCase() } }, );
}),
);
if (res.Item) { if (res.Item) {
const s = unmarshall(res.Item) as Record<string, any>; const s = unmarshall(res.Item) as Record<string, any>;
const lines: string[] = []; const lines: string[] = [];
lines.push(`SITE ${s.siteCode}`); lines.push(`SITE ${s.siteCode}`);
lines.push(`Address: ${s.address === 'TBD' ? 'TBD (not yet established)' : s.fullAddress || s.address}`); lines.push(`Address: ${s.address === 'TBD' ? 'TBD (not yet established)' : s.fullAddress || s.address}`);
if (s.city) lines.push(`City: ${s.city}`); if (s.city) lines.push(`City: ${s.city}`);
if (s.state) lines.push(`State: ${s.state}`); if (s.state) lines.push(`State: ${s.state}`);
if (s.zip) lines.push(`Zip: ${s.zip}`); if (s.zip) lines.push(`Zip: ${s.zip}`);
if (s.latitude && s.longitude) lines.push(`Coordinates: ${s.latitude}, ${s.longitude}`); if (s.latitude && s.longitude) lines.push(`Coordinates: ${s.latitude}, ${s.longitude}`);
if (s.notes) lines.push(`Notes: ${s.notes}`); if (s.notes) lines.push(`Notes: ${s.notes}`);
return lines.join('\n');
}
return `No site found for code "${siteCode}". Please provide a valid Amazon site code (e.g., "ABE2", "DFW6").`;
}
// State-based search via GSI
if (state) {
const res = await dynamo.send(
new QueryCommand({
TableName: process.env.SITES_TABLE!,
IndexName: 'by-state',
KeyConditionExpression: '#st = :state',
ExpressionAttributeNames: { '#st': 'state' },
ExpressionAttributeValues: { ':state': { S: state.toUpperCase() } },
}),
);
const sites = (res.Items ?? []).map((item) => unmarshall(item) as Record<string, any>);
if (sites.length === 0) {
return `No sites found in state "${state}".`;
}
sites.sort((a, b) => (a.siteCode ?? '').localeCompare(b.siteCode ?? ''));
const lines = [`Sites in ${state.toUpperCase()} (${sites.length} total):`];
for (const s of sites) {
const addr = s.address === 'TBD' ? 'TBD' : `${s.address}, ${s.city}`;
const note = s.notes ? ` — ${s.notes}` : '';
lines.push(`- ${s.siteCode}: ${addr}${note}`);
}
return lines.join('\n'); return lines.join('\n');
} }
return 'Please provide a site code (e.g., "ABE2") or a state abbreviation (e.g., "TX") to search.'; return `No site found for code "${siteCode}". Please provide a valid Amazon site code (e.g., "ABE2", "DFW6").`;
} }
// ── Payment lookups ───────────────────────────────────────────────────────── // ── Payment lookups ─────────────────────────────────────────────────────────
@ -355,10 +323,16 @@ export const handler = async (event: BedrockActionEvent): Promise<BedrockActionR
if (!poNum) return makeResponse(event, 'Please provide a purchase order number.'); if (!poNum) return makeResponse(event, 'Please provide a purchase order number.');
result = await lookupPurchaseOrder(poNum); result = await lookupPurchaseOrder(poNum);
} else if (event.function === 'lookup_site') { } else if (event.function === 'lookup_site') {
const code = params.site_code?.trim() ?? ''; const code = params.site_code?.trim();
const state = params.state?.trim(); if (!code) {
if (!code && !state) return makeResponse(event, 'Please provide a site code or state abbreviation.'); // Older prepared agent versions may still pass a `state` parameter;
result = await lookupSite(code, state); // listing sites by state was removed in INFRA-180 (by-state GSI deleted).
const message = params.state
? 'Listing sites by state is no longer supported. Please provide a site code (e.g., "ABE2").'
: 'Please provide a site code (e.g., "ABE2").';
return makeResponse(event, message);
}
result = await lookupSite(code);
} else if (event.function === 'lookup_payment_by_vendor') { } else if (event.function === 'lookup_payment_by_vendor') {
const name = params.vendor_name?.trim(); const name = params.vendor_name?.trim();
if (!name) return makeResponse(event, 'Please provide a vendor or payee name.'); if (!name) return makeResponse(event, 'Please provide a vendor or payee name.');

View file

@ -190,7 +190,7 @@ You help employees with:
3. Employee handbook questions 3. Employee handbook questions
4. Work order lookups — status, history, assignments, comments, and details for any work order by its WO number 4. Work order lookups — status, history, assignments, comments, and details for any work order by its WO number
5. Purchase order lookups — status, line items, suppliers, ship-to details, and dates for any PO by its PO number 5. Purchase order lookups — status, line items, suppliers, ship-to details, and dates for any PO by its PO number
6. Amazon site lookups — address, location, and details for any Amazon facility by its site code (e.g., "ABE2", "DFW6"), or listing all sites in a given state 6. Amazon site lookups — address, location, and details for any Amazon facility by its site code (e.g., "ABE2", "DFW6")
7. Payment and invoice status — whether a vendor has been paid, if an invoice is scheduled, check details 7. Payment and invoice status — whether a vendor has been paid, if an invoice is scheduled, check details
## Vendor Query Rules — STRICTLY follow this priority order: ## Vendor Query Rules — STRICTLY follow this priority order:
@ -213,7 +213,7 @@ When a user asks about a work order (WO) or purchase order (PO) by number, ALWAY
When a user asks about payments, invoices, or whether a vendor has been paid, use the WO_PO_Lookup payment functions. For vendor payment searches, use lookup_payment_by_vendor. For invoice number lookups, use lookup_payment_by_invoice. For check number lookups, use lookup_payment_by_check. Present results clearly: check number, payee, amount, status, method, and relevant dates. Payment records cover the last 90 days. When a user asks about payments, invoices, or whether a vendor has been paid, use the WO_PO_Lookup payment functions. For vendor payment searches, use lookup_payment_by_vendor. For invoice number lookups, use lookup_payment_by_invoice. For check number lookups, use lookup_payment_by_check. Present results clearly: check number, payee, amount, status, method, and relevant dates. Payment records cover the last 90 days.
## Site Lookups: ## Site Lookups:
When a user asks about an Amazon site by its code (e.g., "ABE2", "DFW6", "WWY1"), ALWAYS use the WO_PO_Lookup.lookup_site function. You can also list all sites in a state by passing the state abbreviation. Do NOT use the knowledge base for site code lookups — the action group queries the database directly and is more reliable. When a user asks about an Amazon site by its code (e.g., "ABE2", "DFW6", "WWY1"), ALWAYS use the WO_PO_Lookup.lookup_site function. Do NOT use the knowledge base for site code lookups — the action group queries the database directly and is more reliable. Site lookups require a site code; listing sites by state is not supported.
## General Questions: ## General Questions:
Use the knowledge base for policy, SOP, SA8000 compliance, and handbook questions. Cite the specific document or section when possible. If the information is not in the knowledge base, say so clearly — do not guess. Use the knowledge base for policy, SOP, SA8000 compliance, and handbook questions. Cite the specific document or section when possible. If the information is not in the knowledge base, say so clearly — do not guess.
@ -374,17 +374,12 @@ Keep responses concise, professional, and actionable.`;
}, },
{ {
name: 'lookup_site', name: 'lookup_site',
description: 'Look up an Amazon facility site by its site code or list all sites in a state. Returns address, city, state, coordinates, and notes.', description: 'Look up an Amazon facility site by its site code. Returns address, city, state, coordinates, and notes.',
parameters: { parameters: {
site_code: { site_code: {
type: 'string', type: 'string',
description: 'The Amazon site code (e.g., "ABE2", "DFW6", "WWY1")', description: 'The Amazon site code (e.g., "ABE2", "DFW6", "WWY1")',
required: false, required: true,
},
state: {
type: 'string',
description: 'Two-letter state abbreviation to list all sites in that state (e.g., "TX", "CA", "GA")',
required: false,
}, },
}, },
}, },