--- id: ADHOC-2026-07-24 type: workplan title: "Resolve tenants by identifier, not only internal tenant_id" domain: infotech repo: tenant-engine status: finished owner: codex topic_slug: netkingdom created: "2026-07-24" updated: "2026-07-24" state_hub_workstream_id: "302a69b5-356b-4a7f-a96b-509816434e8a" --- # Resolve tenants by identifier, not only internal tenant_id Discovered via a real cross-service check while implementing `key-cape`'s `KEY-WP-0005-T02` (`tenant_roles` cache-read integration): `key-cape`'s Go adapter called `GET /tenants/tenant:coulomb/roles` and got a genuine `404 tenant_not_found` for a tenant that actually existed. External callers (`key-cape`, `flex-auth`) only ever have a tenant's **profile identifier** (the IAM Profile `tenant` claim value, e.g. `tenant:coulomb`) — never tenant-engine's internal `tenant_id`, which is caller-chosen at creation (`POST /tenants`) and otherwise opaque. Every existing test happened to pass an identical string for both fields, so this gap was invisible to unit/API tests until a genuine second, independent caller (`key-cape`, written against the documented contract, not against tenant-engine's internal test conventions) exercised it for real. ## Task: Fix ```task id: ADHOC-2026-07-24-T01 status: done priority: high state_hub_task_id: "18bb2786-ceb1-44e5-aafa-20ff443bd9f6" ``` `InMemoryTenantStore` gained a `_by_identifier` index (identifier → internal `tenant_id`) and a private `_resolve()` helper every method now calls first, so `get_tenant`, `grant_role`, `revoke_role`, `active_roles`, and `assign_plan` all transparently accept either the internal id or the identifier. `create_tenant` also now rejects a duplicate identifier under a different internal id (previously unenforced — an oversight the same fix surfaced). Done when: a real cross-process check (real `flex-auth`, real `tenant-engine`, real `key-cape` adapter code, over actual HTTP) resolves a tenant by identifier correctly, and all existing tests still pass unmodified in intent. **Done 2026-07-24:** Fixed in `store.py`. Added `test_get_tenant_resolves_by_identifier_not_only_internal_id`, `test_active_roles_resolves_by_identifier`, `test_revoke_role_resolves_by_identifier`, and `test_create_tenant_rejects_duplicate_identifier_with_different_internal_id` in `tests/test_store.py`; added `test_cache_read_roles_resolves_by_identifier_not_only_internal_id` in `tests/test_api_reads.py` (the exact HTTP-level scenario, with the colon characters a real `tenant:friendly:binky`-shaped identifier puts in the URL path). `pytest` → `65 passed` (all 60 pre-existing tests unaffected). Re-ran the exact real end-to-end check that found the bug, on fresh ports to avoid colliding with another session's own `flex-auth serve` process on this shared workstation (a collision that led to accidentally killing that other process — a low-harm, trivially-restartable local dev server, but noted here as a mistake to avoid repeating: check `pgrep` output carefully and prefer distinctive ports before killing anything matching a broad pattern): created a tenant with a genuinely different internal id and identifier, granted it a role through the real `flex-auth`-gated write path, then called `GET /tenants/tenant:coulomb/roles` from `key-cape`'s actual `internal/adapters/tenantengine.Client` (not a stand-in) — correctly returned `roles=[IAM] ok=true`.