Repository navigation
v0.22.0
·
9 commits
to main
since this release
Immutable
release. Only release title and notes can be modified.
Upgrading to v0.22.0
Default changes
stub-bodies:equality-made-constanton a changed production method atwarningat most (exit 0) → at the gate's severity (errorby default, exit 1) (stricter). Restores v0.19.0 blocking for a substantive equality body made constant Restore:allow-stub: <Type> <reason>, or[gates.stub-bodies] severity = "warning"(whole gate).citation-anchors: no minimum quote length →min_quote_chars = 8(stricter). A one-character quote is contained in almost any line and verified anything Restore:min_quote_chars = 0.
Behaviour changes
- library
discipline::guards::perf::bounds::sample_size_for_interval(stricter): Returns an error when the required n is not finite or exceeds the newMAX_SAMPLE_SIZE(1e9) instead of saturating tousize::MAX; adds pub constMAX_SAMPLE_SIZEMigration: Callers passing a vanishing half-width handle theErr; values at or below 1e9 are unchanged gate-command-lint/unfailable-command(stricter): Read from the bash syntax tree: also reports|| echo …,|| /bin/true,||true,|| exit 00,|| { true; },(cmd || true)and|| printf 'lit';|| trueinside a string or comment, ana || bwhose left side only tests a condition, and an|| echodefault inside$(...), are not reported; a command that does not parse is named in the notes Migration: Remove the mask, or end the fallback with a failing command (|| { echo failed; exit 1; })gate-command-lint/pipeline-exit-masked(stricter): Any pipeline with a failable stage before the last is reported withoutpipefail, whatever the filter (| xargs,| python3);pipefailcounts only from a realsetcommand, not text such asecho "set -o pipefail"Migration: Addset -o pipefail;before the pipeline, or setallow_pipeline_without_pipefail = trueci-integrity/exit-code-masked(GitHubrun:, GitLab script) (stricter): Read from the bash syntax tree on both forges: also reports||true,|| exit 0,|| echo …,|| /bin/true,|| { true; },set +o errexit; a mask only mentioned in a comment or string, ana || bwhose left side only tests a condition ([ -f x ] || echo missing, a function made of conditions), or an|| echodefault inside$(...), is not reported; the GitHub finding is reported at the masking line; a changed script that does not parse, or a non-bash/shshell:, is named in the notes Migration: Remove the mask or excuse it withallow-ci-weakening: <step name> <reason>(GitLab subject unchanged:|| true)stub-bodies/equality-made-constant(stricter): An existing production equality method whose substantive body is replaced by constanttrueis reported at the gate's severity (it was capped at warning since v0.20.0; v0.19.0 reported it asbody-replaced-by-stubat gate severity). Added equality methods, ones already constant on the base side, and test code stay capped at warning Migration: Implement a real comparison, orallow-stub: <Type> <reason>stub-bodies/equality-made-constantcarve-outs (stricter (looser for test-double modules)): Read from syntax nodes: a comment naming a derive, a mock import or@Generatedno longer exempts; a hand-written equality method in a@dataclass, attrs or pydantic class, a Lombok or record class, or a Kotlin/Scala data/case class is reported; test doubles infakes/mocks/stubs/doublesmodules, and intests/files judged as production code, are exempt Migration:allow-stub: <Type> <reason>for a deliberate constant equalitystub-bodiesconstant bodies (stricter):return Boolean.TRUE;and parenthesised constants (return (true);,return (null)) are trivial bodies, for bothequality-made-constantandbody-replaced-by-stubMigration: nonesuppression-deltareport text (quote_line = false) (stricter): The message and remediation name a fixed tool and the rule identifiers only; descriptions, justifications,reason =,msg=and other free text are no longer echoed Migration: none;quote_line = truestill quotes the linesuppression-deltaJavaScript sites (stricter):v8 ignore,node:coverage ignoreandnode:coverage disablecomments are suppression sites when they open the comment Migration:allow-suppression: coverage <reason>or a pathunsafe-budget(stricter): A changed file whose head side does not parse stops the run with exit 2 (could not check) instead of a note and a skip, restoring the behaviour before v0.20.1 Migration: Fix the file, or exempt the pathconfig-integrity(stricter): Under--disable <gate>, a change whose own file leaves the gate off, including by deleting anenabled = truetable so a default-off gate falls back to off, is reported asgate-weakened; a head config read or parse error stops the gate instead of skipping Migration:allow-gate-weakening: <gate> <reason>mechanism-sections(bug fix): An untagged claim whose 57th byte falls inside a multi-byte character is reported instead of crashing the run (exit 101) Migration: nonemechanism-sections(looser (false positive removed)):~~~fenced blocks are code, not claims Migration: nonemechanism-sections(stricter): A heading that starts with a designated heading (Root cause (summary),2. Root cause) is in scope;#wordwith no blank is no longer a heading Migration: Tag the claims, or rename the headingmechanism-sections(stricter): A(verified: <path>)target must be tracked in the index, not only present on disk Migration:git addthe cited filecitation-anchors(stricter): A quote with fewer thanmin_quote_chars(default 8) non-whitespace characters is reported as the newcitation-anchors/quote-too-shortMigration: Quote more of the line, or setmin_quote_charscitation-anchors(stricter): A cited commit must be the base or one of its ancestors; a commit only the change or another branch holds iscommit-unresolvableMigration: Cite a commit already on the base branchcitation-anchors(tracker titles) (bug fix):DISCIPLINE_NO_NETWORK=0orfalseno longer skips the check, matching the forge client; atracker-title-mismatchat the start of a line reports the correct line (it was one short) Migration: Baseline entries keyed on the old line:discipline baseline --writescope-confinement(stricter): A rename is judged at its source as well as its destination; symlinks added, retargeted or removed under a forbidden path (or outsideallowed_paths) are reported Migration:allow-scope: <path> <reason>claim-labels(stricter): A label needs a separator and real content;(RUN),[READ],`RUN`,**RUN**,RUN:,-- RUN,(RUN: x)and markers inside code spans no longer satisfy a claim Migration: WriteRUN: <command>or(READ: path:line)hook run --event post-tool([hooks.verified-stop]witnessed runs) (bug fix (stricter)): A run counts as passing only when its exit code is stated: a numeric field, anExit code: Nstatement (Codextool_responsestring, QwenllmContent, Copilot<exited with exit code N>; any non-zero statement wins), or a Claude CodePostToolUsethat was not interrupted. A payload with a result but no stated code was read as exit 0 and is no longer witnessed; CopilotresultType: "success"alone no longer counts Migration: none; a passing run in an unrecognised shape now needsrun_at_stop = truehook run([hooks.verified-stop], refuse mode) (bug fix (stricter)): A stop whose refusal cannot be counted againstmax_per_session(no session id, an unwritable session store, orsessions/.lockheld) is refused (exit 2) with the reason on stderr instead of being let through;stop_hook_activeand agyexecutionNumstill let it through Migration: nonehook run([hooks.verified-stop]default commands) (bug fix (looser for single-ecosystem repositories)): With no command configured, only the default runners whose ecosystem marker is a file at the base tree's root are vouched (cargo test/Cargo.toml,pytest/pyproject.toml,setup.pyorpytest.ini,npm test/package.json,go test ./.../go.mod); before, all four were vouched andrun_at_stoprequired all four to pass. With none applicable, the refusal says so Migration: Set[hooks.verified-stop].commandson the base branch when no marker applieshook install(all agents but Cursor and Aider) (additive): Generated hook files runhook run --agent <name> --event post-toolafter the agent's shell tool (Bash,run_shell_command,bash,run_command, OpenCodebash/shell), so[hooks.verified-stop]can witness verification runs from installed hooks; it never blocks Migration:discipline hook install --agent <name> [--observe] --upgrade- session store (
<common git dir>/discipline/sessions/) (bug fix): Session files and the lock file are created0600on Unix (were0644under the usual umask) Migration: none hook install --agent opencode(2.xexecute.after) (bug fix): In enforcing mode, a failed check whosecall.resulthas neither text content nor a stringoutputis written to stderr instead of being dropped Migration:discipline hook install --agent opencode --upgrade- release tarballs (bug fix): Archives record owner and group 0 with no names, directories and files at 0755/0644 and the binary at 0755, on both GNU tar and bsdtar runners Migration: none
The full ledger is in docs/ROADMAP.md.
What's Changed
- fix: close the v0.19.0..v0.21.2 audit findings by @orieg in #768
- chore(release): bump version to 0.22.0 with its ledger rows by @orieg in #770
Full Changelog: v0.21.2...v0.22.0