Skip to content

Code review · NAMES, the PyPI name-claim workflow and packages

Branch autopilot/pypi-name-claim, 2026-10-29. Two rounds. Round 1: code-reviewer, security-reviewer and doc-reviewer ran in parallel on 0ca035f (range 925ddb0..0ca035f); the coordinator accepted every finding, fixed in ce2f660, 1ad7e3e and 44da735. Round 2: the same three passes re-ran on 2433dc2; all three approve, with minor findings applied in 6268b76 and 46888e0. Round 3: security-reviewer on 46888e0 (PR #56) approves with three minors, all applied (below). No code-simplifier pass: the reviewer's simplify findings (CR-005, CR-006, CR-007) were applied by hand. The step's page is Progress · NAMES.

CODE  step=NAMES  review=approve b/M/m=0/0/4  security=approve b/M/m=0/0/3  rounds=3  simplify=CR-005,CR-006,CR-007 by hand  tests=pass
Pass Round 1 (HEAD 0ca035f) Round 2 (HEAD 2433dc2) Round 3 (HEAD 46888e0)
code-reviewer changes, b/M/m = 0/1/4 (one simplify) approve, b/M/m = 0/0/4 (two simplify): CR-006 to CR-009 not re-run
security-reviewer changes, b/M/m = 0/1/5 approve, b/M/m = 0/0/3: SEC-1b, SEC-7 and zizmor's pedantic "job has no name:"; plus a note on settings being None approve, b/M/m = 0/0/3: SEC-4b, SEC-7b, SEC-8
doc-reviewer changes, 10/14, six required changes approve, 12/14, three changes not re-run

Tests after round 3: the shell of all three jobs replayed locally for each project, with the build step's sums passed to publish (and bogus refused); a file changed, or a file added, in dist/ after the sums fails publish's check; smoke.py for each project, including the logger's refusals; a one-rule Snakefile with --logger spectio on snakemake 9.27.1; the negative checks (a wrong wheel list, a symlinked log file, a FIFO at the log path, and four mutants of the logger's checks) each fail as expected. Details on the Progress page.

Code review (CR-001 to CR-005)

Id Severity Finding Status
CR-001 major The logger's docs/intro.md is never read: the Snakemake catalogue reads docs/intro.md at the root of the Repository URL (snakemake-plugin-catalog collect_plugins.py:636-665 at d2c11a2), and three docs said the placeholder has it applied: the file is removed (ce2f660); the guide §3, dev-env §12, the FC-317 evidence and status say where the catalogue reads it; where it should live in this monorepo is open point 9 of the verification and a line in inbox #66
CR-002 minor The manual fallback ran twine on a dist/ that uv build never wrote applied: uv build --out-dir dist/<name>, then twine check and twine upload on dist/<name>/* (guide §2.5)
CR-003 minor twine unpinned in the build job applied (ce2f660): TWINE_VERSION=7.0.0 in dev/versions.env, read by the job
CR-004 minor The logger smoke test only imported the module applied (ce2f660): smoke.py builds a LogHandler with settings, emits one record and parses the line as JSON
CR-005 minor (simplify) The default file name was written twice applied (ce2f660): file: str = field(default="spectio-events.jsonl", …); Optional dropped

Security review (SEC-1 to SEC-6)

These IDs are local to this record (the security reviewer's numbering for this step), not the spec-wide SEC-1xx series.

Id Severity Finding Status
SEC-1 major The required-reviewer gate is not independent of admins or of the agents guide §2.2: admin bypass unticked on each pypi-* environment, no agent credential as reviewer or admin. Open, Thomas: the proposed .claude/settings.json deny rules for gh workflow run, gh api …/pending_deployments, gh api …/dispatches and gh run rerun are a permission change only Thomas may make; see SEC-1b below and inbox #71
SEC-2 minor A missing or misnamed environment publishes with no protection applied: publish runs only when github.ref == 'refs/heads/main' (ce2f660); the guide §2.2 quotes GitHub ("will create an environment … not have any protection rules") and has Thomas create and check the three environments before the first run
SEC-3 minor The published package sources are not code-owned, and main is unprotected applied: /tools/release/ @weber8thomas in .github/CODEOWNERS (ce2f660); branch protection on main with code-owner review is a prerequisite in the guide §2.2 (autopilot §8, inbox #8)
SEC-4 minor The smoke test ran third-party code in the job that produces the artifact applied, then simplified in round 2 (CR-006): build runs only checkout, uv and the pinned uv_build backend, then uploads dist/. Every checker (twine, the wheel-contents.txt diff, the smoke test) runs in smoke, on a downloaded copy that feeds nothing to publish. The round 1 sha256 hand-off is gone: the isolation holds without it, since an artifact name cannot be uploaded twice in a run and the job token is contents: read. Round 3 restored the sums for publish only (SEC-4b)
SEC-5 minor The logger followed symlinks and created its file with umask permissions applied (ce2f660): os.open(…, O_WRONLY|O_APPEND|O_CREAT|O_NOFOLLOW, 0o600); smoke.py checks that a symlinked path is refused and its target untouched; a real Snakemake run on a symlinked --logger-spectio-file stops with OSError: [Errno 40] and creates nothing
SEC-6 minor The manual fallback left an account-wide token where it can leak applied: the guide §2.5 enters the token only at twine's password prompt, never on the command line or in a file, uploads all three in one sitting and revokes the token at once

Doc review (1 to 6)

Id Finding Status
1 Framework :: Snakemake is not a trove classifier; warehouse forklift/metadata.py:239-242 refuses the upload applied (ce2f660), then met another way in round 2 (CR-007): the three packages carry no classifiers at all, so the inline checker and the trove-classifiers pin are gone
2 The fallback commands fail as written applied with CR-002
3 "The placeholder has both" is false for docs/intro.md applied with CR-001
4 A build-kind change needs a Progress page, a CODE line and this record applied: this record, Progress · NAMES, the PR body's Checks
5 The GitHub facts in §2.2 were unsourced, and the path to main unstated applied: three GitHub docs pages read 2026-10-29, quoted; the PR targets autopilot/integration, so the workflow reaches main through the promotion PR
6 "An approved claim holds the ID" is an inference applied: worded as an inference from plugin-registry.mdx:16 and gradle-plugin.mdx:109-111 in the guide, dev-env §12 and the decision log; refusal of a second claimant is not stated

Round 2 (HEAD 2433dc2)

Id From Severity Finding Status
CR-006 code minor (simplify) The sha256 hand-off guards against what a simpler job split already prevents applied: build is checkout, setup-uv, uv build --out-dir dist, upload; twine check --strict and the wheel-contents diff moved to smoke; the sums output, both sum checks, both file counts and the header comment dropped. security-reviewer agreed (SEC-4 above); round 3 brought the sums back for publish (SEC-4b)
CR-007 code minor (simplify) The inline classifier checker repeats PyPI's own upload check applied: classifiers dropped from the three pyproject.toml files; the checker and TROVE_CLASSIFIERS_VERSION removed (doc #1 above)
CR-008 code minor The uv version is written in five places with nothing to catch drift accepted as is: the workflow is a one-shot name claim; no new tooling. Each copy says = UV_VERSION in dev/versions.env
CR-009 code minor The inbox #66 status and the figure stated the Nextflow outcome as fact applied: the figure reads "approved: the ID should be held" and draws build → smoke → publish, both PNGs re-rendered and opened; inbox #66 says the claim should hold the ID, inferred (guide §1)
SEC-1b security minor The gate also fails if an agent session holds a token of the reviewer's account applied: guide §2.2 says no agent session holds a token of the reviewer's account with actions:write or the right to approve deployments. The deny rules for gh workflow run, gh api …pending_deployments, gh api …/dispatches and gh run rerun are open, Thomas: inbox #71, linked from guide §4 and the PR body
SEC-7 security minor O_NOFOLLOW does not stop a hard link, or a pre-existing file with loose permissions or another owner applied: after os.open, os.fstat; the handler refuses unless the file is regular, owned by os.getuid() and has st_nlink == 1, then os.fchmod(fd, 0o600). smoke.py covers a hard link (refused), another owner (refused; checked by patching os.getuid to return the real uid + 1, since a test cannot create a file owned by someone else without root) and a pre-existing 0666 file (tightened to 0600). Three mutants (no uid check, no link check, no fchmod) each make smoke.py fail
zizmor pedantic security minor (informational) The three jobs have no name: accepted as is: the job ids build, smoke and publish already name them; zizmor's default persona reports nothing
settings None security note Can settings be None? yes, from an API caller only: LogHandlerBase types it Optional[LogHandlerSettingsBase] (snakemake-interface-logger-plugins 2.1.0 base.py:19); the CLI always passes an instance (snakemake 9.27.1 cli.py:2085-2088, built from the dataclass defaults by snakemake-interface-common 1.23.0 plugin_registry/plugin.py:223-231). Handled: self.settings or LogHandlerSettings(); smoke.py builds LogHandler(None, None) and checks the default file is written
doc 1 docs change The figure and the inbox #66 status line applied with CR-009; the Progress page's alt text says what the figure shows
doc 2 docs change The Progress page lacks the §3.2 headings applied: "Figures and media" and "Findings and open questions"
doc 3 docs change The review state and verdicts are not recorded applied: Progress front matter review: approve, review_score: 12/14; the approve verdicts in the CODE line above, here and in the PR body's Checks

Round 3 (HEAD 46888e0, PR #56)

security-reviewer only: approve, b/M/m = 0/0/3.

Id Severity Finding Status
SEC-4b minor smoke runs third-party code (twine, the wheel's dependencies) while the run's artifact token is live, so it could replace the dist artifact before publish downloads it by name applied: the build step writes sha256sum dist/* to $GITHUB_OUTPUT as sums, in the same step as uv build and before the upload; the job exposes outputs: sums; publish gets it through env: SUMS and, between the download and the publish action, runs sha256sum --check --strict and compares the file count (dotfiles excluded) with the number of sums. build still runs no third-party checker. The guide §2.4 and the Progress page no longer say smoke "feeds nothing to publish"
SEC-7b minor A FIFO planted at the log path blocks os.open(O_WRONLY) until a reader appears, so the run hangs applied: os.O_NONBLOCK added to the open flags; with no reader the open fails with ENXIO, with one the S_ISREG check refuses it. smoke.py plants a FIFO with os.mkfifo and asserts the handler is refused, under a 5 s SIGALRM guard; with O_NONBLOCK removed from the installed module, smoke.py fails after 5 s with "opening a FIFO log file blocked"
SEC-8 minor setup-uv downloads uv with no checksum it checks: v6.8.0's built-in list stops at uv 0.8.20 (src/download/checksum/known-checksums.ts at d0cc045) applied: checksum: aab924fd…a4b967 in both jobs, commented = UV_SHA256 in dev/versions.env. That value is the sha256 the uv 0.11.32 release publishes for uv-x86_64-unknown-linux-gnu.tar.gz (uv-x86_64-unknown-linux-gnu.tar.gz.sha256, read 2026-10-29). setup-uv v6.8.0 declares the checksum input (action.yml:21-23) and, when given, checks the downloaded archive against it (src/download/checksum/checksum.ts, validateChecksum)