KEY-WP-0014-T04 sat `wait` with nothing outstanding, which is a contradiction. Its title is admit rotation and verify consumer handoff, and both are delivered: the semantics are published, the executor, authority and transport are named, the CCR will be prepared on request, and step 4 shipped as keycape verify-client; ops-warden took option (a) for the reason given, with both lanes owner-confirmed and no route changed. Executing a rotation was never its deliverable, so treating the owner's deferral as an open item would have kept the task open against work it was not scoped to do. The deferral stands separately, revisited on evidence. GH-DEC-2026-016 §5 applies A-16 to what makes a principal human. Verified against source that `human` has exactly one route here -- a literal on the authorization-code path after an upstream login resolved to a directory user, with no configuration able to assert it -- so A-16 does not yet bite and no provenance claim is warranted. Adding one would encode a distinction that does not exist. Asserting the behaviour would not protect that: a test checking a human token says human passes just as happily when the value starts coming from a registration. The guard parses the package and requires every principal_type assignment to be a string literal, the set being exactly human and service. It covers both shapes -- the browser path assigns into a map, the service path uses a key-value pair in a map literal -- and checking only assignments found one of two routes and passed, which the pinned literal set caught. Verified by mutation. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NV9oijZukGyGbRQGGKnK4P Assistant: claude-code Assistant-Model: opus Assistant-Process: 713576@bnt-lap001 Assistant-Session: 384c511d-9bce-4cb8-a676-2aef6c0c8df6
2.9 KiB
| id | type | title | domain | repo | status | owner | topic_slug | created | updated |
|---|---|---|---|---|---|---|---|---|---|
| KEY-WP-0032 | workplan | Pin the single route by which a principal becomes human | infotech | key-cape | finished | claude | principal-type-provenance-guard | 2026-09-10 | 2026-09-10 |
GH-DEC-2026-016 §5 applies A-16 to what makes a principal human: if human is
reachable by two routes — asserted by the identity layer about the person, or
supplied by a registration about the client they came through — the record must
say which, and a human-in-the-loop control must not be discharged on the
registration-supplied one. Refusing a service principal while accepting an
unverified assertion of humanity moves the defect rather than closing it.
gate-house said explicitly this was not a request. It is built because the property it protects is true today and is the kind a plausible change erases quietly.
Pin the property, not the behaviour
id: KEY-WP-0032-T01
status: done
priority: medium
principal_type has exactly one route to human: a string literal on the
authorization-code path, reached only after an upstream login resolved to a
directory user. No configuration field can assert it — verified against source
rather than assumed, and it is why the tenant provenance work has no counterpart
here and why A-16 does not yet bite.
Asserting the behaviour would not have protected that. A test checking "a human
token says human" passes just as happily when the value starts coming from a
registration. So the guard parses the package and requires every assignment to
principal_type to be a string literal, with the literal set exactly
{human, service} — one route each. claims["principal_type"] = client.PrincipalType would look like a feature in review and fails the build
instead, with a message saying that if it is deliberate the claim must carry its
provenance the way tenant_source does.
Both assignment shapes are covered. The browser path assigns into a map; the service path uses a key-value pair inside a map literal. Checking only assignments found one of two routes and passed — a guard proving less than it claimed, caught because the expected literal set was pinned rather than merely counted.
Verified by mutation: replacing the literal with a function of the client registration fails it at the exact line, and restoring it passes.
What this deliberately does not do
id: KEY-WP-0032-T02
status: done
priority: medium
No principal_type_source claim was added. There is one route, so a provenance
marker would encode a distinction that does not exist and invite consumers to
branch on it — the opposite of the tenant case, where two routes existed and the
claim could not say which had been taken.
The guard is the honest response to "not a request today": it makes the absence of a second route a checked fact rather than a remembered one, and hands the decision to whoever creates the second route, at the moment they create it.