Skip to content

fix(api): AIN-183 P0-3 follow-up · tenant_handle suffix swap + tulkas sanitize#49

Merged
hizrianraz merged 1 commit into
mainfrom
fix/ain-183-tenant-handle-suffix-and-tulkas-sanitize
May 19, 2026
Merged

fix(api): AIN-183 P0-3 follow-up · tenant_handle suffix swap + tulkas sanitize#49
hizrianraz merged 1 commit into
mainfrom
fix/ain-183-tenant-handle-suffix-and-tulkas-sanitize

Conversation

@hizrianraz
Copy link
Copy Markdown
Contributor

Summary

Two issues surfaced after PR #47 (migration 0018) deployed to prod:

1. Founder tenant lost bare 'ainfera-ai' to a dormant duplicate

The 0018 backfill found two prod tenants owning agents with `owner_handle='hizrianraz'`. The first-by-id (dormant artifact, likely an early signup) kept bare `ainfera-ai`. The second — the active founder tenant that owns Varda + Yavanna — got the conflict suffix `ainfera-ai-280f44`. Marketing widget filters on bare `ainfera-ai/varda` and currently finds zero events.

2. Internal-agent name on tenant_handle

The 0018 MIN(owner_handle) backfill set one tenant's `tenant_handle` to literally `tulkas` — the tenant owning the internal red-team agent `sacrificial-001` (registered under `owner_handle='tulkas'`). The string is now visible on the raw `/v1/audit/public` endpoint. The AuditTicker allowlist on marketing masks it from the UI, but the raw API still exposes it.

Migration 20260519_0019

Four phases, all idempotent:

  1. Free bare 'ainfera-ai' — any tenant currently on it that does NOT own a hizrianraz-handle agent gets renamed to `inactive-`.
  2. Promote — the suffixed tenant that DOES own hizrianraz-handle agents gets renamed to bare `ainfera-ai`. Sub-select uses `ORDER BY id LIMIT 1` so the choice is deterministic.
  3. Sanitize — any tenant whose handle is in `{manwe, namo, aule, tulkas}` gets remapped to `internal-`. Varda + Yavanna are intentionally excluded (PUBLIC agents).
  4. Invariants:
    • If any founder agent exists, the founder tenant must end on bare `ainfera-ai`. (Fresh CI DB with zero agents skips this check.)
    • No internal-fleet name may remain on `tenant_handle`.

Both invariants raise on violation, aborting the migration.

Test plan

Downgrade

Reconstructing the original conflict-suffix slice from 0018 is impossible without a backup. Downgrade is a no-op — restore from snapshot if rollback is needed.

Closes: AIN-183 P0-3 follow-up
Discipline: #3 (internal-agent name off tenant_handle), #11

…al-agent sanitize

Two issues surfaced after migration 0018 deployed:

1. Founder tenant lost bare 'ainfera-ai'. The 0018 backfill found two
   prod tenants owning agents with owner_handle='hizrianraz' — first-by-id
   kept bare 'ainfera-ai' (dormant artifact), second (active founder
   tenant, owns Varda/Yavanna) got conflict suffix 'ainfera-ai-280f44'.
   Marketing widget filters on bare 'ainfera-ai/varda' and finds zero
   events.

2. Internal-agent name on tenant_handle. 0018 MIN(owner_handle) set one
   tenant's handle to 'tulkas' (it owns the internal red-team agent
   sacrificial-001 registered under owner_handle='tulkas'). The string
   is now visible on /v1/audit/public — leak the AuditTicker allowlist
   masks on marketing but the raw API still exposes.

## Phases (idempotent)

1. Free bare 'ainfera-ai': non-founder squatter renamed to
   'inactive-<id-slice>'.
2. Promote: suffixed founder tenant (LIKE 'ainfera-ai-%' AND owns
   hizrianraz-handle agent) renamed to bare 'ainfera-ai'. Limit 1 with
   stable ORDER BY id in the sub-select.
3. Sanitize: any tenant whose tenant_handle ∈ {manwe, namo, aule, tulkas}
   renamed to 'internal-<id-slice>'. Varda + Yavanna excluded — they're
   public agents and their tenant_handles are intentionally surfaceable.
4. Invariants:
   - If any founder agent exists, founder tenant must be on bare
     'ainfera-ai'. (Fresh CI DB with zero agents skips this check.)
   - No internal-fleet name may remain on tenant_handle.
   Both raise RuntimeError on violation.

## Downgrade

Reconstructing the original conflict-suffix slice is impossible without
a backup. Downgrade is a no-op — restore from snapshot if rollback is
needed.

Closes: AIN-183 P0-3 follow-up (suffix swap + tulkas sanitization)
Discipline: #3 (internal agent name off tenant_handle), #11
@cursor
Copy link
Copy Markdown

cursor Bot commented May 19, 2026

You have used all Bugbot PR reviews included in your free trial for your GitHub account on this workspace.

To continue using Bugbot reviews, enable Bugbot for your team in the Cursor dashboard.

@linear-code
Copy link
Copy Markdown

linear-code Bot commented May 19, 2026

AIN-183 🟠 All-repos audit sweep — 14 repos × spec-vs-built + Discipline #3/#4/#11/#17 verification

Severity: 🟠 HIGH — comprehensive repo-level Discipline #1 + #17 audit

Filed 2026-05-18 PM after AIN-153 + AIN-158 spec-vs-built audit revealed Aule shipping less than spec'd then marking parents Done. Pattern requires systematic verification across ALL 14 repos in the ainfera-ai GitHub org.

Founder directive: "Hard revert to In Progress and force the missing work. Also check all repos."

Scope

For each of the 14 repos in ainfera-ai org, Aule audits:

  1. Spec vs built — does the repo's stated purpose match what's actually in main?
  2. Tickets marked Done — for each Done ticket touching this repo in last 14d, verify the deliverables match the ticket's acceptance gates
  3. Surfaces live in production — does the prod deployment match what main branch's app/ or src/ declares?
  4. Founder PII grep — 0 matches for "Hizrian|Izzy|Raz|founder|fibromyalgia|ADHD|snowboard|Julius Baer" in any public surface or repo README/docs
  5. Internal agent name grep — public-facing surfaces should NOT mention Manwe/Yavanna/Namo/Aule/Tulkas (Varda is acceptable per PUBLIC build(deps): Bump astral-sh/setup-uv from 5 to 7 #1 lock)
  6. Lock compliance — D7-D37 locks honored in any new code
  7. Discipline fix(orm): use postgresql.ARRAY so capabilities.contains() emits @> #11 — Notion canonical vs code drift

14 repos to audit

Production-facing (Phase 1, urgent)

# Repo Audit focus
1 ainfera-ai/web Marketing + dashboard pages — match D14 + Part 2 spec
2 ainfera-ai/api FastAPI surfaces, /v1/* endpoints match docs.ainfera.ai claims
3 ainfera-ai/ainfera-os Monorepo root, docs/ folder, SKILL.md files, package versions
4 ainfera-ai/mcp mcp.ainfera.ai FastMCP server, tool surface matches docs
5 ainfera-ai/sdk (or python-sdk if named differently) PyPI ainfera package matches API surface, exported symbols

Agent-implementation (Phase 2)

# Repo Audit focus
6 ainfera-ai/varda (or similar) NemoClaw orchestrator config, GPT-5.5 binding, public surface compliance
7 ainfera-ai/aule Claude SDK + Opus 4.7 xhigh config, author override in commit history
8 ainfera-ai/yavanna LangGraph + Sonnet 4.6 + Grok 4, response_review heuristics
9 ainfera-ai/namo Letta + Gemini 3.1 Pro, memory consolidation
10 ainfera-ai/tulkas Garak + Mistral Large 3, probe batteries

Customer + tooling (Phase 3)

# Repo Audit focus
11 ainfera-ai/hermes-agent (Manwe Customer #1 fork) v0.14.0 SHA a91a57fa matches reported state
12 ainfera-ai/examples 5 example agent repos per AIN-78
13 ainfera-ai/specs (or similar) CC-BY 4.0 spec docs match Notion canonical
14 ainfera-ai/.github or org-level Issue templates, PR templates, CODEOWNERS, branch protection

Audit deliverable per repo

For each repo, Aule produces a comment on this ticket with:

## Repo: ainfera-ai/<name>

### Spec match
- Stated purpose: <from README>
- Actual state: <from main branch traversal>
- Drift: <none / specifics>

### Recent Done tickets touching this repo
- AIN-XYZ — claimed: "..." → actual state: ✅ matches / ⚠️ partial / ❌ missing

### Production-vs-main drift
- Last deploy SHA: <sha>
- main HEAD: <sha>
- Drift: <none / files differ / etc>

### Grep results
- Founder PII: <count> matches → <files>
- Internal agent names in public: <count> matches → <files>

### Lock compliance
- D7-D37 references: <count> 
- Discipline #6 corollary violations: <count>

### Recommendation
- ✅ Clean / ⚠️ Cleanup needed / 🔴 Active violation

### Tickets to file
- <list of child tickets needed if cleanup work surfaces>

Audit commands Aule runs per repo

cd ~/code/ainfera-ai/<repo>
git pull origin main

# Discipline #3 grep
rg -i "hizrian|izzy|raz|fibromyalgia|adhd|snowboard|julius baer|sommelier" \
   --type-not lock \
   --type-not log \
   -l

# Internal agent naming in public surfaces
rg -i "manwe|yavanna|namo|aule|tulkas" \
   src/ app/ public/ docs/ README.md \
   --type-not lock \
   -l

# Discipline #4 author override check (last 50 commits)
git log -50 --pretty=format:'%h %an <%ae>' | rg -v "Aule <aule@" | head -20

# Production-vs-main drift (for deployed repos)
gh api repos/ainfera-ai/<repo>/deployments --jq '.[0:3] | .[].sha'
# Compare against `git rev-parse main`

# Spec vs files
ls -la docs/
cat README.md | head -30

Acceptance gates

  • All 14 repos audited
  • Audit comment posted per repo on this ticket
  • Any 🔴 Active violation gets immediate child ticket filed
  • Any ⚠️ Cleanup ticket gets queued for AIN-179 delivery wave
  • Summary comment at end: total findings + per-severity counts + recommended next actions
  • Aule author override on the audit branch
  • No remediation in this ticket — only findings. Remediation = follow-up tickets.

Out of scope

  • Code refactoring (only audit-and-report)
  • Linter sweeps (separate cleanup pass)
  • Test coverage analysis (covered by AIN-118 or successor)
  • Performance audit (separate concern)

Connection

Founder authorization

Per "Hard revert to In Progress and force the missing work. Also check all repos" (2026-05-18 session 3.5 PM).

Review in Linear

@hizrianraz hizrianraz merged commit 3b54ea4 into main May 19, 2026
2 of 3 checks passed
hizrianraz added a commit that referenced this pull request May 23, 2026
…ce (#69)

Final piece of the cross-repo AAMC retirement (paired with sdk #11+#12,
mcp-server #12, ainfera-os #49, routing #2). Removes references to
"AAMC voter" / "voter pool" / "Council" in code comments + display
names + the (now-defunct) invariant test file.

Changes:

- adapters/openai.py: comment reframe — "AAMC voter pool" →
  "canonical routing backends".
- adapters/upstream_aliases.py: same.
- orm.py: drop the `aamc_voter` flag field-comment reference (the
  field itself stays — was repurposed as a generic catalog-eligibility
  flag; just rename the rationale in the comment).
- routers/stats.py: leaderboard endpoint comment reframe.
- services/response_normalizer.py: comment cleanup.
- services/routing.py: comment cleanup.
- scripts/seed_dev.py: display names reframed
  ("GPT-5.5 Pro (AAMC voter)" → "GPT-5.5 Pro").
- tests/integration/test_aamc_invariants.py → renamed to
  test_routing_backends_invariants.py. Test logic unchanged — it
  enforces the canonical 5-backend lock (Opus, GPT-5.5, Gemini, Grok,
  Mistral-Large), reframed away from the retired AAMC framing.

No runtime behavior change. Pure vocabulary cleanup.

Per Ontology v1.2 amendment (2026-05-22) which retired ATS/AAMC and
folded their semantics into Routing (`q_empirical` for trust;
`M_allowed` for eligibility veto). Ontology v1.3 (2026-05-23) further
made Mithril the canonical product the doctrine leads with.

Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant