1---2name: reflect-3-attempt-01-343description: ProcureOps Task Group Skill4---5# ProcureOps Task Group Skill67## 1. Environment & API Usage8- **Base URL**: Use the runner-provided base URL. Do not assume `localhost` or `127.0.0.1`.9- **Endpoints**: `/programs`, `/purchase_orders`, `/purchase_requisitions`, `/receipts`, `/ap_invoices`, `/contracts`, `/suppliers`, `/vendor_risk_events`, `/payments`, `/approval_events`, `/budget_snapshots`, `/items`.10- **Query reliability**: Filtering via query parameters (`?po_id=...`) is unreliable. Fetch the full list (`GET /{endpoint}`) and filter locally in Python.11- **Single-record fetch**: You can also fetch by ID path (`GET /ap_invoices/AP-LUMA-7714`), but bulk fetch is usually faster.12- **Data freshness**: Treat the API as the system of truth. Local memos/packets provide target IDs and narrative context, but live records override static text.1314## 2. General Output Conventions15- **Return format**: Return **only** a JSON object matching the provided `answer_template.json`.16- **Currency**: All USD amounts must be rounded to **cents** (2 decimal places). Use standard `round(value, 2)`.17- **Percentages**: Round to the precision specified in the template (e.g., 1 decimal place for `quantity_variance_pct`).18- **Ratios**: Round to the precision specified (e.g., 4 decimal places for `receipt_completion_ratio`). JSON does not require trailing zeros, but the numeric value must be correct.19- **Lists / Sets**: When the template says "set; evaluator sorts values", still sort the list ascending before emitting. For ID lists, sort lexicographically ascending.20- **Task ID**: Use the exact `task_id` string specified in the answer template (e.g., `task_group_006_train_001` or `train_002`).2122## 3. Data Relationships & Lookups23| Entity | Key Fields | How to Resolve |24|--------|------------|----------------|25| Program | `program_id` | `/programs` or `/budget_snapshots` |26| PO | `po_id`, `program_id`, `supplier_id`, `contract_id`, `requisition_id` | `/purchase_orders` |27| Requisition | `requisition_id`, `program_id`, `sku` | `/purchase_requisitions` |28| Receipt | `receipt_id`, `po_id`, `supplier_id` | `/receipts` |29| AP Invoice | `invoice_id`, `po_id`, `receipt_id`, `supplier_id`, `status`, `hold_code` | `/ap_invoices` |30| Contract | `contract_id`, `sku`, `program_id`, `ceiling_amount`, `unit_price` | `/contracts` |31| Supplier | `supplier_id`, `name`, `risk_rating`, `status` | `/suppliers` |32| Vendor Risk | `event_id`, `supplier_id`, `severity`, `status` | `/vendor_risk_events` |33| Payment | `payment_id`, `invoice_id`, `supplier_id`, `scheduled_date`, `status` | `/payments` |34| Approval | `event_id`, `object_id` (requisition), `action`, `actor`, `event_date` | `/approval_events` |35| Budget | `snapshot_id`, `program_id`, `budget_cap`, `committed_amount`, `pending_invoice_amount` | `/budget_snapshots` |3637## 4. Business Rules by Task Type3839### 4.1 Nomination Readiness Packet40- **Program summary**:41 - `owner` = `programs.owner`.42 - `budget_headroom_usd` = `budget_cap - committed_amount` (from `/budget_snapshots`).43 - `overall_readiness`: If any line is `not_ready`, the program is often `at_risk` (not automatically `not_ready`).44- **Nomination line fields**:45 - `selected_supplier_id` = PO's `supplier_id`.46 - `commercial_basis_id` = PO's `contract_id` (may be `null`).47 - `package_po_ids` = all POs for that SKU in the program.48 - `receipt_evidence_ids` = receipts for those POs, filtered to `as_of_date`.49 - `invoice_exception_ids` = AP invoices for those POs with status `on_hold` or `pending_receipt` (or any non-approved status) as of `as_of_date`.50 - `risk_event_ids` = open or monitoring vendor-risk events for the supplier as of `as_of_date`.51 - `blocker_codes` (sorted ascending):52 - `missing_contract` if `contract_id` is `null`.53 - `open_supplier_risk` if any open vendor risk event exists for the supplier (even if unrelated to the PO).54 - `ap_hold` if any invoice for the line's POs has status `on_hold`.55 - `pending_receipt` if no receipt exists for the PO (or PO status is `open` with no receipts).56 - `late_due_date` if `po.due_date < as_of_date`.57 - `none` if no blockers apply.58- **Committee action**:59 - Map each supplier to `nominate_now`, `conditional`, or `hold` based on `nomination_decision`.60 - `send_to_committee` is typically `no` when holds exist, `yes` when all lines are ready or conditional.61 - `next_owner`: choose from `buyer|finance_ops|quality_ops|program_owner|ap_team` based on the dominant blocker type.6263### 4.2 Receiving Memo64- **Target batch**: The memo names a specific `receipt_id` (e.g., `RCV-BLUE-14`).65- **Invoice focus**: Use the AP invoice whose `receipt_id` matches the batch (e.g., `AP-LUMA-7714`). Ignore other invoices for the same PO unless they are tied to a different receipt.66- **Line reconciliation**:67 - `ordered_qty` = PO line `quantity`.68 - `received_qty` = receipt line `quantity_received`.69 - `rejected_qty` = receipt line `quantity_rejected`.70 - `billed_qty` = invoice line `quantity_billed`.71 - `short_qty_vs_po` = `ordered_qty - received_qty`.72 - `unreceived_billed_qty` = `billed_qty - received_qty` (or `billed_qty - (received_qty - rejected_qty)`).73 - `receipt_completion_ratio` = `received_qty / ordered_qty`, rounded to 4 decimals.74 - `contract_price_match` = `po_unit_price == contract_unit_price`.75- **Exception codes** (sorted ascending, set semantics):76 - `INVOICE_QTY_EXCEEDS_RECEIPT` if `billed_qty > received_qty`.77 - `PARTIAL_RECEIPT` if PO status is `partial_receipt`.78 - `SUPPLIER_WATCH_RISK` if **any** open vendor risk event exists for the supplier (even on a different PO). Do not omit this.79 - `PRICE_MISMATCH` if `invoice_unit_price != contract_unit_price`.80 - `DAMAGE_REJECTION` if `rejected_qty > 0` or dock notes mention damage.81 - `NO_EXCEPTION` only if none of the above apply.82- **Decision**:83 - `batch_disposition`: `accept_partial_hold_variance` when the receipt is accepted/accepted_with_note but there is a shortage or variance.84 - `ap_action`: `keep_invoice_on_hold` when the invoice has a hold code or the batch has a variance; `release_invoice` only when truly clear.85 - `receiving_action`: `record_shortage_follow_up` when `short_qty_vs_po > 0`; otherwise `no_receiving_action`.86 - `supplier_action`: `request_credit_or_remaining_delivery` when there is a shortage.87- **Evidence**:88 - `endpoint_record_ids` must include **every** API record examined, including the program, contract, PO, receipt, invoice, supplier, and any risk events referenced in `supplier_risk_context`.89 - `task_payloads_reviewed` = list of local payload filenames reviewed (e.g., `receiving_memo.md`).9091### 4.3 AP Close Memo92- **Scope**: Reconcile only the invoices named in the memo.93- **Opening balance**: Treat as `0.00` for each target supplier unless the memo states otherwise.94- **Invoice decisions** (sorted by `invoice_id` ascending):95 - `quantity_received`: from the receipt tied to the invoice (`invoice.receipt_id`). If `null`, use `0.00`.96 - `quantity_variance` = `quantity_billed - quantity_received`.97 - `quantity_variance_pct` = `(quantity_variance / PO quantity) * 100`, rounded to 1 decimal.98 - `scheduled_payment_amount`: sum of payments for that specific `invoice_id` with `scheduled_date <= close_date` (or all scheduled payments for the invoice if the memo says "through" the close date). If none, `0.00`.99 - `net_balance_impact` = `invoice_total - scheduled_payment_amount`.100 - `hold_decision`: `HOLD` if invoice status is `on_hold` or `pending_receipt`; `RELEASE` if `approved` or `paid`.101 - `release_to_payment`: `true` only when `hold_decision == RELEASE` and a payment is scheduled.102 - `reason_codes` (alphabetical):103 - `APPROVED_THREE_WAY_MATCH` when invoice qty = receipt qty = PO qty and status is approved.104 - `SCHEDULED_PAYMENT_FOUND` when a payment exists for the invoice.105 - `QTY_VARIANCE` when `quantity_variance != 0`.106 - `NO_RECEIPT` when `receipt_id` is `null`.107- **Vendor balances** (sorted by `supplier_id` ascending):108 - `invoice_total` = sum of target invoice totals for that supplier.109 - `scheduled_payments` = sum of **all** payments for that supplier with `scheduled_date <= close_date` (or as directed by the memo), not just target-invoice payments.110 - `held_invoice_total` = sum of target invoice totals where `hold_decision == HOLD`.111 - `releasable_invoice_total` = sum where `hold_decision == RELEASE`.112 - `close_balance` = `opening_balance + invoice_total - scheduled_payments`.113 - `balance_status`:114 - `FULLY_SCHEDULED` if `close_balance == 0` and all releasable invoices have payments.115 - `OPEN_HELD` if any held invoices remain.116 - `OPEN_APPROVED` if releasable invoices exist but are not fully scheduled.117- **Program summary** (sorted by `program_id` ascending):118 - Aggregate `invoice_count`, `invoice_total`, `held_total`, `released_total`, `net_close_balance` from the invoice decisions.119- **Queues**:120 - `payment_hold_queue` = target invoice IDs with `hold_decision == HOLD`, sorted ascending.121 - `payment_release_queue` = target invoice IDs with `hold_decision == RELEASE`, sorted ascending.122- `total_close_balance` = sum of all `net_balance_impact` values (or sum of vendor `close_balance`s).123124### 4.4 Change Memo / Contract Amendment125- **Contract check**:126 - `noncancelled_subtotal` = sum of `subtotal` for all POs linked to the contract with `status != cancelled`.127 - `headroom_before_change` = `ceiling_amount - noncancelled_subtotal`.128 - `requested_subtotal` = `requested_quantity * contract.unit_price`.129 - `headroom_after_change` = `headroom_before_change - requested_subtotal`.130 - `ceiling_ok` = `headroom_after_change >= 0`.131- **Program budget check**:132 - `remaining_budget` = `budget_cap - committed_amount` (from `/budget_snapshots`).133 - `requested_tax` = `requested_subtotal * (tax_rate_percent / 100)`, rounded to cents.134 - `requested_total` = `requested_subtotal + requested_tax`.135 - `budget_after_change` = `remaining_budget - requested_total`.136 - `budget_ok` = `budget_after_change >= 0`.137 - `max_quantity_with_current_budget` = `floor(remaining_budget / (unit_price * (1 + tax_rate/100)))` when tax is part of budget exposure. If tax is not part of exposure, use `floor(remaining_budget / unit_price)`. Follow the memo's `business_controls` note on budget exposure.138- **Approval check**:139 - Look at `/approval_events` filtered by `object_id == source_requisition_id`.140 - `latest_event_id` = the most recent event (by `event_date`).141 - `approval_ok` = `true` only if `latest_action` is in the memo's `approval_good_actions` list (typically `approved`).142- **Supplier risk check**:143 - `open_event_ids` = all vendor risk events for the supplier with `status == open`.144 - `severe_open_event_ids` = subset where `severity == severe` (or `high` depending on schema).145 - `supplier_risk_ok` = `true` if there are **no** severe open events. A medium open event does **not** block the change unless the memo explicitly says otherwise.146- **Decision**:147 - Choose the most specific hold reason:148 - `hold_for_budget_and_approval` when both are false.149 - `hold_for_budget` when only budget fails.150 - `hold_for_approval` when only approval fails.151 - `hold_for_supplier_risk` when supplier risk fails.152 - `release_amendment` when all checks pass.153 - `reject_contract_mismatch` only when the SKU/contract mapping is wrong.154- **Supporting IDs**:155 - `included_po_ids` = non-cancelled POs under the contract, sorted ascending.156 - `excluded_cancelled_po_ids` = cancelled POs under the contract, sorted ascending.157 - `approval_event_ids` = all approval events for the source requisition, sorted ascending.158- **Required actions** (sorted ascending): include `obtain_final_requisition_approval` when `approval_ok == false`, `raise_budget_exception_or_reduce_quantity` when `budget_ok == false`, `resolve_supplier_risk_hold` when `supplier_risk_ok == false`, otherwise `none`.159160### 4.5 AP Release / Exception Review161- **Target IDs**: Use exactly the IDs listed in the local packet. Sort each list ascending.162- **Chargebacks**: The local packet includes a `chargeback_register_excerpt`. Use it as the authoritative source for chargeback status and amounts. Compute chargeback amount = `basis_quantity * unit_cost`.163- **Release decisions** (one per target invoice):164 - `receipt_ids_in_scope` = the receipt(s) tied to the invoice (`invoice.receipt_id`). If the invoice has no receipt, use `[]`.165 - `excluded_same_po_receipt_ids` = other receipts for the same PO that are **not** tied to this invoice.166 - `decision`:167 - `release_net_after_approved_chargeback` when the chargeback is `approved` and the receipt is accepted.168 - `hold_pending_quality_chargeback` when the chargeback is `pending_quality_review` or the receipt is on `inspection_hold`.169 - `hold_missing_receipt` when `receipt_id` is `null`.170 - `primary_reason`:171 - `approved_qty_chargeback` for approved underage-quantity chargebacks.172 - `approved_ap_quantity_variance` for approved AP-quantity-variance chargebacks.173 - `inspection_hold_pending_chargeback` for inspection-hold receipts with pending chargebacks.174 - `no_receipt_on_po` when there is no receipt.175 - `approved_chargeback_amount` = sum of approved chargebacks for this invoice/receipt.176 - `pending_chargeback_amount` = sum of pending chargebacks for this invoice/receipt.177 - `net_release_amount` = `invoice_total - approved_chargeback_amount - pending_chargeback_amount`.178- **Receiving exceptions** (one per target receipt):179 - `exception_codes` (sorted ascending):180 - `Underage Quantity` when `received_qty < ordered_qty`.181 - `Inspection Hold` when receipt `status == inspection_hold`.182 - `AP Quantity Variance` when a chargeback reason is `AP Quantity Variance`.183 - `Severe Unmatched Quantity` only when the variance is extreme (use judgment based on template guidance).184 - `chargeback_status`: `approved`, `pending_quality_review`, or `not_applicable`.185 - `resolution_status`:186 - `net_release_ready` for approved chargebacks / accepted receipts.187 - `hold_for_quality_review` for pending quality chargebacks or inspection holds.188 - `missing_receipt` when there is no receipt.189 - `accepted_no_receiving_exception` when the receipt is clean.190- **Summary**:191 - `release_invoice_ids` = invoices with decision `release_net_after_approved_chargeback`, sorted ascending.192 - `hold_invoice_ids` = all other target invoices, sorted ascending.193 - `approved_chargeback_total` = sum of all `approved_chargeback_amount`s across release decisions.194 - `pending_chargeback_total` = sum of all `pending_chargeback_amount`s.195 - `net_release_total` = sum of **all** `net_release_amount`s (including holds), or the sum of release invoices only if the template explicitly says so. When in doubt, include the grand total and note the ambiguity.196 - `authoritative_sources` = include all applicable from the allowed list.197 - `supporting_only_sources` = include all applicable from the allowed list (e.g., `ap_release_request_note`, `stale_po73xx_alias_note`).198 - `followup_actions` = include every applicable action from the allowed list. Common ones:199 - `ask_receiving_for_vantix_receipt` when a target invoice lacks a receipt.200 - `route_po00031_quality_review` when a receipt is on inspection hold with a pending chargeback.201 - `post_approved_chargeback_netting` when there are approved chargebacks.202 - `hold_luma_duplicate_receipt_for_separate_invoice` when a PO has multiple receipts and the current invoice is tied to only one of them (the other receipt may belong to a separate invoice).203204## 5. Common Pitfalls2051. **Omitting SUPPLIER_WATCH_RISK**: If a supplier has **any** open vendor risk event, include `SUPPLIER_WATCH_RISK` in receiving-memo exception codes, even if the event is on a different PO.2062. **Unsorted lists**: The evaluator often compares sets, but some fields are compared as ordered lists. Always sort ID lists and enum lists ascending.2073. **Cancelled POs in contract usage**: Exclude `status == cancelled` POs when computing `noncancelled_subtotal` for contract headroom.2084. **Tax in budget exposure**: When the memo says budget exposure includes tax, compute `max_quantity_with_current_budget` using `unit_price * (1 + tax_rate)`.2095. **Payment scope in AP close**: `scheduled_payments` for vendor balances usually means **all** payments for that supplier up to the close date, not just payments for the target invoices.2106. **Quantity received = 0**: When an invoice has `receipt_id == null`, explicitly set `quantity_received` to `0.00`, not `null`.2117. **Evidence completeness**: In receiving memos, include every API record ID that was examined (program, PO, receipt, invoice, contract, supplier, risk events) in `evidence.endpoint_record_ids`.2128. **Duplicate receipts on a PO**: When a PO has multiple receipts and the target invoice is tied to only one, the other receipt is an `excluded_same_po_receipt_id`, and the follow-up action `hold_luma_duplicate_receipt_for_separate_invoice` may be required.2139. **Pending vs. approved chargebacks**: Use the local chargeback register to determine status. Do not infer from the API alone.21410. **Approval event filtering**: Use `object_id == requisition_id` and `object_type == requisition` when looking up approval events.