Skip to content

Code review · T5, scenario runner and isolation

review-code on autopilot/T5, head 5497cfc, 2026-10-28. The step adds dev/scenario.py (up, down, list), the scenario files and unit tests in tests/scenarios/, the isolation proof tests/scenarios/parallel.sh and its workflow scenarios.yml, and changes dev/k8s/cluster.sh for per-scenario kind clusters. Reviewers: code-reviewer and security-reviewer, independent, in parallel. Format: README.md.

Round Head code-reviewer security-reviewer
1 5497cfc changes (0 blockers, 1 major, 6 minors) changes (0 blockers, 2 majors, 5 minors)
2 4148643 (the round-1 fixes, delta 5497cfc..4148643) changes (1 blocker, 0 majors, 4 minors) approve (0 blockers, 0 majors, 5 minors)
3 aa05608 (the round-2 fixes, delta 4148643..aa05608, code only) changes (0 blockers, 1 major, 1 minor) not run
4 aaff4e0 (the round-3 fixes, PR #43, code only) approve (0 blockers, 0 majors, 2 minors) not run

The fixes are in the commit that adds this page (d0d34cb) and in e347ff4, on autopilot/T5, and were reviewed in round 2. The round-2 fixes are in the commit that adds the round-2 rows and were reviewed in round 3; the round-3 fixes (PR #43, squash aaff4e0, every check green on 5158f4c, scenarios.yml included) were reviewed in round 4. code-simplifier was not run. docker, sind and kind cannot run in a Claude Code cloud session (cgroup v1), so the fixes were proved in CI on e347ff4: scenarios.yml 40688967336 and pipelines-k8s.yml 40688967168 green. d0d34cb alone broke pipelines-k8s.yml (40688649443: cluster.sh set its data directory before dev/lib.sh defined SPECTIO_REPO). shellcheck -x and bash -n pass on dev/k8s/cluster.sh and tests/scenarios/parallel.sh. Test command:

python3 -m unittest discover -s tests/scenarios -p 'test_*.py'
CODE  step=T5  review=approve b/M/m=1/2/13  security=approve b/M/m=0/2/10  rounds=4  simplify=notrun -0/+0  tests=pass
Id Severity Finding Status
CR-1 major A failed up left a partial environment (network, realm or cluster, data directory), and the error message printed a literal <id> fixed in the commit that adds this page: after the ID is claimed, any exception in up runs down for that ID and is re-raised; the message no longer names a placeholder; unit test
CR-2 minor down deleted the data directory even when deleting the realm or cluster failed fixed in the commit that adds this page: the data directory is kept when a step fails, so the ID stays claimed; unit test. A follow-up commit also keeps it when sind or kind is missing from the host PATH for a scenario whose scenario.json names a SLURM or Kubernetes scheduler (a realm or cluster would otherwise leak silently); unit test
CR-3 minor --id 0 was read as "no ID" and took the lowest free one fixed in the commit that adds this page: n is not None in claim; ID 0 is rejected; unit test
CR-4 minor No test of claim, the atomic ID claim fixed in the commit that adds this page: 8 threads calling claim(None) get 8 distinct IDs, claim(3) twice exits; STATE points at a temporary directory
CR-5 minor The slurm-site, slurm-rest and k8s-kueue profiles are not exercised by CI kept, recorded: dev-env §5 "Built in T5" and T5 Findings item 8
CR-6 minor parallel.sh required kind get clusters to equal spx-3, had no cleanup on failure, and did not say it is CI-only fixed in the commit that adds this page: grep -qx spx-3, an EXIT trap that runs down for IDs 1-4 on failure, and a header that says CI only (dedicated runner)
CR-7 minor A stale "[to be validated: measured by scenarios.yml]" comment, a job named "three scenarios" that runs four, an unused loop variable fixed in the commit that adds this page: the comment points at the measurement in dev-env §5 (run 40685163237), the job is "four scenarios in parallel", the variable is _
SEC-T5-01 major down ran sind, kind and docker through a PATH that put .scenarios/N/tools/bin first, which cluster jobs can write through the sind data mount fixed in the commit that adds this page: down uses the host environment plus KUBECONFIG only; unit test (subprocess mocked) that no command or lookup gets a PATH with the data directory
SEC-T5-02 major Every kind node mounted the whole repository read-write, so a pod could read or change other scenarios' .scenarios/<id>/ (kubeconfigs, tools) fixed in the commit that adds this page: the repository is mounted readOnly: true (kind extraMounts[].readOnly, kind configuration), plus one read-write mount of the data directory (.scenarios/N, or .scenarios/k8s for the T4 harness); parallel.sh checks with docker inspect that the only read-write bind under the repository is .scenarios/3; green in runs 40688967336 and 40688967168
SEC-T5-03 minor .scenarios/ and the data directories are not checked against symlinks partly fixed in the commit that adds this page: claim refuses a symlinked .scenarios/; the rest deferred
SEC-T5-04 minor No ownership check on the ID lock or on the spx-* labels: any local Docker user can act on another user's scenario deferred: recorded in dev-env §5 "Built in T5" and T5 Findings item 9
SEC-T5-05 minor --id 0 bypassed the explicit-ID path (same as CR-3) fixed with CR-3
SEC-T5-06 minor The kind API server was published on every interface fixed in the commit that adds this page: apiServerAddress: 127.0.0.1 next to apiServerPort
SEC-T5-07 minor The Kueue manifest went to a shared path under .scenarios/, checked and applied there, so parallel runs could race between check and apply fixed in the commit that adds this page: a mktemp file in the scenario's data directory, checked by SHA-256, the same file applied, then deleted
CR-8 blocker Round 2: a failed up ran down(n), which deletes by the host-global names spx-<n>; a second checkout whose claim returned an ID held by another checkout would tear that checkout's scenario down fixed: up first owns the network spx-<n> (docker network create refuses a duplicate name) and only then may clean up by name; before that, a failure only releases the claim; unit test
CR-9 minor Round 2: with sind only in the data directory, down cannot remove a SLURM realm, so a failed up leaks it fixed: up refuses before claim when the layer's sind or kind is not on the host PATH, and down's message names bash dev/bootstrap.sh
CR-10, SEC-T5-09 minor Round 2: a corrupt scenario.json (cluster-writable) made down crash before any teardown fixed: the parse is guarded, down removes whatever exists; unit test
CR-11 minor Round 2: "what it created was removed" was printed even when the cleanup failed fixed: up's cleanup prints its own outcome; main says "see the lines above"
CR-12 minor Round 2: two Down tests leaked run() lines into the test output fixed: sys.stderr patched
SEC-T5-08 minor Round 2: the admin kubeconfig and scenario.json sit in the directory the kind nodes mount read-write deferred: recorded in dev-env §5 "Not built" (a work/ subdirectory as the only read-write mount); the read-only repository mount guards against mistakes, not against a hostile hostPath pod on a privileged kind node
SEC-T5-10 minor Round 2: the Kueue manifest was checked and applied from the read-write data directory fixed: downloaded to the host's TMPDIR, removed on every path (a RETURN trap tried first broke pipelines-k8s kueue: bash keeps the trap after the function returns, and it fired later on an unset manifest, run 40690398142)
SEC-T5-11 minor Round 2: parallel.sh's cleanup trap ran down for IDs 1 to 4 even when this run had not created them fixed: the script refuses to start when any .scenarios/1..4 exists
SEC-T5-12 minor Round 2: the remaining symlink exposure (SEC-T5-03) was not in the owning spec fixed: dev-env §5 "Not built" names it
CR-13 major Round 3: parallel.sh's cleanup trap ran down for every ID after a failed up, so an up --id 3 refused because another checkout holds spx-3 still led to that checkout's scenario being torn down fixed in PR #43 (aaff4e0): the trap runs down only for IDs whose .scenarios/<id>/ this run still holds
CR-14 minor Round 3: down removed the network, the ID's ownership token, even after an earlier step failed, so another checkout could reserve the ID while this one still had a half-removed scenario fixed in PR #43 (aaff4e0): the network is released last and only when every other step succeeded; unit test
CR-15 minor Round 4: the header of parallel.sh still said the failure trap runs down for every ID fixed: the header names only the IDs whose .scenarios/<id>/ this run still holds
CR-16 minor Round 4: no test runs the CR-13 path (a released ID skipped by the trap); CI follows the success path only accepted: checked by reading the code only, recorded on the Progress page (finding 11)