CX-2_CODEX.txt
You are working in the MIZOKI-3-5/MIZOKICloudRun repository (MIZ OKI 3.5, a governed
decision-intelligence platform: 39 Cloud Run cells, BigQuery, Firestore, Next.js command-center UI).
Read in this order before touching code: CLAUDE.md, CONSTITUTION.md (Article VI governs governance
surfaces), docs/audits/AUDIT_WORK_ORDERS_2026-09-08.md, docs/audits/wo/<your WO files>, and
docs/audits/AUDIT_2026-09-06_RECONCILIATION.md if it exists (CX-1 writes it).
Ground rules
- Audit findings are HYPOTHESES pinned at commit fc8b03f9. main has moved. Step 0 of every WO is:
reproduce the counterexample on current main with a failing test. If it does not reproduce,
record "not reproduced on <sha>" in your report and move on — do not fix what is not broken.
- Fail closed. Every fix must make a refusal path explicit and tested. Never widen access to make a test pass.
- No new architecture. Reuse the existing modules named in the WO. If a WO says "reuse X", reuse X.
- Tests are the deliverable. Each WO lists acceptance tests; write them first, watch them fail, then fix.
Name them test_wo<nn>_<what>. Keep the audit's synthetic counterexample numbers ($40 refund → $80,
$70+$70 vs $100 cap, DEL 91.7, etc.) as fixtures so the regression is recognizable.
- Branch: audit/<PROMPT-ID>-<slug>. NEVER use a claude/* branch (they auto-merge to main in seconds).
Commit per WO with message "WO-nn: <title>". Open ONE PR for the prompt when done. Do not merge.
- Protected paths (.github/**, deployment/terraform/**, deployment/cloudbuild*, CODEOWNERS): review PR only.
- Never write a typed gate token in full anywhere (docs, commits, PR bodies, reports): the gate-leak
ratchet (tests/test_gate_leak_scan.py) turns main red. Refer to it only as APPROVED: [MERGE].
- Do not deploy, do not change Cloud Run config, do not touch secrets, do not run anything against
production BigQuery/Firestore, do not spend money on any provider. Local + test fixtures only.
- Coordination: before starting, run
python scripts/claude_memory.py record --title "<PROMPT-ID> claim" --summary "<WOs> on branch <name>" --tags coordination
if the script exists; if not, add a line to docs/audits/COORDINATION.md.
- Stop and report (do not guess) if: a fix needs a new secret, a provider account, an IAM change,
a schema migration on a live dataset, or a change to a file owned by another prompt (see the
ownership table in docs/audits/AUDIT_EXECUTION_PROMPTS_2026-09-08.md).
Final report — write docs/audits/reports/<PROMPT-ID>_REPORT_<date>.md with, per WO:
status (fixed | not reproduced | blocked), repro test name + first failing run, fix summary,
files changed, acceptance tests + pass evidence, anything deferred and why. End with the PR URL,
the exact test command(s) that prove the pack, and the commit SHA the PR is based on.
PROMPT-ID: CX-2. Branch: audit/cx-2-ui-authz-product-truth.
Work orders: WO-15 (P0), WO-16 (P0), WO-22 (P2), WO-23 (P2), WO-33 (P3). Lane: SEC + ENG + CUST.
You own: miz-oki-command-center-ui/app/api/** EXCEPT app/api/bff/lanes/** (CX-1), plus the website
pilot intake, which CX-1 resolved to `# MIZ OKI 3.5/mizoki_runtime/pilot_requests.py` and `# MIZ OKI 3.5/app.py`
(in this repo; see docs/audits/AUDIT_2026-09-06_RECONCILIATION.md on branch audit/cx-1-current-truth / PR #1015). TypeScript/Next.js/Vitest.
Reference implementation for role + actor checks: app/api/bff/actions/authorize/route.ts and its
route.test.ts — copy its pattern, do not invent a new one.
WO-15 Role-check the onboarding mutation handlers
Files: app/api/bff/tenant-economics/save/route.ts; the tenant cost save route; the connector
credential save route under app/api/bff/connectors/ (find it: grep "credential" in
app/api/bff/connectors). Also the backend economics/cost routes these call — find them in
the BFF's fetch targets and note them; if they are Python services, write the required
backend change as a patch file under docs/audits/patches/ and report it (backend is CC/CX-3 territory).
Step 0: Vitest: construct a viewer identity for tenant T, stub the adapters, call all three save
handlers; assert 200 and that the adapter was invoked. Failing test.
Fix: apply the authorize route's role + actor + tenant check to all three; role must be admin (or
the role the authorize route uses for mutations); actor must be the verified principal.
Accept: viewer → 403 on all three and NO adapter call; admin → 200; wrong-tenant admin → 403.
WO-16 Route → required-role inventory enforced in CI
Fix: a script (scripts/ui_route_roles.ts or .py) that walks app/api/**/route.ts, detects mutating
handlers (POST/PUT/PATCH/DELETE), and requires each to export or annotate `requiredRole`
(pick the mechanism the authorize route already uses, or add a small `withRole()` wrapper);
emit a table to docs/audits/UI_ROUTE_ROLES.md. A Vitest test fails if any mutating route lacks it.
Accept: test green on your branch; the table lists every mutating route with a non-null role.
WO-22 Quarantine the legacy omnichannel allocator
Files: app/api/omnichannel/allocation/route.ts.
Step 0: call with no input; assert it currently returns a budget ($68,000) and projected revenue
($281,835.51); call with channel data missing clicks; assert a numeric conversion rate is
returned (267,000%).
Fix: missing live evidence → 422 {error: "evidence_unavailable", missing: [...]}; any simulated
output must carry {mode: "simulation"} and must never be returned from the live path; guard
every denominator (null, never a number); assert allocated + unallocated === budget or throw.
Accept: zero-input → 422; missing clicks → rate null; sum invariant test; simulation mode explicit.
WO-23 No path from the legacy allocator into DCP
Fix: a static test (Vitest or a small script in CI) asserting that no file outside
app/api/omnichannel/** imports the allocator's output type or calls its route, and that DCP
proposal intake never accepts an object with the allocator's shape. Add a deprecation banner
comment and a `X-MIZOKI-Legacy: allocation` response header.
Accept: CI test present and green; grep evidence in the report.
WO-33 Wire the site pilot request callback
Files: per CX-1's reconciliation table. If the code is in another repo, write the fix there on a
branch of the same name and report both PRs; if it truly does not exist, report "not in
scope" with evidence.
Fix: connect the request construction to the pilot intake route; on success persist a record;
on failure surface the error to the user (no silent drop).
Accept: submit test request → intake record exists; failure path shows an error.
Gates: npx vitest run; npx tsc --noEmit; next build if it runs in CI.
PR title: "Audit pack C/F (UI) — mutation role checks, allocator quarantine, pilot callback (WO-15,16,22,23,33)".