P7 E A11 Cnus Staging Dry Run Signoff
Source: docs/operations/p7-e-a11-cnus-staging-dry-run-signoff.md
# P7-E-A11: CN->US Staging Dry-Run + Signoff
Operational checklist for staged activation rehearsal of the CN->US corridor.
**Plan linkage:** `P7-E` post-gate execution slice (`A11`)
---
## Goal
Produce a single, auditable dry-run packet showing:
1. Wrong-corridor behavior does not fire (`screeningAuthority=CA` control run)
2. In-corridor behavior fires (`screeningAuthority=US` activation run)
3. Owner signoff decision is explicit (`GO | CONDITIONAL GO | NO-GO`)
---
## Prerequisites
- Staging API reachable (`/health` returns `status=ok`)
- Tenant auth — either:
- **Preferred:** Integration key → `POST /api/auth/token/integration` → Bearer JWT (validates the full token-exchange path)
- **Fallback:** API key headers (`x-api-key` + `x-org-id`) — sufficient for endpoint validation but does not exercise the integration-token exchange path
- Tenant UUID (`x-org-id`)
- Known SKU set for rehearsal:
- At least one SKU expected to trigger CN->US corridor evidence
- Optional neutral SKUs for additional control coverage
---
## Step 1: Run Dry-Run (Script or Manual API)
> **Note:** The 2026-02-23 execution used direct API calls (`POST /api/sima/batch` + `GET /api/jobs/:id` + `GET /api/sima/results/:sku/evidence`) rather than the script below. Both approaches produce equivalent evidence; the script automates polling and artifact capture. See the Dry-Run Execution Record section for the actual results.
### Option A: Automated Script
```bash
scripts/run-cnus-corridor-dry-run.sh \
--api-url='https://<staging-api-domain>' \
--api-key='<integration-key>' \
--org-id='<tenant-uuid>' \
--skus='SKU_A,SKU_B,SKU_C' \
--expect-corridor-skus='SKU_A'
```
Script output:
- `tmp/cnus-corridor-dry-run/<timestamp>/summary.md`
- Raw JSON artifacts for both runs (`CA` control + `US` activation)
- Per-SKU evidence snapshots
Fail-fast behavior:
- Non-2xx API response
- Job failure (`/api/jobs/:id`)
- Corridor evidence present in CA control run
- Missing corridor evidence for expected SKUs in US run
---
## Step 2: Capture Rollback Readiness Snapshot
Record current state before signoff:
1. Tenant detector toggle path is available (`SIMA_EXPOSURE`)
2. Rule-level toggles are available (`SECT301_CHECK`, `US_232_CHECK`)
3. Global kill-switch value recorded (`TRADE_DETECTOR_MODE`)
Recommended evidence:
- API responses (or SQL output) proving current enabled/disabled states
- Timestamped note of who verified controls
---
## Step 3: Fill Decision Record
Copy this block into the PR comment or update this file directly.
```md
## P7-E-A11 Decision Record
- Date (UTC): <YYYY-MM-DDTHH:mm:ssZ>
- Tenant: <tenant-id>
- Run artifacts: <path or link to summary.md>
- CA control result: PASS | FAIL
- US activation result: PASS | FAIL
- Rollback controls verified: YES | NO
- Decision: GO | CONDITIONAL GO | NO-GO
- Product approver: <name>
- Engineering approver: <name>
- Notes / mitigations:
- <item 1>
- <item 2>
```
---
## Dry-Run Execution Record (2026-02-23)
### Environment
- API URL: `https://rgl8r-staging-api.onrender.com`
- Tenant: `00000000-0000-0000-0000-000000000099`
- Auth: API key fallback (`x-api-key: dev-key-1`) — used instead of integration-token path because no integration keys were provisioned on staging (requires Clerk auth to create). The fallback exercises the same route handlers and query logic; the only untested path is `POST /api/auth/token/integration` → JWT issuance, which is covered by CI contract tests in PR #392.
- `TRADE_DETECTOR_MODE=new`
- `SIMA_MEASURES_SOURCE=db`
- Test SKUs: `KC-STL-020`, `KC-STL-030`, `KC-ALU-011`, `KC-FRN-040`, `KC-SINK-001`
- All SKUs: CN origin, steel/aluminum/furniture HS codes
### Prerequisites Applied
1. Applied missing migration `20260307000000_add_trade_remedy_materials_columns` (added `materials`, `estimatedAddPerUnit` columns to `ref.trade_remedies`)
2. Applied missing migration `20260309000000_p10c_rule_hierarchy_level` (added `level` column to `app.rule_definitions`)
3. Created `USITC` data source in `ref.data_sources`
4. Seeded 25 US AD/CVD trade remedies into `ref.trade_remedies` (from `packages/modules/trade/src/config/us-adcvd-measures.ts`)
5. Set `SIMA_MEASURES_SOURCE=db` env var on Render
6. Disabled `FORCE ROW LEVEL SECURITY` on 13 app-schema tables (pre-existing RLS/`withTenant()` gap in query endpoints — see Known Issues)
### CA Control Run (wrong-corridor should NOT fire)
| Field | Value |
|-------|-------|
| Job ID | `391588f8-0909-4cce-8f66-97ab0a38ba13` |
| Screening Authority | `CA` |
| Status | `COMPLETED` |
| Result | **PASS** — no corridor evidence on any SKU |
Per-SKU results:
| SKU | Status | Corridor Evidence |
|-----|--------|-------------------|
| KC-STL-020 | NEEDS_REVIEW | None |
| KC-STL-030 | NEEDS_REVIEW | None |
| KC-ALU-011 | NEEDS_REVIEW | None |
| KC-FRN-040 | AT_RISK | None |
| KC-SINK-001 | AT_RISK | None |
### US Activation Run (corridor rules SHOULD fire)
| Field | Value |
|-------|-------|
| Job ID | `5c1b7afe-0500-47e7-bffc-44eacc3dc565` (initial), `11ce4e0f-ebb8-4971-bbcd-dc9c37deb382` (re-run) |
| Screening Authority | `US` |
| Status | `COMPLETED` |
| Result | **PASS** — corridor evidence with both rules on all 5 SKUs |
Per-SKU results:
| SKU | Status | Corridor Evidence | Override Applied |
|-----|--------|-------------------|------------------|
| KC-STL-020 | NEEDS_REVIEW | SECT301_CHECK + US_232_CHECK (CN→US) | No (already NEEDS_REVIEW) |
| KC-STL-030 | NEEDS_REVIEW | SECT301_CHECK + US_232_CHECK (CN→US) | No (already NEEDS_REVIEW) |
| KC-ALU-011 | NEEDS_REVIEW | SECT301_CHECK + US_232_CHECK (CN→US) | No (already NEEDS_REVIEW) |
| KC-FRN-040 | AT_RISK | SECT301_CHECK + US_232_CHECK (CN→US) | Yes — promoted from CLEARED to AT_RISK |
| KC-SINK-001 | AT_RISK | SECT301_CHECK + US_232_CHECK (CN→US) | Yes — promoted from CLEARED to AT_RISK |
AT_RISK outcomes with US corridor evidence (API snapshot): **2 SKUs** (KC-FRN-040, KC-SINK-001) — these were originally CLEARED by SIMA (no US AD/CVD match) but promoted to AT_RISK by corridor rules.
### Evidence Sample (KC-STL-020)
```json
{
"corridorRules": {
"originCountry": "CN",
"destinationCountry": "US",
"rulesEvaluated": 2,
"rulesMatched": 2,
"matchedRuleCodes": ["SECT301_CHECK", "US_232_CHECK"],
"matchedMessages": [
"Section 301 tariff exposure (CN→US)",
"Section 232 steel/aluminum tariff"
]
}
}
```
### Rollback Controls Verified
| Control | Verified | Current Value |
|---------|----------|---------------|
| `TRADE_DETECTOR_MODE` | Yes | `new` (staging only — revert to `legacy` after dry-run) |
| `rule_definitions.enabled` for SECT301_CHECK | Yes | `true` |
| `rule_definitions.enabled` for US_232_CHECK | Yes | `true` |
| Tenant detector toggle (SIMA_EXPOSURE) | Yes | No override row → defaults to enabled |
### Known Issues Discovered During Dry-Run
1. **RLS gap in query endpoints**: `handleSimaEvidence` and `handleSimaResults` use bare `prisma` client without `withTenant()`. Returns HTTP 500 with `invalid input syntax for type uuid: ""` when `FORCE ROW LEVEL SECURITY` is enabled. Workaround: disabled FORCE RLS on 13 tables. Fix needed before production corridor activation.
2. **RLS gap in `fetchTenantDetectorConfigs`**: Uses bare `prisma.tenantDetectorConfig.findMany()` without `withTenant()`. Fails silently in `sima-validation.ts` catch block.
3. **Missing staging migrations**: Staging was 3 migrations behind main. Applied 2 of 3 manually (`materials` columns, `level` column). Migration `20260308000000_p10b_carrier_data_expansion` not applied (not needed for dry-run).
4. **BETA tier filter**: US trade remedies seeded as `coverageTier=BETA` were filtered out by `getActiveTradeRemediesForRuntime()` (requires `SIMA_INCLUDE_BETA=true`). Fixed by updating to `GA` tier in staging.
### P7-E-A11 Decision Record
- Date (UTC): 2026-02-23T01:10:00Z
- Tenant: `00000000-0000-0000-0000-000000000099`
- Run artifacts: Inline above (API-driven dry-run, not script-based)
- CA control result: **PASS**
- US activation result: **PASS**
- Rollback controls verified: **YES**
- Decision: **CONDITIONAL GO** — engineering gates G1-G6 complete; corridor rules fire correctly; requires RLS fixes (issues #1-#2 above) and customer trigger before production activation
- Product approver: TBD (pending customer trigger)
- Engineering approver: Dan (dry-run executed and verified)
- Notes / mitigations:
- RLS fixes are blocking for production but not for staging validation
- Corridor rules use empty conditions (match all CN→US products) — production rules should have HS-code or material conditions
- US trade remedies seeded from `us-adcvd-measures.ts` v1.0.0 (25 AD/CVD orders)
- Staging env to be reverted to `TRADE_DETECTOR_MODE=legacy` and FORCE RLS re-enabled after dry-run
---
## Post-Dry-Run Cleanup
After signoff, revert staging to baseline:
1. Remove `SIMA_MEASURES_SOURCE` env var (or set to `hardcoded`)
2. Set `TRADE_DETECTOR_MODE=legacy`
3. Re-enable `FORCE ROW LEVEL SECURITY` on affected app-schema tables
4. US trade remedies and USITC data source may remain (no runtime impact with `SIMA_MEASURES_SOURCE=hardcoded`)
---
## Exit Criteria (A11)
- [x] Dry-run executed against staging (API-driven — CA control + US activation batches)
- [x] CA control run passed (no corridor evidence)
- [x] US activation run passed for all 5 CN-origin SKUs (corridor evidence with SECT301_CHECK + US_232_CHECK)
- [x] Rollback controls re-verified (3-tier: tenant toggle, rule toggle, global env var)
- [x] Decision record completed (engineering-only conditional signoff; product approval deferred to customer trigger)