CX-3_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-3. Branch: audit/cx-3-perimeter-providers.
Work orders: WO-21 (P0), WO-24 (P2), WO-25 (P2). Lane: SEC + ENG + OPS.
You own: miz-oki-adk-agents/boss/**, miz-oki-adk-agents/app/main.py,
services/service-action-runner/execution_adapters/google_ads.py and credentials.py ONLY (CC-2 owns
the rest of execution_adapters/), services/service-data-manager-connector/**,
miz-oki-adk-agents/kg-canonical-ingest/mappers/google_ads.py. Deploy workflows are protected paths:
you may propose changes as a separate review PR but not in this branch.

WO-21 Authenticate the Boss service perimeter
  Context from CC-1 (PR #1016): approval routing now derives approver identity from the verified principal; the BFF must send
  the principal header and the roles seed/broker registration are deployment config (see CC-1 report §Deferred 5). Reuse that
  principal-auth module here; do not add a second auth scheme.
  Files: miz-oki-adk-agents/boss/app.py, miz-oki-adk-agents/app/main.py (the production entry that
         re-exports the Boss app).
  Step 0: FastAPI TestClient: POST the chat route and the direct-action route with no credentials;
          assert 200 today.
  Fix: (1) write docs/audits/BOSS_ROUTE_INVENTORY.md — every route, method, public|private,
       required principal, and why; health/readiness are the only public routes unless the inventory
       argues otherwise. (2) add an auth dependency (reuse the platform's existing principal-auth
       module — find it via the DCP/approval services; do not write a new auth scheme) to chat and
       direct-action and every route marked private. (3) In a SEPARATE review PR touching
       .github/workflows/** and any deploy config, remove --allow-unauthenticated / allUsers for the
       Boss service and document the invoker principal the UI uses. Do not merge; do not deploy.
  Accept: anonymous chat → 401; anonymous direct-action → 401; authenticated principal → 200;
          inventory committed; deploy-config PR opened with a one-paragraph rollback note.
  Report: which routes changed from public to private, and the OPS verification step still needed
          (read-only IAM invoker check on the live service — do not perform it).

WO-24 Google Ads adapter: leave sunset v21
  Files: execution_adapters/google_ads.py (line ~55: API_VERSION default "v21"), credentials.py if
         version-specific.
  Step 0: assert the default is v21 (it is, as of main 15a6ba29b); check Google's sunset list.
  Fix: default to the newest version that is BOTH listed in Google's current Ads API release notes
       and supported by the installed google-ads client library (pin the library accordingly in the
       runner's requirements — coordinate with CX-4's lockfile work by putting the pin in a clearly
       marked single line). Audit every mutation and read-back call in the adapter against the new
       version's changelog; fix renamed fields/enums. Keep GOOGLE_ADS_API_VERSION override.
  Accept: unit tests for every mutation + read-back with recorded responses (VCR-style fixtures, no
          live calls); a test asserting the default version is not in a committed SUNSET_VERSIONS
          list; validate-only mutation path exists and is tested. Do NOT run against a real account;
          write the validate-only run as an OPS checklist item in the report.

WO-25 Data Manager connector request contract
  Files: services/service-data-manager-connector/main.py; kg-canonical-ingest/mappers/google_ads.py.
  Step 0: unit test the built request against the Data Manager Event / IngestEvents / Destination REST
          schema (write the schema as JSON-schema fixtures from the official docs): assert today it
          omits productDestinationId, drops conversion_action, sends conversionValue as an object,
          and does not encode hashed userData. Four failing tests.
  Fix: conform exactly: productDestinationId per destination; conversion_action routed per event to
       its destination (multi-action routing); conversionValue numeric with sibling currencyCode;
       hashed userData normalized + SHA-256 + encoded as the schema requires; validate-only flag
       plumbed and its diagnostics surfaced in the response.
  Accept: four schema tests pass; a golden request fixture is committed; validate-only path tested
          with a recorded diagnostics response. Do not build a second connector.

Gates: pytest for miz-oki-adk-agents/boss, the two adapter files, and the connector service.
PR title: "Audit pack C/F (perimeter + providers) — Boss auth, Ads API version, Data Manager contract
(WO-21, 24, 25)". Plus the separate protected-path PR for deploy config.
← All docsView source on GitHub →