CC-1 report — Audit pack B: spending admission hard gates (WO-01..07, WO-47)

Date: 2026-09-09 · Branch: audit/cc-1-spending-gates-90e88i · Base: main @ 26bec9c6b424ca4b86b35621602f7a06764e5afe (merge of #1012) Lane: ENG + SEC · Source: docs/audits/AUDIT_EXECUTION_PROMPTS_2026-09-08.md §CC-1, docs/audits/wo/WO-0{1..7}.md, WO-47.md Claim: claim: CC-1 audit pack B spending admission hard gates (ledger, 2026-09-09; commit 33672f7) PR: https://github.com/MIZOKI-3-5/MIZOKICloudRun/pull/1016 (draft; do not merge — Boss merges after the typed gate).

Every finding in this pack was treated as a hypothesis and re-run on current main before anything was changed (Step 0). All eight reproduced on 26bec9c. Each fix makes a refusal path explicit, coded and tested; nothing widens access. The four services' suites (tests/governance, which is the six governance services' own suite), tests/remediation, tests/connectors and the shared content gates are green on the branch head.

Non-independence disclosure (rule 01): the same session wrote every fix and every test in this report. The Step-0 reproductions were run before the fixes and their first failing outputs are quoted verbatim below; the acceptance runs are the same session's measurements, not an independent verifier's.


Summary table

WO Status Repro test (first run FAILED on 26bec9c) Fix, one line
WO-01 fixed test_wo01_failed_incremental_profit_alone_is_ineligible_not_del_91_7 HARD check taxonomy; any HARD failure → BLOCKED before DEL
WO-02 fixed (companion; acceptance tests only) explicit eligibility_class=exploration with approved envelope + cap + logged probability; not exempt from HARD
WO-03 fixed test_wo03_foreign_tenant_passport_with_invalid_seal_cannot_authorize passport resolved as immutable, sealed, tenant/path/domain/validity-bound record; binding inside the signed authorization
WO-04 fixed test_wo04_proposer_boolean_alone_no_longer_proves_experiment_evidence registry proof of holdout (tenant, registered_at < first exposure, salt_version); proposer boolean ignored
WO-05 fixed test_wo05_service_principal_with_a_human_body_actor_is_403 approver identity + role from the verified principal only
WO-06 fixed test_wo06_flag_true_with_no_proof_is_refused Stage 4 requires a stored RollbackProof for the exact tenant/account/action class
WO-07 fixed test_wo07_two_concurrent_redemptions_yield_one_authorization atomic claim → persist → complete; deterministic authorization id; verbatim replay
WO-47 fixed test_wo47_tenant_b_caller_cannot_read_tenant_a_decision tenant-scoped by-id reads answer 404 (never 403) on DCP, chain and passport routes

Commits, one per WO (WO-01 and WO-02 share one commit because the exploration class lives in the same evaluate() branch the hard gate rewrites):

785b737 WO-01/WO-02: hard-gate failures are terminal; bounded exploration class
2903834 WO-03: bind evidence passports to the decision
745b7b1 WO-04: holdout registration is proved, not asserted
03a6880 WO-05: approver identity from authentication only
3f3f1bd WO-06: rollback proof artifact before promotion
ba500ef WO-07: single-use approval redemption under concurrency
3ab2eab WO-47: tenant-scope DCP get_decision, decision chain and passport reads

WO-01 — Hard-gate failures are terminal

Status: fixed.

Step 0. tests/governance/test_wo01_02_hard_gates.py::test_wo01_failed_incremental_profit_alone_is_ineligible_not_del_91_7 issues a real media passport through the validation orchestrator with the audit's $40 revenue → $80 media-cost shape (incremental_profit FAILS, the other five checks pass, pass_rate == 5/6) and proposes it with evidence_completeness=1.0, verification_weight=1.0. First run on 26bec9c:

E   assert 'eligible' == 'blocked'

with del_score == 91.7 and a signed authorization in the response — the audit's [R1][R2] counterexample, verbatim.

Fix. - contracts/mizoki_contracts/validators.py: the ONE check taxonomy — CHECK_CLASSES (economic / integrity / consent / policy = HARD_CLASSES; rank otherwise), check_class(name), hard_failures(checks). An unnamed check that failed is HARD (class unknown) — fail closed. Media: incremental_profit=economic, consent_coverage=consent, causal_refutation+incrementality_evidence=integrity, mmm_agreement+creative_fatigue=rank. Finance and CRE batteries classified too. - ValidationCheck.check_class (additive, None on passports sealed before it); the validation orchestrator stamps it on every issued check, reports the taxonomy on /health, and audits hard_failures per passport. - Policy engine: EvalRequest.checks (per-check verdicts). evaluate() computes the hard failures FIRST (after the treasury/supply vetoes, before materiality, the floor, and the DEL) and returns BLOCKED with reason codes HARD_CHECK_FAILED:<class>:<name>, hard_failures=[…], del_score=0.0 ("DEL not computed"). The class is re-derived by NAME — a caller's check_class label is never trusted. Not a weight tweak. - DCP forwards the passport's checks to the engine and stores hard_failures and the class on the decision record; a hard-blocked decision is not redeemable (409).

Files: contracts/mizoki_contracts/validators.py, contracts/mizoki_contracts/decision_objects.py, services/service-validation-orchestrator/main.py, services/service-policy-engine/main.py, services/service-decision-control-plane/main.py, tests/governance/test_wo01_02_hard_gates.py, tests/remediation/test_end_to_end.py (ACT-991 scenario re-cut to rank-only weakness; DEL 41 kept).

Acceptance. - (a) test_wo01_each_hard_check_failing_alone_is_ineligible[…] — each HARD media check failing alone, all others 100% → blocked, no approval route, its code in reasons. - (b) test_wo01_property_no_rank_vector_flips_a_hard_failure — seeded property sweep: boundary grid × 150 random vectors of (pass_rate, evidence_completeness, verification_weight), random rank pass/fail, random value/reversibility; a HARD failure never flips. (No hypothesis dependency: adding one to the governance image is a .github/** change; the sweep is deterministic under seed 0xB01.) - (c) test_wo01_fully_passing_battery_is_still_eligible (DEL 96.5, signed) plus the whole pre-existing policy-engine / DCP / meter suites unchanged in outcome. - Also: relabelling a HARD check as rank changes nothing; unknown failed checks are HARD; rank-only failures still route on DEL; the validator emits {name, class, passed}.

Owner decision surfaced — F4 calibration lane. services/growth-scheduler/main.py _seal_passport seals the F4 passport with media_cost and geo counts only, so its honest media battery fails consent_coverage (0/0), incremental_profit (−cap), incrementality_evidence and causal_refutation — all HARD. Under the 2026-08-24 ruling that passport routed to the human queue (calibration floor 0.0, approval_only); under WO-01 it is BLOCKED. This is pinned on purpose by test_wo01_f4_shaped_calibration_passport_is_now_blocked_by_hard_checks so the change is a decision at review, never a surprise at deploy. Resolutions, either of which is a separate change: (i) the lane supplies evidence that passes the HARD checks (consent on the geo panel is measurable; the economic check needs a stated basis), or (ii) a DECLARED, bounded, test-pinned exemption on the calibration domain (which is already actuator/action-bound) — never a hidden one. Nothing in this pack deploys; the live F4 pilot is unaffected until a dispatch carries this change.

WO-02 — Bounded exploration class

Status: fixed (companion to WO-01; no separate Step 0 — the audit's exploration path WAS the averaging WO-01 removes).

Fix. eligibility_class ∈ {standard, exploration} on EvalRequest and ProposeRequest. Exploration requires exploration_envelope.envelope_id naming an APPROVED, tenant-bound record in exploration_envelopes (cap read from the STORE, never the request), estimated_value ≤ cap_usd, and a logged assignment_probability ∈ (0,1]. It relaxes only the DEL bar, still obeys the value ceiling and reversibility, and is exempt from no HARD check. Refusal codes: EXPLORATION_ENVELOPE_MISSING / NOT_FOUND / NOT_APPROVED / FOREIGN_TENANT / CAP_INVALID, EXPLORATION_OVER_CAP, EXPLORATION_ASSIGNMENT_PROBABILITY_INVALID. POST /api/v1/exploration-envelopes registers an envelope (tenant-resolved, attributed, immutable). The class, envelope and probability are stored on the decision record (decision_requests) and audited.

Acceptance (tests/governance/test_wo01_02_hard_gates.py): no envelope → refused; unknown / foreign / over-cap envelope → refused; no or invalid probability → refused; HARD failure under exploration → refused; valid exploration → eligible with the envelope echoed and the probability logged; the domain ceiling still applies; class stored on the decision record; unknown class → 422.

WO-03 — Bind evidence passports to the decision

Status: fixed.

Step 0. tests/governance/test_wo03_passport_binding.py::test_wo03_foreign_tenant_passport_with_invalid_seal_cannot_authorize: passport issued for gov-other-tenant, body altered, immutable_hash set to an invalid seal, actuator registry staged to Stage 4. First run on 26bec9c: HTTP 200 with "stage":"stage-4-bounded-autonomy" and a signature — the [R3] shape.

Fix. DCP resolve_bound_passport() runs before any decision document exists and refuses with 422 + code: PASSPORT_FOREIGN_TENANT (tenant ≠ resolved caller tenant), PASSPORT_SEAL_INVALID (no well-formed seal), PASSPORT_ALTERED (re-derived seal ≠ stored seal — computed over the STORED dict so pre-existing passports still verify), PASSPORT_STALE (outside PASSPORT_MAX_AGE_SECONDS, default 24h, skew 120s), PASSPORT_ACTION_MISMATCH (path ≠ chosen_path_id, or domain not permitted — DOMAIN_PASSPORT_BINDINGS = {"calibration": ("media","calibration")} is the one declared cross-domain binding, pinned). model_version and measurement_window (horizon) are required (PASSPORT_BINDING_INCOMPLETE). The bound fields — passport id + seal, passport tenant, path, domain, model version, horizon, issued/valid-until and decision_fingerprint = sha256(tenant|decision|actuator|action|bounds) — ride INSIDE the signed ActionAuthorization as the additive evidence_binding field, so the runner's HMAC covers them; verify_evidence_binding() re-verifies a grant against the ledger.

Files: services/service-decision-control-plane/main.py, contracts/mizoki_contracts/decision_objects.py (ActionAuthorization.evidence_binding), tests/governance/test_wo03_passport_binding.py, tests/governance/conftest.py (propose() binds model version/horizon and follows the passport's path), tests/remediation/* helpers (binding fields), src/shared/mizoki_governance/client.py (passthrough kwargs), tests/governance/test_decision_control_plane.py (advisory-only case now rides a CRE passport), tests/governance/test_passport_chain.py (honest-absent model_version superseded: refused by code).

Acceptance: one test per code (foreign tenant, seal invalid, altered, stale, path mismatch, domain mismatch, binding incomplete); valid bound passport → authorized with all ten binding fields inside the signed body, signature verifies at the runner, re-verification OK; tampering EACH bound field after signing → signature fails and the runner refuses 403 (parametrized over seven fields); a passport rewritten after issue is caught by re-verification; the approved path carries the same binding.

Consequences reported, not changed here: proposers that do not declare model_version + measurement_window are now refused at propose with PASSPORT_BINDING_INCOMPLETE: services/growth-scheduler/main.py (F4 sends the window but no model version), src/cells/cell37/market_cell/main.py, ops/remediation/live_proof.py, and every mizoki_governance.client.propose caller (the client gained the kwargs; callers must pass them). Authorizations signed before the evidence_binding field existed no longer verify at the runner (fail closed; re-propose) — a ≤1h TTL window at deploy.

WO-04 — Holdout registration is proved, not asserted

Status: fixed.

Step 0. tests/governance/test_wo04_holdout_registry.py::test_wo04_proposer_boolean_alone_no_longer_proves_experiment_evidence: has_experiment_evidence=True, no registered holdout. On 26bec9c: eligible with a signed authorization (the boolean alone sufficed).

Fix. contracts/mizoki_contracts/holdouts.py::resolve_holdout_registration(store, tenant_id, holdout_id, first_exposure_at) — the ONE resolver over holdout_registrations (pilot_report.HOLDOUT_COLLECTION): sufficient only when the record exists for THIS tenant, is well formed (registered_at, salt_version), and registered_at < first_exposure_at strictly — the registry's measured first_exposure_at beats the proposer's claim, which beats now. Codes: HOLDOUT_ID_MISSING / UNREGISTERED / FOREIGN_TENANT / RECORD_INVALID / REGISTERED_AFTER_EXPOSURE. DCP: ProposeRequest.holdout_id (or bounds.holdout_id) and first_exposure_at; the proposer boolean is IGNORED (recorded for audit only); the verdict drives the engine's has_experiment_evidence and is stored on the decision record; a proven holdout is stamped onto the bound action so the runner re-proves it. POST /api/v1/holdouts/register (server-stamped registered_at, immutable, cannot be backdated — extra="forbid"). Action runner require_proven_holdout() runs before the single-use dispatch claim for intent-driven actions or any holdout id: 403 by code, nothing sent, authorization unconsumed; resolve_holdout_registration is re-exported from main.py for the adapters.

Files: contracts/mizoki_contracts/holdouts.py (new), services/service-decision-control-plane/main.py, services/service-action-runner/main.py, tests/governance/test_wo04_holdout_registry.py, tests/governance/conftest.py (register_holdout; propose() rides one by default), remediation helpers, tests that meant "no experiment evidence" now pass holdout_id=None.

Acceptance: resolver — unregistered, post-exposure (request-claimed and registry-measured), other-tenant, malformed → insufficient; registered → passes with the record. DCP — the three refusals → experiment-required with the code stored; registered → eligible with bounds.holdout_id on the signed grant; registration route stamps and is immutable; backdating → 422. Runner — the three refusals → 403 HOLDOUT_*, authorization unconsumed; registered → dispatches; intent-driven without id → HOLDOUT_ID_MISSING.

Gaps reported: (1) services/service-action-runner/execution_adapters/base.py::check_holdout (CC-2) still checks presence only; the interface it should call is main.resolve_holdout_registration / main.require_proven_holdout. (2) No producer in this repo writes holdout_registrations today (Cell 36 registration is external; pilot_report only reads it). Until registrations land — via the new DCP route or the collection — every media candidate is honestly experiment-required. That is the fail-closed posture CONSTITUTION II.10 asks for, and it matches the platform's own "first real registered holdout is the open gate" state.

WO-05 — Approver identity from authentication only

Status: fixed.

Step 0. tests/governance/test_wo05_approver_principal.py::test_wo05_service_principal_with_a_human_body_actor_is_403: service principal S sends actor="some human". On 26bec9c: 200, approved_by="some human".

Fix. service-approval-routing: resolve_principal (dependency over verify_caller) classifies the verified caller. Service accounts / local-dev are SERVICE principals and can never satisfy a HUMAN approval (403 APPROVER_NOT_HUMAN); a human's roles come only from the approver registry (MIZOKI_APPROVER_ROLES env seed + approver_roles store collection), and the request's route is the role required (403 APPROVER_ROLE_MISSING). A registered identity broker (MIZOKI_APPROVER_BROKERS — the command-center BFF service account, which already authenticated the human session) may relay the human id in x-mizoki-principal; any other caller's header is ignored and a broker cannot relay a service id. Body actor is optional and must equal the principal (400 APPROVER_ACTOR_MISMATCH). The stored approval carries the principal's verified id, kind, roles and broker; D6 self-approval is still refused.

Files: services/service-approval-routing/main.py, tests/governance/test_wo05_approver_principal.py, tests/governance/conftest.py (DEFAULT_HUMAN, as_principal, human, service), tests/governance/test_approval_routing.py, tests/governance/test_treasury_gate.py, tests/remediation/conftest.py (install_human_principal) + three remediation files.

Acceptance: service + body-human → 403; human wrong role → 403; human right role → 200 with approved_by = verified id (not the body); actor mismatch → 400; body actor never the stored identity; denial under the same rules; classification of SA / local-dev / direct human OIDC / broker relay; broker relay over HTTP uses the header, not the body; the suite's own transport identity (local-dev) cannot approve without the override; DCP redeems only the verified approver.

Operator actions (deployment config, not done here — the pack forbids Cloud Run changes): seed MIZOKI_APPROVER_ROLES, register the BFF SA in MIZOKI_APPROVER_BROKERS, and have the BFF send x-mizoki-principal (its lib/bff/approval-mutations.ts already derives the actor from the session; CX-2 owns app/api/**). Until then no approval can be granted — fail closed, and honest on /health (approver_roles_seeded, identity_brokers). Stronger form not done (needs a Track O secret): verifying the Supabase session JWT directly on approval-routing.

WO-06 — Rollback proof artifact before promotion

Status: fixed.

Step 0. tests/governance/test_wo06_rollback_proof.py::test_wo06_flag_true_with_no_proof_is_refused: register + /actuators/demonstrate (flag flipped on request) then promote. On 26bec9c: 200, Stage 4 granted with no drill anywhere.

Fix. RollbackProof {drill_id, tenant_id, account, action_class, actuator, executed_at, outcome, evidence_ref} — POST /api/v1/rollback-proofs stores it immutably (409 on re-record; outcome vocabulary; executed_at in the past; evidence_ref and actuator required). Shape follows ops/remediation/live_proof.py's drill record (read only, unchanged). Promotion to Stage 4 names its scope (tenant_id, account, action_class — 422 otherwise) and requires a stored SUCCESSFUL proof for exactly that scope drilled through this actuator (403 ROLLBACK_PROOF_MISSING); the proof is recorded on the actuator (stage4_proof) and audited; walking back clears it. Registration's rollback_demonstrated and /demonstrate are advisory only. Dispatch: a Stage-4 row with no recorded proof is held (ROLLBACK_PROOF_MISSING); an authorization whose tenant / action class the proof does not cover is refused (ROLLBACK_PROOF_SCOPE_MISMATCH) — before the single-use claim, nothing sent.

Files: services/service-action-runner/main.py, tests/governance/test_wo06_rollback_proof.py, tests/governance/test_action_runner.py, tests/governance/test_fail_closed.py, tests/governance/conftest.py (earn_stage4), tests/remediation/conftest.py (earn_stage4), tests/remediation/test_end_to_end.py, test_services_hardening.py, test_execution_adapters.py (promotion helpers switched to proofs).

Acceptance: flag=True + no proof → refused; proof for a different action class → refused; different tenant / account → refused; failed drill → refused; proof naming another actuator → refused; missing scope → 422; future / evidence-less / duplicate proof → 422 / 409; matching proof → promotion allowed and recorded; /demonstrate earns nothing; walk-back needs no proof; dispatch bound to the proof's tenant and action class; legacy Stage-4 row without a proof never dispatches; proof shape pinned.

Reported: ops/remediation/live_proof.py step 7a earns Stage 4 through /demonstrate and will now get 403; the drill needs to POST /api/v1/rollback-proofs after its rollback step. Not modified (read-only for this pack).

WO-07 — Single-use approval under concurrency

Status: fixed.

Step 0. tests/governance/test_wo07_single_use_redemption.py::test_wo07_two_concurrent_redemptions_yield_one_authorization with a barrier store handing each thread an independent snapshot (as Firestore does — the memory store aliases its dicts, which masked the race on the first attempt). On 26bec9c: two 200s, {'AUT-205680917638464c', 'AUT-c7cd8f1d38a248a5'} — two distinct signed authorizations for one approval.

Fix. DCP authorize_approved is three atomic phases over Store.transact_update (a real Firestore transaction in deployment; the store lock in memory): (1) CLAIM the approval — still approved by this approver; COMPLETE → replay the stored authorization verbatim; CLAIMED (concurrent redeemer / crash after claim) → complete under the same id; pre-WO-07 "id set, no redemption state" → 409. (2) PERSIST action_authorizations/{id} first-writer-wins; every caller receives the persisted document (identical signature / issued_at / expires_at). (3) COMPLETE the approval. authorization_id = "AUT-" + sha256(approval_id|decision_fingerprint|tenant)[:16]. The treasury reservation keyed by that id is idempotent on retry.

Files: services/service-decision-control-plane/main.py, tests/governance/test_wo07_single_use_redemption.py, tests/governance/test_decision_control_plane.py, tests/governance/test_decision_meter.py, tests/remediation/test_end_to_end.py (a replay is now the same authorization, not 409).

Acceptance: 50-way concurrent redemption → exactly one id, 49 identical replays, one persisted authorization, one complete redemption; deterministic id; crash after claim / after persist / after complete → retry converges on the same id (state inspected at each boundary); contention (transaction aborted BEFORE the write) at each of the three write boundaries → retry converges (fault-injection store); legacy marker → 409; wrong approver → 403 even after redemption.

Reported: ops/remediation/live_proof.py step 5e expects a replay to be 409; it is now 200 with the identical authorization. Not modified.

WO-47 — DCP get_decision returns any tenant's DecisionProof

Status: fixed.

Step 0. tests/governance/test_wo47_decision_tenant_scope.py::test_wo47_tenant_b_caller_cannot_read_tenant_a_decision (caller→tenant registry armed with A and B; caller B requests A's id). On 26bec9c: 200 with the full document; /decision/{id}/chain 200; /passport/{id} 403 (confirms the id exists).

Fix. DCP tenant_scoped_read(): resolve the caller against the DOCUMENT's tenant; foreign, unmapped-strict, or missing all answer 404 {"detail": "not found"}; an explicit ?tenant_id must match the document. services/service-audit-replay/main.py (owned by no audit prompt — touched only for WO-47's three named acceptance routes): /decision/{id}/chain, /passport/{id} and /passport/{id}/verify convert the tenant refusal to the same 404.

Acceptance: tenant B → 404 with only a detail field on all three routes; tenant A positive path unchanged on all three; missing and foreign indistinguishable; explicit tenant query must match; strict-mode unmapped caller → 404.


Deferred / gaps (all reported above, gathered here)

  1. F4 calibration lane is BLOCKED by WO-01's hard checks (owner decision at review; pinned by test). Options (i)/(ii) above.
  2. Proposers without model_version + measurement_window are refused (WO-03): growth-scheduler, cell37 market cell, live_proof, SDK callers.
  3. No writer of holdout_registrations in-repo (WO-04); media candidates stay experiment-required until registrations are written (DCP route provided).
  4. execution_adapters/base.py::check_holdout still presence-only (CC-2); interface exposed from main.py.
  5. Approval-routing deployment config (WO-05): roles seed, broker registration, BFF header — operator/CX-2 actions; Supabase JWT verification would need a Track O secret.
  6. ops/remediation/live_proof.py steps 5e and 7a encode the old 409-replay and demonstrate-earns-Stage-4 behaviours (WO-06/07); read-only for this pack.
  7. Contracts touched (owned by no prompt): validators.py (taxonomy), decision_objects.py (two additive optional fields), holdouts.py (new resolver). Both signing sides dump the same model, so signatures stay consistent once both deploy; in-flight authorizations (≤1h) signed without evidence_binding are refused.
  8. src/shared/mizoki_governance/client.py gained four passthrough kwargs; that one file is what fans the Deploy Router out to seven non-governance workflows (below).

Gates run on the branch head

/tmp/venv/bin/python -m pytest tests/governance -c tests/governance/pytest.ini          # exit 0 (six governance services' suite, incl. 100+ new WO tests)
/tmp/venv/bin/python -m pytest tests/remediation --ignore=tests/remediation/test_token_minters.py -o addopts=""   # 298 passed (token_minters needs google-auth; env only)
/tmp/venv/bin/python -m pytest tests/connectors tests/test_edge_inference_hardening.py tests/test_canonical_events_ddl.py tests/test_marketsignal_closure_register.py -o addopts=""   # 464 passed, 32 skipped
bash .github/scripts/content_gates.sh                                                   # exit 0 (138 passed) — the same script ci.yaml and auto-merge run
python3 scripts/skill_sync.py --audit                                                   # canon audit — 0 findings across 15 skills
flake8 <changed files> --select=E9,F63,F7,F82                                            # 0 (ci.yaml's selection); pyflakes F-class clean on the new test files
python3 scripts/gate_leak_scan.py --check                                               # 0 new / 0 grown / 0 stale
rule-03 V1–V3 greps over the diff                                                        # no hits
python3 .github/scripts/deploy_router.py --base origin/main --head HEAD                 # 10 workflows would dispatch on merge (see below)
python3 scripts/claude_memory.py check --strict                                         # structurally valid

scripts/mizoki_canon.py --check is a no-op and is not cited. ruff/black are not configured as blocking gates in this repo (black --check … || true in ci.yaml).

Merge is a deploy decision (rule 04). The Deploy Router matches 10 workflows on this diff: deploy-service-action-runner, deploy-service-canonical-ingestion, deploy-service-marketing-connectors (from the three contracts/mizoki_contracts/* files) and deploy-boss-agent-core, deploy-cell2, deploy-cell3, deploy-coding-moa, deploy-gemini-kg-pipeline, deploy-moa-controller, deploy-moe-router (from src/shared/mizoki_governance/client.py). The five governance services themselves ship through the dispatch-only deploy-governance-services.yml, which this merge does NOT fire. Nothing in this pack was deployed, no Cloud Run config, secret or live dataset was touched, and nothing ran against production BigQuery/Firestore.

Exact commands that prove the pack

python3 -m venv /tmp/venv && /tmp/venv/bin/pip install -e contracts pytest pyyaml httpx reportlab==5.0.1 Flask==3.1.3
/tmp/venv/bin/python -m pytest tests/governance/test_wo01_02_hard_gates.py tests/governance/test_wo03_passport_binding.py tests/governance/test_wo04_holdout_registry.py tests/governance/test_wo05_approver_principal.py tests/governance/test_wo06_rollback_proof.py tests/governance/test_wo07_single_use_redemption.py tests/governance/test_wo47_decision_tenant_scope.py -c tests/governance/pytest.ini -q
/tmp/venv/bin/python -m pytest tests/governance -c tests/governance/pytest.ini -q
/tmp/venv/bin/python -m pytest tests/remediation -q -o addopts="" --ignore=tests/remediation/test_token_minters.py
bash .github/scripts/content_gates.sh

Base SHA: 26bec9c6b424ca4b86b35621602f7a06764e5afe. PR: https://github.com/MIZOKI-3-5/MIZOKICloudRun/pull/1016 — head e6d57d8 at PR open (the PR page carries the current head). Branch head at report time: see the PR. Merge gate: APPROVED: [MERGE], typed by the owner in-session — never by an agent.

← All docsView source on GitHub →