← automation
sherpa-api#465 Done · In review ⏱ 1h 12m
Per-outlet claim rules
⎇ auto/api-465-per-outlet-claim-rules
⤷ based on #456
Portal localhost:3008Embed localhost:3108API localhost:4008
🔍 iterative-review ✓ (main-pass) 🧪 test:api failed 262 passed · 2 failed · 0 flaky · 4 skipped · report 🧑‍🔬 QA ✅ PASS

Feedback

Pull requests

loading…

QA evaluation

✅ PASS

PASS. I independently drove the real sherpa-portal UI (headless Chromium, freshly minted :3008 storageState) — not the implementer's screenshots. On a product my e2e org actually owns (235; the implementer's hardcoded 226 belongs to another org), I created three outlets, assigned them, and configured the full acceptance scenario through the new 'Outlet Rules' editor: product default limit 1000; Outlet A override auto-pay limit 500; Outlet B override forcing manual review with AI disabled; Outlet C left to inherit. After reload the resolved overrides table renders exactly as required (A=Outlet override/On/500, B=Outlet override/Manual review/AI Off, C=Product default/1000), and the 'Source' column surfaces which rule set applies — the audit. Live /claim-rule-sets/resolve corroborated each scope (outlet/outlet/product_default) with distinct ruleSetIds, which is precisely the input the claim engine uses (policyOrder.outletId -> resolveClaimRuleSet -> governance + persisted appliedRuleSet). Tests: 262 passed / 2 failed; both failures (policy-suspension cancel-preview-while-suspended -> 400, product-translations coverage-per-locale -> 0) are pre-existing shared-DB/stateful-suite drift explicitly catalogued in the #458 QE baseline, and #465's diff touches neither path — no new regressions. Console/network clean apart from a benign Sentry /monitoring beacon abort.

5/5 acceptance criteria passed
  • Per-outlet claim-rule editor exists in the portal (Outlet Rules tab)Drove the real portal at http://localhost:3008/products/235/claim-rules (authenticated as [email protected]). A third tab 'Outlet Rules' renders OutletClaimRuleSets: a 'Product default' card (Automated decisions / AI assessment / Auto-payout limit + Edit default) and a 'Per-outlet overrides' table with one row per assigned outlet and an editor dialog (Automated decisions switch, AI assessment switch, Auto-payout limit input). Edited and saved live; values persisted across reload. Screenshot eval-1/eval-4.
  • Outlet A auto-pays claims under a limit (outlet override)In the editor I set Outlet A override = automated decisions On, auto-payout limit 500 (eval-2). After reload the overrides table shows Outlet A: Source 'Outlet override', Automated decisions On, AI On, limit 500.00. Live API resolve(productId=235, outletId=A) returned scope:'outlet', autoPayoutLimit:500 — the engine governs amounts <=500 as auto-approve, above as manual review (claimRuleEvaluationService).
  • Outlet B forces manual review with AI assessment disabled (outlet override)Set Outlet B override = Automated decisions OFF + AI assessment OFF via the dialog switches (eval-3 shows both switches unchecked, confirmed data-state=unchecked). Overrides table shows Outlet B: Source 'Outlet override', Automated decisions 'Manual review', AI 'Off', No limit. Live API resolve returned scope:'outlet', autoDecisionEnabled:false, aiAssessmentEnabled:false — engine routes every claim in this scope to manual review and skips AI.
  • Third outlet with no override uses the product defaultLeft Outlet C with no override. Product default was set to limit 1000 (eval-1). The table shows Outlet C: Source 'Product default', Automated decisions On, AI On, limit 1000.00 — i.e. it inherits the default (eval-4). Live API resolve(outletId=C) returned scope:'product_default', autoPayoutLimit:1000, ruleSetId of the product default row.
  • Audit shows the applied rule set (resolution chain, most specific wins)The overrides table's 'Source' column makes the applied rule set visible per outlet in the UI (Outlet override vs Product default). Backend: resolveClaimRuleSet returns scope ('outlet' | 'product_default' | 'system_default') + ruleSetId + outletId, and claimRuleEvaluationService persists this appliedRuleSet onto each claim's ruleEvaluation and ruleEvaluationHistory. Verified live via /v1/plus/claim-rule-sets/resolve for A/B/C (distinct ruleSetIds and scopes) and via code review of the evaluation engine. The 10/10 passing API spec (10-per-outlet-claim-rules.spec.ts) covers the resolution chain + default/system fallback.
tests_ok: true · quality: Solid, surgical. New ClaimRuleSets tenant table keyed (org, product, outletId) with two partial-unique indexes (one product default + one override per outlet), permissive RLS matching the CI template, DECIMAL(12,2) for money, FK CASCADE. resolveClaimRuleSet is a clean most-specific-wins chain; evaluation engine gates AI, forces manual review when autoDecision off, and enforces the auto-payout limit, persisting appliedRuleSet for audit. Upsert PUT is hardened against the partial-unique-index race (catches UniqueConstraintError and updates). CRUD is org-scoped via safe-model (the GET /:id 404 for a foreign-org product is correct). Portal editor reuses Button/Input/Switch/Dialog, has loading/error states, testids and t() i18n. policyOrder/quote creation gained an additive outletId via resolveOutletForSale (from #456). No security or correctness issue warranting a fail.