Skip to content

v0.30.1

Latest

Choose a tag to compare

@github-actions github-actions released this 04 Nov 04:47
· 7 commits to main since this release
Immutable release. Only release title and notes can be modified.
v0.30.1
394a472

Upgrading to v0.30.1

This section is cumulative for the minor: it includes the changes of v0.30.0.

Behaviour changes

  • instruction-smuggling (invisible-characters-added, invisible-characters-in-description: a zero-width joiner) (looser): A U+200D inside an emoji ZWJ sequence is no longer reported: the character before it is an emoji base (a variation-sequence base, U+2600-U+27BF or U+1F300-U+1FAFF), an emoji base followed by U+FE0F, or a skin-tone modifier U+1F3FB-U+1F3FF, and the character after it is an emoji base. Before, every U+200D was reported, so a Dependabot body quoting upstream release notes with 🧔‍♂️ was instruction-smuggling/invisible-characters-in-description. A joiner between letters, at the start or end of a line, a second one in a row and one after a letter before an emoji are still reported (#1009) Migration: None
  • hook run --observe (after an edit), hook log (phase, run_id, tree_digest, check_pid), hook log (changed): In observe mode, the edit hook answers the agent at once and runs its check as a detached child: a started line when the hook returns, a finished line from the check. An OS lock on <git dir>/discipline/hook-check.lock keeps checks from overlapping. A check still waiting when a newer edit starts, or whose tree changed while it ran, logs verdict: "superseded" and no verdict. hook log marks a started line with no finished line as killed, timed out or still running. Before, the edit hook waited for the whole check, and said on stderr what would have blocked. The end of the turn and enforcing mode are unchanged and synchronous (#995) Migration: A reader of the observe log pairs started and finished lines by run_id, and reads an edit's verdict from the finished line. The would-be findings of an edit are no longer printed on the hook's stderr; the end of the turn still prints them.
  • hook run (after an edit), check under an edit hook, hook log (not_evaluated) (looser (an edit only)): Each gate declares a scope (file, change set, repository, provenance) and the paths that trigger it. The check an agent's edit hook runs evaluates file and change set gates, plus a repository or provenance gate whose trigger paths the change touches (a workflow file for ci-integrity and ci-skip-set, AGENTS.md / CLAUDE.md / GEMINI.md for agents-md). Every other gate is reported as not evaluated: in its notes (not evaluated on an edit), in a line of the agent prompt, and in the hook log's new not_evaluated list. It counts neither as passed nor as failed. Before, an edit ran every gate. The end of the turn, pre-commit, CI and any run in a CI job still run every gate (#995) Migration: None: the end-of-turn hook and CI still evaluate every gate. A reader of the observe log that counted per-edit findings of test-floor, dependency-delta or the provenance gates reads them from the stop lines.
  • check (new --timings, DISCIPLINE_TIMINGS, report timings), hook run (log duration_ms) (additive): check --timings or DISCIPLINE_TIMINGS=1 adds a timings object to the JSON report: wall time per gate and per shared phase (the diff extraction five gates share has its own entry), and deterministic counters of full extractions, bytes extracted and changed- and tracked-file listings. Absent unless asked for; unstable until 1.0; never part of a verdict, a baseline, a fingerprint or a replay cache key (replay removes the variable from each check it runs). Every change check a hook runs logs duration_ms, the wall time the hook waited for it, and hook log prints it (#995) Migration: None
  • action (new require_provenance and trusted_root inputs, install step), install.sh (stricter): On a mirror, another forge, or github.com without a token, the install step checks the release's Sigstore bundle (discipline.intoto.jsonl) with gh attestation verify --bundle for the same signer workflow and tag when gh is on the runner and the store serves the bundle: provenance=verified on success, a failed install when it does not verify, and a failed install when no release tag is resolved. The trusted root is Sigstore's, online, or the trusted_root file, never the mirror's. New input require_provenance (default false) fails the install before the binary runs when provenance stays unverified; binary_path is accepted. The --version probe of the binary runs without GH_TOKEN. An unverified install on github.com is a ::warning instead of a ::notice. install.sh: --gpg-key / DISCIPLINE_GPG_KEY, which could never succeed (releases publish no SHA256SUMS.asc), now fail with a message naming --verify-provenance, a new option that runs the same bundle check before installing, with --trusted-root (#986, #987) Migration: A mirror that serves discipline.intoto.jsonl to a runner with gh installs only a release whose bundle verifies, and needs a version input when the action ref does not resolve one. A mirror whose bundle cannot verify (no network and no trusted_root) stops serving the bundle or sets trusted_root. install.sh --gpg-key users switch to --verify-provenance.
  • check --format github-summary (job summary), init (starter file, test-runner detection) (none): The job summary puts the verdict, the counts and the findings first and the gate table last, folded in <details> with its on and off counts; the table and its planned-gates line were above the findings. init detects pytest from a tests/test_*.py file, and its starter file carries a commented # mode = "advisory" line under [meta]. The terminal and JSON reports are unchanged (#985) Migration: A workflow that parses the job summary reads the gate table inside <details>
  • config-integrity (a first discipline.toml) (stricter): A discipline.toml added where the base side has none is compared with the built-in defaults the base ran at: mode = "advisory", a disabled or demoted gate, a grown exempt_paths and every other loosening are reported as config-integrity/gate-weakened, advisory mode is not honoured for that run, and the gate runs although the new file switches it off. Before, nothing was compared and the advisory mode applied at once. A file that restates or tightens the defaults (discipline init) reports nothing. The policy floor still needs a base configuration file (#984) Migration: A change adding a first discipline.toml that loosens a default carries one allow-gate-weakening: <gate or meta> <reason> line for each loosened gate. A file that only tightens needs none.
  • action (download_url input, new provenance output) (stricter): The install step refuses a download_url that is cleartext http:// to anything but a loopback address (127.0.0.1, localhost, [::1]), and any scheme other than https://, file:// and that, as install.sh does; DISCIPLINE_INSTALL_ALLOW_INSECURE_HTTP=1 in the environment accepts one. Before, any URL was fetched. New output provenance: verified (the release workflow's build provenance attestation), unverified (SHA256SUMS of the release store only) or local (binary_path) (#978) Migration: A workflow installing from a cleartext mirror sets DISCIPLINE_INSTALL_ALLOW_INSECURE_HTTP: '1' in its env:, or moves the mirror to https.
  • doctor (workflows, codeowners fixes) (none): With no job running discipline, the fix names this release pinned to its commit (uses: orieg/discipline@<sha> # vX.Y.Z), which ci-integrity accepts; it named @v0, a tag ref ci-integrity reports. A missing CODEOWNERS is advised at the repository root, which every forge reads, instead of .github/CODEOWNERS (#972) Migration: None
  • Policy refusal policy/protective-gate-disarmed (exempt_paths of a protective gate) (stricter): Which paths an exempt_paths entry must not reach is derived from the tables the protective gates read, not a fixed list of nine: every agent-instruction file instruction-smuggling knows (GEMINI.md, .github/copilot-instructions.md, .opencode/, .github/instructions/, ...) and the base's instruction_files, every agent settings file and container definition sandbox-config reads, the base's ci-integrity workflows, the configuration and baseline files config-integrity compares, and the base's protected_paths. An entry naming one such file directly is refused too. Exempting .github/copilot-instructions.md from instruction-smuggling was accepted on one allow-gate-weakening: directive and is now refused (#967) Migration: A change that exempts one of these files from its protective gate on purpose: make it to the base branch's configuration in a change of its own, merged under the repository's review rules without the discipline check. A narrow entry such as tests/fixtures/** is unchanged.
  • check (report: directive_notes, terminal directives: line, job summary) (additive): When fail_on_overrides is set and the run's actor is in allowed_override_actors, the report notes that fail_on_overrides was not applied and how many overrides the change writes stand. Before, the bypass left nothing in the report. The login is not echoed (#974) Migration: None
  • pii (remediation of a fixed-format credential finding) (none): A credential match (the fixed-format token classes pii shares with shell-secrets) is told to revoke or rotate the credential, then remove it. Before, it got the placeholder advice of a home path or a host name (#968) Migration: None
  • hook install --upgrade (the discipline-hook-file: digest), Renovate preset (looser): The digest a generated hook file carries leaves out the release on its pin lines: the action pin of the Copilot setup workflow (uses: orieg/discipline@<ref> # vX.Y.Z) and the Claude Code bootstrap's version="vX.Y.Z". A pin Dependabot or Renovate moved is no longer reported as changed after hook install wrote it, and --upgrade rewrites the file without --force; any other change to those lines or to the file still is. A digest written by 0.29.0 and earlier, over the whole text, is still read. The Renovate preset moves the version of a bootstrap written without pinned digests (sums=release) and leaves a pinned one alone (#964) Migration: None. A file written by 0.29.0 or earlier counts a moved pin as an edit until hook install --upgrade rewrites it once.
  • check (gate notes: refused hidden directives) (none): A note about one directive takes its gate from the directive table where the directive is read, not from the words in the note. A refused hidden directive written in its namespaced form (the gate id in parentheses after allow) is noted on its own gate; before, it resolved to no gate and was noted on every gate a directive can lift. Other notes are placed as before (#983) Migration: None

The full ledger is in docs/ROADMAP.md.

What's Changed

  • fix(instruction-smuggling): a joiner inside an emoji ZWJ sequence is not invisible text by @orieg in #1011
  • chore(hooks): bump repository agent hooks, forge-lab image and docs SHA pins to v0.30.0 by @orieg in #1013
  • chore(release): bump version to 0.30.1-rc.1 by @orieg in #1014
  • chore(release): bump version to 0.30.1 with its ledger row by @orieg in #1015

Full Changelog: v0.30.0...v0.30.1