From 7bd6033b16e36d703b6b1283b66fffff441a83f9 Mon Sep 17 00:00:00 2001 From: tegwick Date: Sat, 31 Oct 2026 23:04:44 +0100 Subject: [PATCH] NK-WP-0056-T18: accept audit-core 202/200, distinct 409; progress note audit-core review (AUDIT-WP-0018-T02): a first accept is 202 and a duplicate 200; the client accepted only 200/201, so the executor would have stopped with audit_unavailable after consuming its approval. 409 (run id reused with a different body) is now a distinct, never-retried defect. 58 tests. Co-Authored-By: Claude Opus 5.5 Assistant: claude-code Assistant-Model: opus Assistant-Process: 5502@bnt-lap001 Assistant-Session: 68b3b02f-5d0f-4e71-8421-796f62e50196 --- WORK-RECORDS.md | 2 +- bounded-executor/clients.py | 11 ++++++-- bounded-executor/tests/test_clients.py | 25 +++++++++++++++++++ workplans/NK-WP-0056-bounded-executor-path.md | 25 ++++++++++++++++++- 4 files changed, 59 insertions(+), 4 deletions(-) diff --git a/WORK-RECORDS.md b/WORK-RECORDS.md index c514a88..20472a8 100644 --- a/WORK-RECORDS.md +++ b/WORK-RECORDS.md @@ -360,7 +360,7 @@ | task | NK-WP-0056-T15 | todo | — | workplans/NK-WP-0056-bounded-executor-path.md | | task | NK-WP-0056-T16 | wait | — | workplans/NK-WP-0056-bounded-executor-path.md | | task | NK-WP-0056-T17 | done | — | workplans/NK-WP-0056-bounded-executor-path.md | -| task | NK-WP-0056-T18 | todo | — | workplans/NK-WP-0056-bounded-executor-path.md | +| task | NK-WP-0056-T18 | progress | — | workplans/NK-WP-0056-bounded-executor-path.md | | task | NK-WP-0056-T19 | todo | — | workplans/NK-WP-0056-bounded-executor-path.md | | intake | NK-IN-0001 | closed | blue | docs/intakes/activity-core-ops-sso-operators.md | | intake | NK-IN-0002 | closed | blue | docs/intakes/activity-core-ops-sso-operators.md | diff --git a/bounded-executor/clients.py b/bounded-executor/clients.py index 0e1b064..3185a4d 100644 --- a/bounded-executor/clients.py +++ b/bounded-executor/clients.py @@ -149,6 +149,13 @@ class AuditCore: self.http = _Http(base, token_file, ca_file) def emit(self, event: dict) -> None: - status, _ = self.http.call("POST", "/v1/events", event, {"Idempotency-Key": event["id"]}) - if status not in (200, 201): + """audit-core answers 202 for a first accept and 200 for an idempotent duplicate (AUDIT-WP-0018-T02 tests). + 409 means the run id was reused with a different body: a defect, never retried.""" + try: + status, _ = self.http.call("POST", "/v1/events", event, {"Idempotency-Key": event["id"]}) + except ClientError as error: + if str(error).endswith(" 409"): + raise ClientError("audit conflict 409: run id reused with a different body") from None + raise + if status not in (200, 201, 202): raise ClientError(f"audit {status}") diff --git a/bounded-executor/tests/test_clients.py b/bounded-executor/tests/test_clients.py index b06a5ca..81002e9 100644 --- a/bounded-executor/tests/test_clients.py +++ b/bounded-executor/tests/test_clients.py @@ -103,6 +103,31 @@ class ClientTests(unittest.TestCase): self.assertEqual(str(caught.exception), "GET 403") +class AuditStatuses(unittest.TestCase): + """audit-core: 202 first accept, 200 duplicate, 409 reused run id (audit-core AUDIT-WP-0018-T02, 2026-10-31).""" + + def emit_with(self, status): + fake = Fake() + fake.status = status + server = serve(fake) + self.addCleanup(server.shutdown) + tmp = tempfile.TemporaryDirectory() + self.addCleanup(tmp.cleanup) + token = pathlib.Path(tmp.name) / "token" + token.write_text("synthetic-bearer\n") + C.AuditCore(f"http://127.0.0.1:{server.server_port}", str(token)).emit({"id": "run:r:started"}) + + def test_202_and_200_are_stored(self): + self.emit_with(202) + self.emit_with(200) + + def test_409_is_a_distinct_defect(self): + with self.assertRaisesRegex(C.ClientError, "conflict 409"): + self.emit_with(409) + with self.assertRaisesRegex(C.ClientError, "500"): + self.emit_with(500) + + class CliTests(unittest.TestCase): def test_exit_codes(self): self.assertEqual(CLI.exit_code({"outcome": "completed"}), 0) diff --git a/workplans/NK-WP-0056-bounded-executor-path.md b/workplans/NK-WP-0056-bounded-executor-path.md index 368675d..0573f41 100644 --- a/workplans/NK-WP-0056-bounded-executor-path.md +++ b/workplans/NK-WP-0056-bounded-executor-path.md @@ -757,7 +757,7 @@ Next: ```task id: NK-WP-0056-T18 -status: todo +status: progress priority: high assignee: claude-code depends_on: [NK-WP-0056-T14] @@ -777,6 +777,29 @@ Presenting the approval to the founder follows the alignment review's "present a **Done when:** the request and run phases are tested on fakes, and the new image is pinned. +**2026-10-31, built:** + +- `b71f509`: `KeyCapeClientCredentials` (`client_secret_basic`, one scope per token, public Host for the in-cluster + URL, no redirects). +- `ApprovalEngine` takes a per-scope token provider and can create approvals. +- `cli.py --request` files the approval as `service:nk-bind-executor`: + - principal is the first approver's subject; + - target is the admission id and digest; + - validity is the admission window; + - `human_control` is on; + - `required_count` is the number of listed approvers; + - the client secret comes through its own OpenBao lane, is read once, and that login is revoked at once. +- audit-core's review found that the audit client rejected 202, the status of a first accept. The executor would + have stopped after consuming its approval. Fixed: 202 and 200 count as stored, and 409 is a distinct defect. + 58 executor tests. +- rapp `48566a6`: + - the config names the executor's own client, and the renderer requires it exactly; + - the run Job mounts only the audit-core bearer; + - `make request` adds a request Job. + +Left: the image and its pin. A live request also needs the KeyCape client (T14 type acknowledged, then the case +live) and informed-decision's `present_existing`. + ## Executor approval exercise against the real services ```task