perf(release-management): sibling scripts for the deterministic steps - #1512
Merged
Merged
Conversation
potiuk
added this pull request to stack #1513
October 4, 2026 23:06
Seven stdlib scripts take over steps the model computed by hand: vote-tally's binding resolution and pass rule (reply text never reaches the count), audit-report's record renderer and schema check, archive-sweep's retention, keys-sync's key-strength and URL check, and prepare's previous tag, Category-X scan and next dev version. Classifying each vote, retention summaries, and every command the release manager runs stay with the model. Rules settled by the maintainer: an already-expired key blocks keys-sync (expiring within 90 days stays advisory; secp256k1 refused); only the configured version file counts as Python-style for the next dev version. The affected eval cases embed the scripts' real output. The audit-report injection case now carries the injection source, so the judge, which sees only the output, can verify it. Tests: every script has a stdlib unittest suite under the skill's tests/ directory. The vote-counting and key-check suites cover the pass-rule boundaries, the veto override, GitHub handles never being binding, reply text never reaching the output, the strength floor and accepted curves, expired keys, fingerprint normalisation, and each script's command-line errors. Nothing ran the plugins/*/skills/*/tests suites before; a new skill-script-tests pre-commit hook (tools/dev/run-skill-script-tests.sh) now runs all of them. Generated-by: Claude Opus 5
…he scripts - keys-sync check_key.py read only strength and expiry, so a revoked, invalid or disabled key passed. gpg's validity field now blocks it. - vote-tally tally.py counted every vote, so one member voting twice, or writing from two addresses, was counted twice. Votes are grouped by person (roster Apache ID, else address); any repeat halts the tally, even under --force-close, until the RM says which vote stands. - archive-sweep retention.py let a pre-release in the release area count as a train's latest, which kept the RC and proposed archiving the real latest release. Pre-releases are now listed for the RM and never decide retention. - audit-report render_record.py rendered planning-issue values into the record unescaped, so a line break or `|` could break the tables or add sections, and a link field could carry any target. Values are confined to their cell or code span, and link fields accept only a plain https:// URL. Each fix has tests; the four skills describe the new output. Generated-by: Claude Opus 5
A follow-up security review found the earlier sanitisation incomplete: it only flattened line breaks and escaped `|`, so planning-issue text could still render as links, images (a tracking pixel), HTML tags, headings or @-mentions in the audit record. Every free-text value (product name, version, redaction reasons, injection sources, the planning-issue reference) now has each markdown and HTML character backslash-escaped. binding_voters accepts only GitHub-login-shaped handles, so a team mention such as @apache/committers is refused. Generated-by: Claude Opus 5
The maintainer decided that a changed vote should settle itself: when one person votes more than once, or a member writes from two addresses, only their latest vote counts (newest date, else the later one in the thread). The earlier votes are listed in superseded_votes for the tally to name. A clear later vote replaces an earlier ambiguous one; an ambiguous latest vote still halts. This replaces the earlier halt on any repeated vote. Also lints the sibling scripts to the workspace rules: the plugins workspace member runs ruff, ruff format and mypy over the skills' scripts and tests too. Generated-by: Claude Opus 5
…keep identities apart From a security review of the latest-vote rule: - "Latest" was the newest Date, but the sender writes their own Date header, so a message with a forged future date always won. The latest vote is now the later one in thread order, the order the list archive received the votes. A vote whose date runs backwards against that order is reported in date_order_mismatches for the RM. - A non-roster sender written as a bare "alice" shared an identity with the roster member whose Apache ID is "alice", so a non-binding vote could replace a binding one. Identities are namespaced (roster:<id>, address:<addr>). Generated-by: Claude Opus 5
potiuk
force-pushed
the
perf/release-sibling-scripts
branch
from
October 4, 2026 23:38
2aabf5e to
a869eaa
Compare
This was referenced Oct 5, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Third of three stacked PRs (#1345); based on the
release-verifyPR below it.Summary
Seven stdlib sibling scripts take over steps the model computed by hand:
vote-tally: binding resolution and the pass rule;audit-report: the record renderer and schema check;archive-sweep: retention;keys-sync: the key-strength and URL check;prepare: the previous tag, the Category-X scan and the next dev version.Classifying each vote, retention summaries, and every command the release manager runs stay with the model.
Rules settled by the maintainer:
keys-sync(expiring within 90 days stays advisory);Date, and superseded votes are listed.From reviews of the scripts:
Tests now run: each script has a unittest suite. Nothing ran the
plugins/*/skills/*/testssuites before; a newskill-script-testspre-commit hook now runs all of them.Test plan
skill-script-testshook: all eight skill test directories passvote-tally,audit-report,archive-sweep,keys-syncandprepareevals. Everything passes except keys-sync step-0 case-3, which fails onmaintoo.🤖 Generated with Claude Code