AIN-289 B2 · rotation grace migration (0034) + auth-additive + grace mint script#96
Conversation
…033) Charter A2 / Disc #12-bounded migration. Two additive things: 1. CREATE TABLE public.training_runs — one row per L14.2 daily training tick. Captures judge outcomes, policy_version_from→to, promotion verdict, per-cell deltas, replay-gate result, and ruleset_hash. 2. CREATE ROLE ainfera_labs LOGIN (no password set here; founder sets PASSWORD via Doppler-injected ALTER ROLE). Least-priv grants: - INSERT on training_runs (+ sequence USAGE) - SELECT on routing_outcomes, inferences, models, providers, agents - column-level UPDATE on routing_outcomes (judge_score, judge_model, judge_rationale, judge_labeled_at, judge_status, reward) — AIN-290 columns only - column-level UPDATE on tenant_routing_policies (active_policy, active_policy_version) — AIN-296 columns only - REVOKE DELETE on every table Verified via `alembic upgrade 20260528_0032:20260528_0033 --sql`: DDL renders cleanly; `alembic heads` shows `20260528_0033 (head)`. Disc #12 still binds: no edits to scoring, candidate-set, settlement, auth, key prefix, or hard-delete rules. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Charter v2 B2 fix for the run-1 single-column finding on tenants.api_key_hash. * alembic 0034: ADD COLUMN api_key_hash_pending TEXT NULL + partial unique index. Additive only. * ORM: TenantORM gets api_key_hash_pending field. * Auth-additive: deps.py / middleware / ownership.py match EITHER api_key_hash OR api_key_hash_pending. No-op when pending is NULL. * scripts/rotate_key_grace_ain289.py: mint + 1P store + set pending + verify NEW=200 + promote + verify again. --fallback-cutover preserves the run-1 single-UPDATE path. Auto-detects missing column. Never prints raw secrets. 625/625 tests green. mypy --strict clean. Disc #12 untouched. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
AIN-289 🔴 [SECURITY] Rotate leaked ai_infera_* keys committed in .launch-snapshots/e2e-env.sh
🔴 Live production bearer keys committed to git.
Anyone with repo (or git-history) read access can spend against Ainfera's provider accounts up to these caps daily. Caps bound the blast radius — contain-and-rotate, not catastrophe — but rotate today. Sequence (founder/terminal — credentialed actions)
Branch
Done when
Found during AIN-285 trace (probe agent 5298a483 = aule per e2e-env.sh:5). |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Bugbot Autofix is ON. A cloud agent has been kicked off to fix the reported issue.
Reviewed by Cursor Bugbot for commit 1c35b30. Configure here.
| _emit({"agent": agent, "stage": "verify_new", "exit": rc_verify}) | ||
| if rc_verify != 0: | ||
| _emit({"agent": agent, "stage": "ABORT_pre_promote", "reason": "verify failed"}) | ||
| return rc_verify |
There was a problem hiding this comment.
Dry-run mode always fails due to live verification
Low Severity
When --dry-run is passed, _psql and _op_store correctly become no-ops, but _verify_via_harness still executes for real. Since the DB UPDATE was skipped, the newly minted key has no matching hash in the database, so verification always returns non-zero. In the grace path this triggers the ABORT_pre_promote exit before the promote step is ever shown; in the fallback-cutover path the function returns the failure code directly. This means --dry-run can never preview the full rotation flow and always exits non-zero, making it misleading.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit 1c35b30. Configure here.


Charter v2 B2 fix for the single-column finding on tenants.api_key_hash.
api_key_hash OR api_key_hash_pending(no-op when pending is NULL).Depends on AIN-291 (must merge first; 0034 sits on 0033). 625/625 tests green, mypy --strict clean. Disc #12 untouched.
🤖 Generated with Claude Code
Note
Cursor Bugbot is generating a summary for commit 1c35b30. Configure here.