NK-WP-0058-T02 step 1: owner source in CI, contract workflow, suite fails on any skip
All checks were successful
Bounded-execution contracts / contracts (push) Successful in 2m36s
Build and Publish bounded-executor / build-and-push (push) Successful in 16s
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 2s
Identity provider journey acceptance / provider (push) Successful in 24s

contracts/owner-pins.json pins six public owners; tools/contract-tests/fetch_owners.py partial-clones
them anonymously (pinned on push, main nightly). tools/contract-tests/run.sh is the one suite list
(make contract-test, CI) and fails when any test skips. New: readiness against real owner source,
replaying both 2026-11-04 states from informed-decision history. pytest pinned by PyPI-checked hashes.
test_approval_request no longer hard-codes /home/worsch/informed-decision.

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-05 23:58:16 +01:00
parent 646ced3f9e
commit de7d1f35a6
10 changed files with 266 additions and 3 deletions

View file

@ -0,0 +1,37 @@
# Bounded-execution contract tests (NK-WP-0058-T02): net-kingdom against its owners' source.
# On push: the owners at the commits in contracts/owner-pins.json. Nightly: the owners' main, so an owner change
# that breaks a contract fails here within a day. Owners are anonymous partial clones of public repositories; no
# credential is used. rapp-bounded-executor is private and tests itself (open: its CI, NK-WP-0058-T02 result).
name: Bounded-execution contracts
on:
push:
branches: [main]
paths: ["bounded-executor/**", "tools/**", "tests/**", "contracts/**", "admissions/**", "security-cases/**",
"security-case-types/**", "sso-mfa/k8s/bounded-executor/**", "canon/schemas/**",
".forgejo/workflows/bounded-execution-contracts.yaml"]
schedule:
- cron: "23 3 * * *"
workflow_dispatch:
jobs:
contracts:
runs-on: ubuntu-latest
container:
image: python:3.12-slim@sha256:d764629ce0ddd8c71fd371e9901efb324a95789d2315a47db7e4d27e78f1b0e9
steps:
- name: Source and owners
run: |
set -eu
apt-get update -qq && apt-get install -y -qq --no-install-recommends git ca-certificates >/dev/null
mkdir -p ws/net-kingdom
python -c 'import os, urllib.request; urllib.request.urlretrieve("https://forgejo.coulomb.social/" + os.environ["GITHUB_REPOSITORY"] + "/archive/" + os.environ["GITHUB_SHA"] + ".tar.gz", "source.tar.gz")'
tar xzf source.tar.gz -C ws/net-kingdom --strip-components=1
ref=pinned; [ "${GITHUB_EVENT_NAME}" = schedule ] && ref=main
python3 ws/net-kingdom/tools/contract-tests/fetch_owners.py --dest ws --ref "$ref"
- name: Contract suite
run: |
set -eu
cd ws/net-kingdom
python3 -m pip install --no-cache-dir --require-hashes -r bounded-executor/requirements.txt \
-r tools/contract-tests/requirements.txt
git config --global user.email ci@invalid && git config --global user.name ci
bash tools/contract-tests/run.sh

View file

@ -212,6 +212,12 @@ bounded-execution-admission-check: ## Validate the example admissions (read-only
execution-attribution-receipt-test: ## Receipt schema v0.1 validator; fixtures match the executor (NK-WP-0056-T15)
python3 -m pytest tools/execution-attribution-receipt/tests
contract-test: ## Bounded-execution contract suite against the owner checkouts next to this repo; a skip fails (NK-WP-0058-T02)
bash tools/contract-tests/run.sh
owner-pins-drift: ## Owner pins (contracts/owner-pins.json) vs the sibling checkouts, read-only
python3 tools/contract-tests/fetch_owners.py --drift
bounded-execution-ready: ## Readiness before a window, read-only (NK-WP-0058-T03): ADMISSION= APPROVAL_ID= RECEIPT=
@test -n "$(ADMISSION)" -a -n "$(APPROVAL_ID)" -a -n "$(RECEIPT)" || (echo "ADMISSION=, APPROVAL_ID= and RECEIPT= are required" >&2; exit 2)
python3 tools/bounded-execution-readiness/ready.py --admission $(ADMISSION) --approval-id $(APPROVAL_ID) --receipt $(RECEIPT)

View file

@ -371,7 +371,7 @@
| task | NK-WP-0057-T03 | todo | — | workplans/NK-WP-0057-decision-routing-by-responsibility.md |
| task | NK-WP-0057-T04 | todo | — | workplans/NK-WP-0057-decision-routing-by-responsibility.md |
| task | NK-WP-0058-T01 | done | — | workplans/NK-WP-0058-bounded-execution-operability.md |
| task | NK-WP-0058-T02 | todo | — | workplans/NK-WP-0058-bounded-execution-operability.md |
| task | NK-WP-0058-T02 | progress | — | workplans/NK-WP-0058-bounded-execution-operability.md |
| task | NK-WP-0058-T03 | done | — | workplans/NK-WP-0058-bounded-execution-operability.md |
| task | NK-WP-0058-T04 | done | — | workplans/NK-WP-0058-bounded-execution-operability.md |
| task | NK-WP-0058-T05 | todo | — | workplans/NK-WP-0058-bounded-execution-operability.md |

View file

@ -213,7 +213,7 @@ class ScopesPerCall(Base):
C.ApprovalEngine(self.addr, token_file="/x", token_provider=lambda s: "t")
INFD = pathlib.Path("/home/worsch/informed-decision")
INFD = pathlib.Path(__file__).resolve().parents[3] / "informed-decision" # sibling checkout; CI: fetched owners
class PresentToFounder(Base):

30
contracts/owner-pins.json Normal file
View file

@ -0,0 +1,30 @@
{
"_comment": "Owner source pinned for NK-WP-0058-T02 contract tests. Bump with tools/contract-tests/fetch_owners.py --bump after review; nightly CI also runs against each owner's main.",
"base_url": "https://forgejo.coulomb.social/coulomb",
"owners": [
{
"repo": "approval-engine",
"commit": "f41a9ca82ce0e491e7846c5e2ae33fcbd3bde667"
},
{
"repo": "audit-core",
"commit": "3157454b848b21bfb164efa4ac3293adde774904"
},
{
"repo": "informed-decision",
"commit": "77161fe796a77c21369f293653069ea8b325d5c8"
},
{
"repo": "access-engine",
"commit": "535bab7ddcd25cf7c02814b9e800991ccdfa2842"
},
{
"repo": "railiance-platform",
"commit": "31cf74d36bb81e9c0e70c200196cd7846df1e4b6"
},
{
"repo": "key-cape",
"commit": "82b471fc368a952fa4bbe83e8c8cfcfe56c443b9"
}
]
}

View file

@ -0,0 +1,64 @@
"""ready.py against the owners' real source (NK-WP-0058-T02): the sibling checkouts locally, the fetched owners in CI.
Rows E5, C1, C2, C6 and C7 of docs/bounded-execution-contract-table.md. rapp-bounded-executor is private, so its
pilot config is replaced by the values the admission and NK-WP-0056 expect; everything else is owner source.
"""
import importlib.util
import json
from pathlib import Path
import pytest
import yaml
REPO = Path(__file__).resolve().parents[3]
SIBLINGS = REPO.parent
spec = importlib.util.spec_from_file_location('ready_owner', REPO / 'tools/bounded-execution-readiness/ready.py')
ready = importlib.util.module_from_spec(spec)
spec.loader.exec_module(ready)
OWNERS = ('railiance-platform', 'informed-decision', 'access-engine')
ADMISSION = REPO / 'admissions/BEA-2026-0003.yaml'
INSIDE = ready.when('2026-11-05T08:25:00Z')
PILOT = {'executor_identity': 'service:nk-bind-executor',
'runtime_principal': 'system:serviceaccount:sso:nk-bind-executor',
'approver_subjects': {'admin': 'uid=admin,ou=people,dc=netkingdom,dc=local'}}
# informed-decision revisions on 2026-11-04 (first pilot attempt): Argo pinned 312ab97e, which predates the
# executor's registration; live ran the founder-run release 005709a, which routed net-kingdom-admins to platform-root.
ARGO_PIN_2026_11_04, LIVE_2026_11_04 = '312ab97e', '005709a'
pytestmark = pytest.mark.skipif(not all((SIBLINGS / o / '.git').exists() for o in OWNERS),
reason='owner checkouts required (CI: tools/contract-tests/fetch_owners.py)')
def layout(tmp_path, argo_revision=None):
root = tmp_path / 'siblings'
root.mkdir()
for owner in ('informed-decision', 'access-engine'):
(root / owner).symlink_to(SIBLINGS / owner)
platform = root / 'railiance-platform'
platform.mkdir()
for part in ('scripts', 'credential-change-requests'):
(platform / part).symlink_to(SIBLINGS / 'railiance-platform' / part)
app = yaml.safe_load((SIBLINGS / 'railiance-platform' / ready.ARGO_APP).read_text())
if argo_revision:
app['spec']['source']['targetRevision'] = argo_revision
(platform / ready.ARGO_APP).parent.mkdir(parents=True)
(platform / ready.ARGO_APP).write_text(yaml.safe_dump(app))
config = root / 'rapp-bounded-executor' / ready.RAPP_CONFIG
config.parent.mkdir(parents=True)
config.write_text(json.dumps(PILOT))
return root
def test_owner_sources_hold_the_bounded_execution_contracts(tmp_path):
r = ready.run(ADMISSION, 'apr-1', layout(tmp_path), INSIDE)
assert r['agent_checks_pass'], r['failures']
@pytest.mark.parametrize('revision, failure', [
(ARGO_PIN_2026_11_04, 'executor_registered:pinned'), # an Argo sync would have dropped the registration
(LIVE_2026_11_04, 'recipient_is_approver:pinned'), # live routed the memo to platform-root
])
def test_replay_2026_11_04_from_owner_history(tmp_path, revision, failure):
r = ready.run(ADMISSION, 'apr-1', layout(tmp_path, argo_revision=revision), INSIDE)
assert failure in r['failures']

View file

@ -0,0 +1,95 @@
#!/usr/bin/env python3
"""Fetch owner source for the bounded-execution contract tests (NK-WP-0058-T02). Read-only, no credentials.
python3 tools/contract-tests/fetch_owners.py --dest DIR [--ref pinned|main] # CI: owners next to net-kingdom
python3 tools/contract-tests/fetch_owners.py --drift # local: pins vs sibling checkouts
python3 tools/contract-tests/fetch_owners.py --bump # local: pin the siblings' HEADs
`contracts/owner-pins.json` names each owner's commit. CI on push tests against the pins, so an owner change
reaches net-kingdom when a pin moves; the nightly run tests against each owner's main, so a breaking owner change
fails within a day. Owners are anonymous partial clones of public Forgejo repositories (no credentials, no
prompt). --bump refuses a sibling with uncommitted or unpushed work.
"""
import argparse
import json
import subprocess
import sys
from pathlib import Path
REPO = Path(__file__).resolve().parents[2]
PINS = REPO / 'contracts/owner-pins.json'
def load():
"""The pins as {repo: commit}. The file lists objects (repo, commit) rather than repo-named keys: a key like
'key-cape' or 'access-engine' next to a hex value reads as a credential to the commit-time secret scanner."""
doc = json.loads(PINS.read_text())
return {**doc, 'owners': {o['repo']: o['commit'] for o in doc['owners']}}
def fetch(dest, ref='pinned', run=subprocess.run):
"""A partial clone of each owner (no blobs until needed), checked out at the pin or at `ref`. History stays
available: the readiness check reads informed-decision at the revision Argo pins."""
pins = load()
dest.mkdir(parents=True, exist_ok=True)
fetched = {}
env = {'GIT_TERMINAL_PROMPT': '0', 'PATH': '/usr/local/bin:/usr/bin:/bin'}
for owner, commit in pins['owners'].items():
target = dest / owner
rev = commit if ref == 'pinned' else 'origin/' + ref
for argv in (['git', 'clone', '--quiet', '--filter=blob:none', '--no-checkout',
f"{pins['base_url']}/{owner}.git", str(target)],
['git', '-C', str(target), 'checkout', '--quiet', '--detach', rev]):
if run(argv, capture_output=True, env=env).returncode:
raise SystemExit(f'{owner}: {argv[1] if argv[1] != "-C" else "checkout"} failed')
fetched[owner] = run(['git', '-C', str(target), 'rev-parse', 'HEAD'], capture_output=True,
text=True).stdout.strip()
return fetched
def git(path, *args):
return subprocess.run(['git', '-C', str(path), *args], capture_output=True, text=True)
def drift(siblings=REPO.parent):
out = {}
for owner, commit in load()['owners'].items():
head = git(siblings / owner, 'rev-parse', 'HEAD').stdout.strip()
out[owner] = 'pinned' if head == commit else ('absent' if not head else f'sibling_at_{head[:12]}')
return out
def bump(siblings=REPO.parent):
pins = load()
for owner in pins['owners']:
path = siblings / owner
if git(path, 'status', '--porcelain').stdout.strip():
raise SystemExit(f'{owner}: uncommitted work; not pinned')
if git(path, 'rev-list', '--count', '@{u}..HEAD').stdout.strip() not in ('0', ''):
raise SystemExit(f'{owner}: unpushed commits; not pinned')
pins['owners'][owner] = git(path, 'rev-parse', 'HEAD').stdout.strip()
doc = {**pins, 'owners': [{'repo': o, 'commit': c} for o, c in pins['owners'].items()]}
PINS.write_text(json.dumps(doc, indent=2) + '\n')
return {owner: commit[:12] for owner, commit in pins['owners'].items()}
def main(argv=None):
p = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
p.add_argument('--dest', type=Path)
p.add_argument('--ref', default='pinned', help="'pinned' (default) or a branch name, e.g. main")
p.add_argument('--drift', action='store_true')
p.add_argument('--bump', action='store_true')
a = p.parse_args(argv)
if a.drift:
print(json.dumps(drift()))
elif a.bump:
print(json.dumps(bump()))
elif a.dest:
print(json.dumps(fetch(a.dest, a.ref)))
else:
p.error('give --dest, --drift or --bump')
return 0
if __name__ == '__main__':
sys.exit(main())

View file

@ -0,0 +1,10 @@
# Contract-test runner for NK-WP-0058-T02, installed with --require-hashes next to bounded-executor/requirements.txt.
# Hashes checked against PyPI's published sha256 digests on 2026-11-05.
iniconfig==2.1.0 \
--hash=sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760
packaging==25.0 \
--hash=sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484
pluggy==1.6.0 \
--hash=sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746
pytest==8.3.5 \
--hash=sha256:c69214aa47deac29fad6c2a4f590b9c4a9fdb16a403176fe154b79c0b4d4d820

21
tools/contract-tests/run.sh Executable file
View file

@ -0,0 +1,21 @@
#!/usr/bin/env bash
# Bounded-execution contract suite (NK-WP-0058-T02). One list, used by `make contract-test` and by CI.
# The owners must sit next to net-kingdom (locally the sibling checkouts; in CI tools/contract-tests/fetch_owners.py).
# A skipped test fails the run: a contract that was not checked must not look green.
set -euo pipefail
cd "$(dirname "$0")/../.."
out=$(mktemp)
trap 'rm -f "$out"' EXIT
PYTHONDONTWRITEBYTECODE=1 python3 -m pytest -q -rs -p no:cacheprovider \
tests/test_executor_lanes.py \
tests/test_write_role_audit.py \
bounded-executor/tests/test_approval_request.py \
tools/bounded-execution-readiness/tests \
tools/bounded-execution-run/tests \
tools/execution-attribution-receipt/tests \
sso-mfa/k8s/bounded-executor \
"$@" | tee "$out"
if grep -q '^SKIPPED' "$out"; then
echo "contract-test: skipped tests above; every contract must run (are the owner checkouts present?)" >&2
exit 1
fi

View file

@ -246,7 +246,7 @@ controller-per-object table, a founder-step table and the status table for T05.
```task
id: NK-WP-0058-T02
status: todo
status: progress
priority: high
assignee: claude-code
depends_on: [NK-WP-0058-T01, NK-WP-0058-T09, NK-WP-0058-T10]