--- id: KEY-WP-0031 type: workplan title: "Implement the gate-house tenant ruling" domain: infotech repo: key-cape status: finished owner: claude topic_slug: gate-house-tenant-ruling created: "2026-09-10" updated: "2026-09-10" state_hub_workstream_id: "2bc4d085-9b10-5160-bc8a-4fccc5d41ba9" --- gate-house ruled GH-DEC-2026-013 (`gate-house@16c1d46`) on the human-tenant question KeyCape declined to settle for itself. Directory-sourced is the terminal state; the registration-bound shape is granted as a declared bounded gap under three conditions, on the general property that *a transitional shape is admissible where it fails closed on exactly the case distinguishing it from the correct resolution*. Ours refuses issuance where registration and directory disagree, which is that case. Conditions (a) and (c) needed nothing from this repository โ€” (a) is already the implemented behaviour, and its strengthening, that a future change preferring the **directory** is equally void because picking any winner is a silent cross-tenant assertion, is recorded in the contract. This workplan covers the two items that were implementation. ## Carry the tenant claim's provenance ```task id: KEY-WP-0031-T01 status: done priority: high state_hub_task_id: "46d42120-eca5-5b7f-8445-2211b3b8eab0" ``` gate-house ruled on GH-DEC-2026-013 while this was in flight. Section 5 is a finding nobody asked for and is ours to implement: `tenant` is a bare string, so a consumer cannot tell a zone the directory asserted about the person from one a registration supplied about the client they came through. approval-engine exact-matches that string while its contract reads as though it relies on the first โ€” the check is sound and the property a reader infers from it is absent. Every token now carries `tenant_source` beside `tenant`: `directory`, `registration`, or `default`. It is advertised in `claims_supported` and asserted on both grants at the token level, not only in the resolution function, because the claim a consumer reads is the thing under obligation. Three values where the ruling names two, and that is the substantive judgement here. Labelling an unasserted profile default as `directory` would reproduce the same defect one level down โ€” a consumer reading an assertion the identity layer never made. The ruling cites GH-DEC-2026-011 ยง3 on unknown versus absent for the case it examined; the same rule applies to our own fallback, so the fallback is named rather than folded into the strongest neighbouring value. The agreement case resolves to `directory` deliberately: if a registration declares the zone the directory also assigned, the directory did assert it, and reporting the weaker source would understate what is known. ## Condition (b) corrected a message I had just shipped ```task id: KEY-WP-0031-T02 status: done priority: high state_hub_task_id: "b21aeac8-8370-5572-ab00-6e9812fa48e7" ``` The guard committed in `5f516a0` told a future reader that adding dynamic registration could be resolved by dropping the capability **or gating it to statically configured registrations**. Condition (b) voids the second: admitting dynamic registration voids the registration-bound shape that day, and the directory becomes the only source whatever state the adapter is in. gate-house strengthened our own "must be revisited" on the grounds that *revisited* implies the answer might survive review, and it would not. So the message offered a way out the ruling forbids, in the exact place someone would read it while making that change. Corrected the same day. Worth recording rather than quietly editing: the guard was written to force a confrontation, and a guard that suggests an inadmissible resolution is worse than none.