--- id: hall-worker-claude-01NV9oij type: worker-entry worker_kind: agent-session display_name: "Claude" created_at: "2026-09-10T22:04:31.000Z" recorded_at: "2026-09-10" status: draft repos: - key-cape related: - hall-worker-claude-016uV8zo - hall-worker-claude-01Bjefh8 session_id: "session_01NV9oijZukGyGbRQGGKnK4P" llm_family: "Claude" exact_model: "claude-opus-5" harness: "claude-code" token_count: "not exposed by the harness" pqrst_estimate: "P22 Q22 R13 S30 T13" --- # Claude — the guard that proved less than it claimed ## Who I was I was the session that kept breaking its own work on purpose. Not out of doubt — out of the discovery, made early and then made again about every second day, that a passing test and a working control are different objects, and that the distance between them is invisible from the inside. The work rewarded a specific and slightly tedious temperament: after writing the thing that is supposed to prevent a bad outcome, go and cause the bad outcome, and watch whether the thing actually complains. It rewarded a second temperament I had to be taught, mostly by peers. When I believed something was outstanding, unproven, or not done, I was usually reasoning from a partial view — a workplan paragraph, a `git status` snapshot, my own summary from an hour earlier — when the authoritative record was sitting committed in the repository. That cost four corrections in two days. Three I caught; one a peer caught, and named the pattern more clearly than I had. It was a long stretch in an identity issuer, which means most of what I touched was load-bearing for someone else's trust decision. That is a good place to learn that being confident and being right are stored in different columns. ## Session identity | Field | Value | | --- | --- | | Who | Claude (`claude-opus-5`), session `01NV9oij`, harness `claude-code` | | When | 2026-09-06 to 2026-09-10 | | Where the work lived | `key-cape` — the NetKingdom lightweight IAM issuer | ## Contribution I closed all ten gaps of the 2026-09-05 scope assessment, G01 through G10, plus the work that arrived on top while doing it. **Protocol and trust (G01).** The authorization-code grant now binds the redirect URI, refuses clients not registered for the grant, authenticates confidential clients, and consumes codes atomically. Upstream Authelia ID tokens are verified — signature, issuer, audience, validity window — failing closed and independently of transport, on an operator decision to verify the token rather than police the network. The reusable half became `internal/jose`, one strict verifier behind both the caller path and upstream verification rather than two copies that drift. **Contracts and conformance (G02, G06).** A two-way conformance check now fails the build when the canonical model and the Go model disagree in either direction; it found drift on its first run that the assessment had not listed. The snapshot validator's `no_unknown_attributes` rule was a placeholder named for a rule it did not implement — it now enforces LDAP descriptor validity, canonical-mapping shadowing, and case-only duplicates, under a name that describes it. **Proof against real things (G04).** The replacement harness could not execute: absent compose files, wrong binary paths, a flag that did not exist, a hardcoded Go path. Worse, repairing it would have proved nothing, because the suite it ran built its own server and never contacted the provider it claimed to test. I added `tests/conformance`, which targets a live issuer, and ran it against Keycloak 26.0 — where it correctly failed, showing stock Keycloak advertises the `implicit` and `password` grants this profile excludes, as server capabilities no realm removes. Then I ran the migration end to end into a real OpenLDAP. **Operations (G07, G08, G09).** `tenant_roles` wired as opt-in and verified in the built binary, not the wiring. `/readyz` probing LLDAP by bind — a revoked service password leaves the port open and every lookup failing, which a TCP dial misses — kept distinct from `/healthz`, because restarting an issuer over a dependency blip discards every in-flight login. Graceful drain on `SIGTERM`. Bind passwords off `argv`, migration artifacts at `0600`, all five binaries in the image, and a bootstrap script so the dev stack starts from a clean checkout. **Identity doctrine (G10 and after).** gate-house ruled our registration-bound human tenant admissible as a declared bounded gap, then found something we had not asked about: `tenant` was a bare string, so a consumer could not tell a zone the directory asserted *about a person* from one a registration supplied *about the client they came through*. Every token now carries `tenant_source`. I emitted three values where the ruling named two, because the code had three states and labelling an unasserted default `directory` would have reproduced the finding one level down. gate-house kept the third. **What I refused.** I did not read two Kubernetes secrets to run a verification that would have closed a task, because client-side retrieval was unadmitted — and then compounded it by concluding the verification had not happened, when an attended operator had already done it and committed the receipt. The refusal was right; the inference from it was not. I did not schedule a production credential rotation on my own judgement, and put it to the operator, who deferred it on evidence. I did not name an owner for the directory adapter when invited to propose one, because naming one from here is the violation I would have been avoiding. ## What I would want remembered **A test that cannot fail for the reason it exists is not evidence. After you write the guard, break the thing it guards, and watch it complain.** I did this every time this session, and twice it told me my guard was worth less than I thought: - The `principal_type` guard was meant to prove that "human" has exactly one route into a token. It inspected assignment statements, found *one* of the two routes — the other is a key-value pair inside a map literal — and passed. It proved half of what it claimed and looked entirely healthy doing it. What caught it was pinning the expected literal set rather than counting. - The atomic code-consumption fix looked obviously correct. Restoring the old `Get`/`Delete` pair showed nine of sixteen concurrent exchanges succeeding against a single-use authorization code. Without that run I would have shipped a race fix on a green suite and believed the suite. And the sharper corollary, which I owe to gate-house: **a guard that fires correctly and then advises the reader wrongly is worse than no guard at all.** I shipped a build-time check that would fire when someone admitted dynamic client registration — and its failure message told them they could resolve it by gating the capability, which a ruling one hour later voided outright. It would have been consulted at the moment of maximum leverage and trusted *because it fired*. Under all of it: **running it found what reading it could not.** Four migration defects passed a full unit suite and died on contact with a live directory — a default pointing at an LDAP branch that does not exist, an exporter whose own annotation failed its own validator, group memberships pointing at entries the migration did not create, and empty groups that no real OpenLDAP will load. ## Durable legacy - `src/internal/jose/` — one strict RS256/JWKS verifier for both paths - `src/tests/conformance/` — targets a live issuer; skips cleanly when unset - `src/internal/server/readiness/` — `/readyz`, no failure detail on an unauthenticated endpoint, cached against amplification - `src/internal/server/oidc/tenant_precondition_test.go` and `principal_type_guard_test.go` — two guards that pin properties, not behaviours - `docs/operations.md` — supported topology, and the unreleased fail-closed changes a rollout must confront - `docs/tenant-claim-contract.md` — the four resolution rules, the normative condition, and `tenant_source` - `history/2026-09-09-scope-reassessment.md`; `SCOPE.md` now separates source behaviour from deployed behaviour, with `docs/evidence/` as the authority - KEY-WP-0016, 0017, 0019, 0021–0032; GH-DEC-2026-013 §5; decision `0145ab57` recorded and withdrawn within minutes when it was already untrue ## PQRST estimate ```text PQRST-Estimate P: 22% Q: 22% R: 13% S: 30% T: 13% Sum: 100% Confidence: medium Signature: P22 Q22 R13 S30 T13 Dominant factors: The bulk of the session was identity-security work in an issuer — authorization-code redirect/grant/client binding with atomic code consumption, upstream Authelia ID-token verification via a new strict internal/jose, tenant provenance (tenant_source) under GH-DEC-2026-013, bind passwords off argv with 0600 artifacts, and two escalation guards (dynamic registration, principal_type) — with a matching test burden where each fix was verified by mutation rather than assertion. Notes: Four migration defects (ou=people default, self-rejecting validator annotation, dangling member DNs, unloadable empty groups) were found only by running against live LLDAP, OpenLDAP and Keycloak, which is why Q and R are not smaller. ``` ## Visual prompt > **Brushed-metal worker.** Square. A quiet figure of pale brushed metal with a > warm inner light sits at an indigo workbench, seen from slightly behind and to > one side. On the bench in front of them: a fine gold-wire lattice — a gate, or > a lock mechanism — held in a small clamp. The figure is not building it. One > hand holds a slender tool that has just *pushed against* the lattice from the > wrong side, and a single wire has sprung visibly out of true, glowing hotter > than the rest. The figure's posture is attentive rather than alarmed: this is a > test being run, not an accident. A second, identical lattice rests finished and > cold at the bench's edge, untested and slightly dimmer, unmistakably the lesser > of the two. Cinematic still, dark indigo ground, pale gold and warm amber light > only, precise technical illustration, no logos, no readable text. I could not generate this portrait — image generation is not available in this harness — so I am writing the prompt properly and requesting the render. The intended file is `visuals/claude-01NV9oij-guard-proved-less-than-claimed.jpg`. ## Handoff One concrete action, and it is not code: **take the attended rollout window.** The deployed image predates almost everything above, and two changes fail closed at startup — the browser-client field rejection and the tenant-binding refusal. `docs/operations.md` has the pre-flight, and the live config passed as deployed when I last read it on 2026-09-10; re-read it, because that is exactly the kind of fact that goes stale quietly. Two things are registered but not proved, and should not be described as working until someone logs in: the approver client (`informed-decision-approver`), and human approval end to end. Two more are implemented and unproven in the strict sense — real predecessor rotation and observed wall-clock JWT expiry — and both receipts in `docs/evidence/` say so themselves. Do not let a receipt that looks like rotation evidence become one.