diff --git a/workplans/KEY-WP-0030-tenant-precondition-guard.md b/workplans/KEY-WP-0030-tenant-precondition-guard.md index 798c606..0f74e0d 100644 --- a/workplans/KEY-WP-0030-tenant-precondition-guard.md +++ b/workplans/KEY-WP-0030-tenant-precondition-guard.md @@ -51,58 +51,6 @@ This is deliberately not a vote on the tenant question. It makes the preconditio of one option checkable; it does not choose between them, and if option 1 lands the capability and this guard are removed together. -## Carry the tenant claim's provenance - -```task -id: KEY-WP-0030-T03 -status: done -priority: high -``` - -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-0030-T04 -status: done -priority: high -``` - -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. - ## What stays with the owners ```task diff --git a/workplans/KEY-WP-0031-gate-house-tenant-ruling.md b/workplans/KEY-WP-0031-gate-house-tenant-ruling.md new file mode 100644 index 0000000..caa1a00 --- /dev/null +++ b/workplans/KEY-WP-0031-gate-house-tenant-ruling.md @@ -0,0 +1,78 @@ +--- +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" +--- + +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 +``` + +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 +``` + +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.