NK-WP-0058-T08 worker protocol and excerpt tool; T06 window watch (row O5), first use of the protocol

T08: .claude/rules/worker-protocol.md (imported by CLAUDE.md, pointed to from AGENTS.md) and
tools/contract-tests/excerpt.py: name the rows, hand every worker one excerpt, one owner per step, check
with the excerpt's tests. A test requires every table row to have details and a contract test (found O4).
T06: bounded-executor/window_watch.py flags or closes the write role when no run Job is active or recent;
stub tests; in the image. Built from the excerpt for O5, E1, C1, E8, E9, R2.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 943911@bnt-lap001
Assistant-Session: fc322940-1c8e-4bed-ab39-86cec99f23a0
This commit is contained in:
tegwick 2026-11-06 01:36:09 +01:00
parent 558bfce848
commit c63aa398d6
9 changed files with 398 additions and 3 deletions

View file

@ -311,7 +311,8 @@ NK-WP-0058-T09, as of 2026-11-05. It uses the same revisions as part 1, plus acc
`sso/nk-bind-executor-audit` key `audit-token`. That is a removal or correction request to railiance-platform.
- audit-core's seeder hard-codes `/home/worsch/railiance-platform/...CCR-2026-0063...yaml` and the cluster UID
(`seed-nk-bounded-executor-sender.py:58-69`), for T07.
- the names appear in four repos (CCR, seeder, ESO manifest, rapp Job) and no test compares them.
- the names appear in four repos (CCR, seeder, ESO manifest, rapp Job); since T02,
`tools/contract-tests/tests/test_custody_names.py` compares them, and the rapp's CI checks the Job mount.
### C6: the executor's submitter profile
@ -368,6 +369,7 @@ evidence `docs/evidence/2026-11-04-nk56-t06-go-live/`, `2026-11-04-nk56-t06-pilo
| O2 | warden lanes ↔ child scripts | ops-warden: `warden access <lane> --exec` (silent, one command, fails on output, revokes on every path). Children: railiance-platform and audit-core silent scripts | the founder; seven lane invocations in the go-live runbook | none across repos | Seed r1 refused `token_env_must_be_unset`: warden strips `BAO_TOKEN` and `VAULT_TOKEN`, not `OPENBAO_TOKEN` | **keep**; **owner request** to ops-warden |
| O3 | Platform-admin session: lane vs shell | Two patterns: ops-warden lanes (railiance-platform, audit-core) and an interactive `bao login -method=oidc -path=netkingdom role=platform-admin` in the shell (net-kingdom `executor-lanes.py`, `pilot-run.sh`, T06 runbook 6c) | `executor-lanes.py attended_platform_admin()` | none | 2026-11-05: `window-close` failed `phase: identity, LaneError` in a shell without a login | **merge** into one pattern |
| O4 | Cluster access routes | Three in one runbook: `kubectl --kubeconfig ~/.kube/config-railiance01` (admin); `keycape-client.py` via `portal-client-rollout.py` hard-coding `/home/worsch/.kube/config-hosteurope` with `--server https://127.0.0.1:16444` (cluster UID pinned); the agent's default context (NetworkPolicy reads only) | founder, agents | none | none directly; costs founder setup | **merge** (T07: no hard-coded kubeconfigs) |
| O5 | Window watch: the write role outside a run | net-kingdom `bounded-executor/window_watch.py` (rule, modes); rapp CronJob `bounded-executor-window-watch` (schedule, identity); railiance-platform CCR for its OpenBao role (read, or read and delete, on the write role only) | the founder; the agent status identity (CronJob status) | `bounded-executor/tests/test_window_watch.py` (stubs); rapp packaging tests | 2026-11-04/05: the write role stayed open overnight (T11) | **keep** (new, NK-WP-0058-T06) |
| F1 | Founder ↔ runbook steps | net-kingdom T06 runbook, railiance-platform go-live, audit-core founder runbook | the founder | live only | about 50 copy-paste rounds; values carried by hand between steps (table below) | **merge** into T04's commands |
| F2 | Founder identity in the browser | informed-decision UI at aal2; the identity model (`admin` for production, `platform-root` kept out) | the founder; executor `_check_approval` | none | The first approval came as `uid=platform-root`, the identity signed in at that moment, and was refused | **keep** the refusal; add identity hygiene to T04 |
@ -404,6 +406,34 @@ Argo pin with live) cover the gap.
as silent children of the warden lane (`executor-lanes.py --quiet`). `ready` and `run` each need one login,
and no shell login is left open. `pilot-run.sh` is deleted.
### O4: one cluster route
- **Contract (T07, 2026-11-06):** tools and runbooks reach the cluster through one route, `NK_KUBECONFIG`,
defaulting to `~/.kube/config-railiance01`. No second kubeconfig file, absolute home path or `--server` override.
- **Exceptions:** node-local commands on railiance01 use the node's own `/etc/rancher/k3s/k3s.yaml`. They're listed
in `tools/conventions/lint.py` with their reasons.
- **The agent's own read-only route (T05):** `~/.kube/config-agent-status`, identity `sso/nk-agent-status`, 1 h
tokens.
- **Checked by:** the `one-cluster-route` rule of `tools/conventions/lint.py`.
### O5: window watch
- **Rule:** the write role `net-kingdom-bind-executor-svc-authelia-write` exists, no bounded-executor run Job is
active, and none was created in the last 10 minutes. Run Jobs are the Jobs labelled
`app.kubernetes.io/name=bounded-executor` without an `app.kubernetes.io/component` label. A role in that state is
a window left open.
- **Modes:**
- `flag` (read-only): the watch Job exits 3 and its status shows `Failed`.
- `close`: delete the role, prove it is absent, and exit 0 with `closed_stale`.
Closing only reduces privilege; a run that was about to start is then refused before any effect.
- **Identity:** ServiceAccount `sso/nk-window-watch`, projected token with audience `openbao`. Its OpenBao role is
`net-kingdom-bounded-executor-window-watch`, with a policy of `read` (flag) or `read` and `delete` (close) on
`auth/kubernetes/role/net-kingdom-bind-executor-svc-authelia-write` only, plus token self-lifecycle. Its
Kubernetes Role allows `list` on Jobs in `sso`. No Secrets.
- **Output:** one value-free line: `ok`, `flagged_stale` or `closed_stale`, the count of run Jobs and the age of the
newest one. It holds no token and no value.
### F1: the founder's steps, 2026-11-04/05
| Step | Authority it needs | Value carried by hand | Value-free result | Safe to repeat |
@ -526,7 +556,8 @@ The agent's default context can read NetworkPolicies in `sso`, `approval-engine`
| C1 | `tests/test_executor_lanes.py` runs railiance-platform's real helper modules | net-kingdom contract CI |
| C2, C3 | the readiness check: `case_type_bytes_match_broker`, `case_materialized` | net-kingdom contract CI |
| C4, C5 | `tools/contract-tests/tests/test_custody_names.py` | net-kingdom contract CI |
| O2–O4, F1, F2 | `tools/bounded-execution-run/tests/test_bx.py` (lane child silent, window always closed); conventions in T07 | net-kingdom contract CI |
| O5 | `bounded-executor/tests/test_window_watch.py` (rule and modes on stubs); rapp `tests/test_packaging.py` (CronJob shape) | net-kingdom contract CI; rapp CI |
| O2–O4, F1, F2 | `tools/bounded-execution-run/tests/test_bx.py` (lane child silent, window always closed); `tools/conventions/lint.py` (`one-cluster-route` for O4) | net-kingdom contract CI |
Not covered from source: OpenBao's ingress (rapp-openbao is private) and live drift from source, such as the
approval-engine policy that was live but not in its file. The readiness preflight Job catches unreachable endpoints