Invariant Enforcement Layer
Summary (3–6 bullets)
- Defines automated invariant gates that protect core engineering and data safety rules.
- Lists current invariants with phase and blocking/warn status.
- Documents local execution commands and CI integration behavior.
- Establishes allow-tag policy with required justification and escalation path.
- Captures stop conditions for false positives and scope-tightening approach.
When to use this (3–6 bullets)
- Before merging changes that touch BigQuery queries, wizard flows, or money paths.
- When an invariant fails in local checks or CI.
- When deciding whether an exception needs an allow-tag.
- When tuning invariant scope to reduce false positives safely.
What you’ll walk away with (2–5 bullets)
- A practical runbook for running and interpreting invariant checks.
- A clear policy for exceptions without weakening controls.
- The CI merge expectations for invariant compliance.
Invariants Enforced
| Invariant | Phase | Blocking | Description |
|---|---|---|---|
bq_tenant_filter | 1 | ✅ | Every BigQuery query must filter by tenant_id |
readiness_requires_period | 1 | ✅ | Readiness endpoints must require period_label (no default) |
frontend_no_tenant_readiness_in_wizard | 1 | ✅ | Wizard must not call tenant-wide readiness (/admin/onboarding/businesses/readiness) |
python_no_float | 1 | ⚠️ Warn | No float() or math.* in money paths (Phase 2: blocking) |
frontend_no_dev_panels_prod | 1 | ⚠️ Warn | Debug panels must be guarded for production (Phase 2: blocking) |
Running Locally
Allow-Tag Policy
When an invariant must be violated for a justified reason:- Add inline comment on the same line or preceding line:
# INVARIANT_ALLOW: <tag> - Must include justification in the same or next line
float_usage— Python no-float checktenant_filter— BigQuery tenant filter check
CI Integration
.github/workflows/invariants.yml runs on:
pull_requesttomainpushtomain
Stop Conditions
If invariant checks produce excessive false positives:- Do not weaken rules silently
- Tighten scope (e.g.
--pathsto narrower directories) - Add allow-tags with justification for known exceptions
- Update this doc with scope changes
- Escalate if scope tightening is insufficient
Deterministic Replay (Phase 3)
Seeapi/tests/test_deterministic_replay_stage1.py for the Stage1 idempotency scaffold. Phase 3 will make deterministic replay blocking.