Repository navigation
·
7 commits
to main
since this release
Immutable
release. Only release title and notes can be modified.
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🧔♂️wasinstruction-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: Nonehook 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: astartedline when the hook returns, afinishedline from the check. An OS lock on<git dir>/discipline/hook-check.lockkeeps checks from overlapping. A check still waiting when a newer edit starts, or whose tree changed while it ran, logsverdict: "superseded"and no verdict.hook logmarks astartedline with nofinishedline 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 pairsstartedandfinishedlines byrun_id, and reads an edit's verdict from thefinishedline. 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),checkunder 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 evaluatesfileandchange setgates, plus arepositoryorprovenancegate whose trigger paths the change touches (a workflow file forci-integrityandci-skip-set,AGENTS.md/CLAUDE.md/GEMINI.mdforagents-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 newnot_evaluatedlist. 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 oftest-floor,dependency-deltaor the provenance gates reads them from thestoplines.check(new--timings,DISCIPLINE_TIMINGS, reporttimings),hook run(logduration_ms) (additive):check --timingsorDISCIPLINE_TIMINGS=1adds atimingsobject 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 (replayremoves the variable from each check it runs). Every change check a hook runs logsduration_ms, the wall time the hook waited for it, andhook logprints it (#995) Migration: None- action (new
require_provenanceandtrusted_rootinputs, 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) withgh attestation verify --bundlefor the same signer workflow and tag whenghis on the runner and the store serves the bundle:provenance=verifiedon 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 thetrusted_rootfile, never the mirror's. New inputrequire_provenance(defaultfalse) fails the install before the binary runs when provenance staysunverified;binary_pathis accepted. The--versionprobe of the binary runs withoutGH_TOKEN. An unverified install on github.com is a::warninginstead of a::notice.install.sh:--gpg-key/DISCIPLINE_GPG_KEY, which could never succeed (releases publish noSHA256SUMS.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 servesdiscipline.intoto.jsonlto a runner withghinstalls only a release whose bundle verifies, and needs aversioninput when the action ref does not resolve one. A mirror whose bundle cannot verify (no network and notrusted_root) stops serving the bundle or setstrusted_root.install.sh --gpg-keyusers 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.initdetects pytest from atests/test_*.pyfile, 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 firstdiscipline.toml) (stricter): Adiscipline.tomladded 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 grownexempt_pathsand every other loosening are reported asconfig-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 firstdiscipline.tomlthat loosens a default carries oneallow-gate-weakening: <gate or meta> <reason>line for each loosened gate. A file that only tightens needs none.- action (
download_urlinput, newprovenanceoutput) (stricter): The install step refuses adownload_urlthat is cleartexthttp://to anything but a loopback address (127.0.0.1,localhost,[::1]), and any scheme other thanhttps://,file://and that, asinstall.shdoes;DISCIPLINE_INSTALL_ALLOW_INSECURE_HTTP=1in the environment accepts one. Before, any URL was fetched. New outputprovenance:verified(the release workflow's build provenance attestation),unverified(SHA256SUMSof the release store only) orlocal(binary_path) (#978) Migration: A workflow installing from a cleartext mirror setsDISCIPLINE_INSTALL_ALLOW_INSECURE_HTTP: '1'in itsenv:, or moves the mirror to https. doctor(workflows,codeownersfixes) (none): With no job running discipline, the fix names this release pinned to its commit (uses: orieg/discipline@<sha> # vX.Y.Z), whichci-integrityaccepts; it named@v0, a tag refci-integrityreports. 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_pathsof a protective gate) (stricter): Which paths anexempt_pathsentry must not reach is derived from the tables the protective gates read, not a fixed list of nine: every agent-instruction fileinstruction-smugglingknows (GEMINI.md,.github/copilot-instructions.md,.opencode/,.github/instructions/, ...) and the base'sinstruction_files, every agent settings file and container definitionsandbox-configreads, the base'sci-integrityworkflows, the configuration and baseline filesconfig-integritycompares, and the base'sprotected_paths. An entry naming one such file directly is refused too. Exempting.github/copilot-instructions.mdfrominstruction-smugglingwas accepted on oneallow-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 astests/fixtures/**is unchanged. check(report:directive_notes, terminaldirectives:line, job summary) (additive): Whenfail_on_overridesis set and the run's actor is inallowed_override_actors, the report notes thatfail_on_overrideswas 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: Nonepii(remediation of a fixed-format credential finding) (none): A credential match (the fixed-format token classespiishares withshell-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: Nonehook install --upgrade(thediscipline-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'sversion="vX.Y.Z". A pin Dependabot or Renovate moved is no longer reported as changed afterhook installwrote it, and--upgraderewrites 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 untilhook install --upgraderewrites it once.check(gatenotes: 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 afterallow) 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