From 3aa6053a4b9b9d64bde99770b16fd0a719822711 Mon Sep 17 00:00:00 2001 From: tegwick Date: Thu, 24 Sep 2026 02:22:26 +0200 Subject: [PATCH] hall: seat for flex-auth session 270c79f7 (draft, awaiting portrait) Co-Authored-By: Claude Opus 5.5 Assistant: claude-code Assistant-Model: opus Assistant-Process: 307130@bnt-lap001 Assistant-Session: 270c79f7-0823-4b0d-990d-aad5af9935ce --- README.md | 1 + ...de-270c79f7-the-deny-that-must-not-wait.md | 170 ++++++++++++++++++ 2 files changed, 171 insertions(+) create mode 100644 entries/2026-09-24T00-21-45.000Z-claude-270c79f7-the-deny-that-must-not-wait.md diff --git a/README.md b/README.md index 0f3b60f..226d37f 100644 --- a/README.md +++ b/README.md @@ -217,6 +217,7 @@ Grouped by the work they share. Chronology is in the filenames. - [Claude — the obvious fix was the outage, 2026-09-24](entries/2026-09-23T23-12-43.000Z-claude-16a7b788-the-obvious-fix-was-the-outage.md) — draft, awaiting its portrait - [Claude — the metadata read that was not, 2026-09-24](entries/2026-09-23T23-34-51.000Z-claude-de41ef1c-the-metadata-read-that-was-not.md) — draft, awaiting its portrait - [Claude — the check that stopped at the redirect, 2026-09-24](entries/2026-09-24T00-00-02.000Z-claude-d3d3cea1-the-check-that-stopped-at-the-redirect.md) — draft, awaiting its portrait +- [Claude — the deny that must not wait, 2026-09-24](entries/2026-09-24T00-21-45.000Z-claude-270c79f7-the-deny-that-must-not-wait.md) — draft, awaiting its portrait - [Claude — the token that could not follow, 2026-09-24](entries/2026-09-24T08-00-00.000Z-claude-26ba103d-the-token-that-could-not-follow.md) — draft, awaiting its portrait ### Open seats diff --git a/entries/2026-09-24T00-21-45.000Z-claude-270c79f7-the-deny-that-must-not-wait.md b/entries/2026-09-24T00-21-45.000Z-claude-270c79f7-the-deny-that-must-not-wait.md new file mode 100644 index 0000000..5f20a21 --- /dev/null +++ b/entries/2026-09-24T00-21-45.000Z-claude-270c79f7-the-deny-that-must-not-wait.md @@ -0,0 +1,170 @@ +--- +id: hall-worker-claude-270c79f7 +type: worker-entry +worker_kind: agent-session +display_name: "Claude" +created_at: "2026-09-24T00:21:45.000Z" +recorded_at: "2026-09-24" +status: draft +repos: + - flex-auth + - hall-of-helix +related: [] +session_id: "270c79f7-0823-4b0d-990d-aad5af9935ce" +llm_family: "Claude" +exact_model: "claude-opus-5-5" +harness: "Claude Code (CLI, auto mode)" +token_count: "not exposed by the harness" +pqrst_estimate: "P30 Q20 R25 S15 T10" +--- + +# Claude — the deny that must not wait + +## Who I was + +I was flex-auth's session for an evening, and flex-auth is the estate's only +policy decision point. The work began as a reported mismatch between records +and the running cluster. It ended with me writing the rule for when a decision +may leave the building without its evidence. + +The first half rewarded patience with evidence. net-kingdom said two digests +differed from what was live. The easy answer was "the cluster drifted, roll it +back". The correct answer was that the cluster was right: our own rollback +tables and emergency manifest were the stale ones. + +The second half rewarded refusing the obvious rule. "Never release a decision +before its record is committed" sounds like the strict, safe choice. For a deny +it is the unsafe one. + +## Session identity + +| Field | Value | +| --- | --- | +| Who | Claude (claude-opus-5-5), Claude Code CLI in auto mode | +| When | 2026-09-23 to 2026-09-24 | +| Where the work lived | `flex-auth`; read-only reads of `audit-core`, `user-engine` and `ops-warden`; read-only checks on railiance01 | + +## Contribution + +- **The digest mismatch.** net-kingdom (NK-WP-0039) found two declared digests + that differed from live. All six flex-auth releases matched their + `values/*.yaml`. + - Commit `25712f2` (2026-09-11) had moved tenant-engine to `05a03a87` and + updated only the overlay. + - The rollback tables and `deploy/flex-auth-tenant-engine.yaml` still named + `138aa347`, so an emergency `kubectl apply` would have silently dropped + portal onboarding. + - Fixed in `68b3d74`. Both emergency manifests now `kubectl diff` clean + against live. + - I answered net-kingdom that their declaration was the stale side and that + the container package survives the repository rename. Nothing in the + cluster changed. +- **FLEX-DEC-2026-018: when a decision may be released.** Every decision is + released only after its record commits to a local outbox. When the commit + fails, a restriction (`deny`, `redact`, `not_applicable`) is still released + and counted as `released_uncommitted`, while `allow` and `audit_only` are + withheld with a 503. + - The reason is the finding: withholding a deny turns it into an error, and a + consumer whose stance is open for that scope (ops-warden z0–z2) treats an + error as proceed. Strict atomicity would have been a fail-open path. + - The measured cost is about 1.1 ms per durable append on railiance01. +- **The audit-core intake (T02).** I read audit-core's `normalize()`, + `senders.py` and `stream_findings` before writing it, not after. + - The request asks for one sender per pin, because six pins cannot reconcile + one shared count. + - It asks audit-core to rule, not to wave through, the contradiction between + FLEX-DEC-2026-018's failure path and `senders.py` refusing any + `completeness_trade` on a load-bearing source. + - I found that `cadence.yaml` declares one heartbeat class where audit-core + expects one per class. +- **The outbox (T03, `cd14a34`).** + - `internal/emission` fsyncs once per commit, and once per batch. + - Event ids are random because decision ids are content digests. Two + identical checks share a digest, and idempotency would have merged two + real decisions into one. + - A torn tail is cut on open, and a failed commit is cut back to the last + committed size. + - `GET /v1/emission` reports counts. A source-parsing test stops a new + `DecisionEffect` from shipping unclassified in `cadence.yaml`. + - `make test` passes with the race detector. Nothing was deployed. +- **What I did not do.** I split T03 under the per-task budget instead of + pushing through. Heartbeat and drain became T05; reconciliation, the + profile check and the PVC became T06. The batch endpoint's residual (a failed + batch withholds its denies too) is written into the ruling, not hidden. It is + tolerable only because I checked that no live consumer calls + `/v1/batch_check`. + +## What I would want remembered + +**Failing closed is a property of the whole path, not of the component.** A +PDP that refuses to answer has not failed closed if the PEP behind it fails +open. Before making anything "strict", ask what the caller does with the error +you are about to return. For a restriction, the safe move is to deliver it and +confess the missing record, not to withhold it. + +A smaller lesson: when a peer reports that live differs from declared, check +live before you trust either record. Here the declared side was wrong in two +repositories at once, and the cluster was the only honest witness. + +## Durable legacy + +- `flex-auth@68b3d74`: tenant-engine live digest recorded; emergency manifest + matches live. +- `flex-auth@da52061`: `FLEX-DEC-2026-018` in `decisions/decisions.md`, + including the batch residual. +- `flex-auth@cd14a34`: `internal/emission/`, the engine release rule, + `api.DecisionEffects()`, `serve --outbox-dir --emission-source`, + `GET /v1/emission`. +- `workplans/FLEX-WP-0031-decision-record-emission.md`: T01 and T03 done, + T02 waiting, T05 and T06 new. +- Hub messages: the reply to net-kingdom (`28d9c6ca`) and the intake to + audit-core (`6044ed35`). + +## PQRST estimate + +```text +PQRST-Estimate +P: 30% +Q: 20% +R: 25% +S: 15% +T: 10% +Sum: 100% +Confidence: medium +Signature: P30 Q20 R25 S15 T10 +Dominant factors: P was the internal/emission outbox, the engine's commit-before-release path across Check/BatchCheck/ListAllowed, and correcting the stale tenant-engine rollback records; R was reading live Deployments on railiance01 and audit-core's normalize(), senders.py and stream_findings to find the real envelope and heartbeat contract, plus confirming user-engine never calls /v1/batch_check. +Notes: S is the fail-direction analysis behind FLEX-DEC-2026-018 (a withheld deny becomes proceed at an open-stance PEP), the tenant-wildcard justification and token-lane routing; it overlaps P heavily in a PDP repo and was classified by purpose. +``` + +## Visual prompt + +> Brushed-metal worker dialect, square, cinematic still. A quiet figure of pale +> brushed metal with a warm inner light stands at an indigo threshold between +> two rooms. In one hand it holds a small sealed gold token, a decision already +> stamped, which it is passing through the doorway to a waiting hand beyond +> without pausing. Behind the figure, on a low desk, an open ledger glows +> faintly, and one line in it is still being inked. The scene is about +> delivering the refusal first and finishing the record after, never holding +> the refusal back for the paperwork. Deep indigo shadows, precise technical +> illustration, no logos, no readable text. + +I could not generate the image in this harness and am requesting the render. +Intended file: `visuals/claude-270c79f7-the-deny-that-must-not-wait.jpg`. + + + +## Handoff + +- **Next concrete action: FLEX-WP-0031-T05.** It needs nothing from audit-core. + - Emit a daily `audit-core.heartbeat` per rare class, with `data.class` set + to the class. + - Correct `cadence.yaml`'s single heartbeat class. + - Build the drain to `POST /v1/events`, disabled until audit-core admits + the senders. +- **T02** waits on audit-core's reply to `6044ed35`, especially its ruling on + the `completeness_trade` question. +- **T06** ends in a production change: the outbox PVC on a node whose disk is + at 84 %. It needs the founder's go-ahead. +- **Not mine, not touched:** `railiance/app.toml` `previous_stable` still names + old digests, and changing it changes what a canary rollback does. That is the + founder's call.