Release 0.9.1 - #149
Merged
Merged
Release 0.9.1#149
Conversation
Version 0.9.1 in pyproject.toml, CITATION.cff, action.yml's default and the docs/installation.md example (tests/test_release_surfaces.py holds the last two to pyproject). The changelog's Unreleased section becomes the 0.9.1 heading, so the release workflow's first-section extraction yields these notes; brand assets regenerated for the version they print. Everything else on main since 0.9.0 is what this release ships: #148. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: Jothi Mani Rajendran <250249270+jothimani-rajendran@users.noreply.github.com>
…r a local path or commit id shutil.copytree dereferenced symlinks, so a catalog pack carrying leak -> ~/.ssh/id_rsa installed the adopter's private key as a regular file; a manifest whose id differed from its folder name became two policies (lock and compiled tree under one name, config under the other); a commit id failed against git clone --branch, and --ref was silently ignored against a local path; a path-like artifact id tracebacked instead of erroring cleanly. add() now refuses a pack carrying a symlink or a foreign manifest id before hashing, routes a local path plus --ref through git so the ref is honoured, falls back to git init/fetch/checkout FETCH_HEAD when a ref cannot be branch-cloned, and main() catches ValueError so a bad id is a clean error. Signed-off-by: Jothi Mani Rajendran <250249270+jothimani-rajendran@users.noreply.github.com>
…cannot be read An empty or missing chock.lock over installed packs verified clean -- nothing was compared -- and a pack nested under .agents/policies/<group>/ was compiled but never locked, since build_lock() read one level while discover_policy_dirs() reads every level; a lock that was not JSON tracebacked instead of failing. read_lock() now raises LockError on invalid JSON or shape, which verify_lock() reports as a failure; build_lock() iterates discover_policy_dirs() and records a nested pack's path; verify_lock() flags every discovered pack absent from the lock and resolves a pack from its recorded path or the top-level default. Signed-off-by: Jothi Mani Rajendran <250249270+jothimani-rajendran@users.noreply.github.com>
…g on a bad ref or YAML
{git-hook, ci-gate} -> {git-hook, ambient-rule} kept the same surface count and passed a
strict-subset comparison while losing a real gate; a base ref that did not resolve read
as "no config there", which means every policy enabled, so an unfetched base silently
passed; a base config that was not valid YAML tracebacked.
weakenings() now flags a head whose surfaces are not a superset of the base's, not just
a smaller set; config_at() resolves the ref first and raises BaselineError when it
cannot, and YAML errors raise the same; check_baseline() reports either as an error
finding ending "nothing was compared".
Signed-off-by: Jothi Mani Rajendran <250249270+jothimani-rajendran@users.noreply.github.com>
… and name the mistake at validate policies: null, overrides: null, a null or string override, and disabled: scan-secrets (iterated as letters) each crashed the resolver or misread the config as something other than what it says; a mandatory policy listed in policies.disabled was skipped by sync, even though POL-1 says the config cannot disable it. A _policies() normaliser treats a missing or malformed policies/overrides block as empty, overrides keep only mapping values, disabled and surfaces accept a bare string as one id, and policy_status() applies disabled only when the policy is not mandatory. validate's check_policy_toggles() is split into a _toggles() helper (complexity) and reports a non-mapping policies, non-list disabled, non-mapping override, and an unknown or malformed surfaces value as named errors instead of crashing -- including on a non-mapping override for a block-severity security guard, which the shape check used to report before an unguarded second lookup crashed on the same value. Signed-off-by: Jothi Mani Rajendran <250249270+jothimani-rajendran@users.noreply.github.com>
https:\\evil.io, https:/evil.io and https:///evil.io yielded no host and passed; example.com\@evil.io is one host to a browser and another to curl; evil.io%00.example.com matched the allowlist's suffix once the null byte was ignored. _AUTHORITY_RE gains a (?:https?|wss?|ftp):/+ alternative so a run of slashes after the scheme still matches, and the authority exclusion class no longer excludes a backslash; _hosts_in() scans the raw text, the percent-decoded text, and both with backslashes turned to slashes, so every reading a client could take is checked. _ascii_host() punycodes a non-ASCII host and refuses anything outside the DNS/IP character set as an undecidable host, which is a block, not a match. Signed-off-by: Jothi Mani Rajendran <250249270+jothimani-rajendran@users.noreply.github.com>
content_pattern: "(" passed schema validation and sync, then every commit it guarded
died in a re.error traceback instead of being refused up front.
After schema validation, every string param whose name ends _pattern, _regex or _pragma
is compiled; a re.error becomes a named validate finding instead of a runtime crash.
Signed-off-by: Jothi Mani Rajendran <250249270+jothimani-rajendran@users.noreply.github.com>
… chock knows chock init . --agents claude followed by sync wrote ten vendors' hook files and runtimes (.cursor/, .codex/, .windsurf/, .devin/, ...), and the SessionStart arm hook installed whether or not claude was named. wired_vendors(agents) filters WIRED_VENDORS through CHOCK_AGENT for the requested agents; the witness and the install loop both use it, and the SessionStart hook installs only when claude_code is among the wired vendors. docs/cli-reference.md's sync paragraph is rewrapped to say so within its existing line count. Signed-off-by: Jothi Mani Rajendran <250249270+jothimani-rajendran@users.noreply.github.com>
…ad's Required checks, the check registry, attestation_floor and unattestable_paths were all read from the pull request's own .chock/config.yaml, so the change being judged chose what it was judged on; and a recorded failure passed require when no required set existed at all. New src/chock/review/policy.py (evidence.py was at the 300-line budget) holds BUILTIN_CHECKS, DEFAULT_UNATTESTABLE and the policy readers. review_policies(root, base_ref) returns the base's and the head's policy, base read via `git show <base>:.chock/config.yaml`; required_checks and unattestable_paths are the union across both, attestation_floor the higher, and check_registry keeps the base's argv for a check both define. base_ref is threaded through build, verify, require, command_set_hash and review/cli.py's emit/verify/require. require() refuses any recorded fail regardless of whether a required set exists, keeping the "required check(s) recorded failing" wording when every failing check is required. _touched_unattestable_paths() also now threads base_ref into unattestable_paths(): it previously read only the head's list, so a head could touch a path the base protects and drop it from its own unattestable_paths to skip the attestation floor entirely, even though attestation_floor() and unattestable_paths() themselves already read the stricter union -- the floor was right, but nothing was ever found to apply it to. docs/reviewer-evidence.md's judgement table (row 4: "any check ... required or not") and a new paragraph cover the base/head union. Signed-off-by: Jothi Mani Rajendran <250249270+jothimani-rajendran@users.noreply.github.com>
spec/policy-spec.md's POL-1 bullet now says sync compiles a mandatory policy in full whatever policies.disabled says; spec/enforcement-matrix.md's POL-1 row notes that policy_status() ignores the entry. CHANGELOG.md's 0.9.1 section gets one bullet per finding: chock add's symlink/foreign-id/--ref fixes, verify attesting every compiled pack, the baseline check comparing reach instead of size, config shape normalisation with POL-1 held in sync, egress_allowlist's URL reading, gate regex validation at validate, sync wiring only supported_agents, and review require judging by the stricter of base and head. Signed-off-by: Jothi Mani Rajendran <250249270+jothimani-rajendran@users.noreply.github.com>
jothimani-rajendran
marked this pull request as ready for review
September 20, 2026 23:23
jothimani-rajendran
added a commit
that referenced
this pull request
Sep 20, 2026
This was referenced Sep 21, 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.
What
The release-prep PR for 0.9.1, one commit. Everything the release ships is already on
mainin #148; this moves the version and cuts the notes.pyproject.toml,CITATION.cff,action.yml'sversiondefault and thedocs/installation.mdexample → 0.9.1.tests/test_release_surfaces.pyholds the last two topyproject.toml, so a bump that forgot either fails the suite.CHANGELOG.md:## Unreleasedbecomes## 0.9.1 — …with no empty Unreleased heading above it, becauserelease.ymlextracts the first##section as the release notes.docs/assets/social-preview.{svg,png}regenerated: they print the version.Why a patch and not 0.10.0. No emitted byte changes: the runtime goldens are untouched and every adopter's
chock sync --checkstays clean. The two installer fixes (a policy-less repo keeps the runtime its arm hook runs; the runtime is written LF on Windows) make the install match what it already claimed. Thebaselinecheck, the gate-log ignore rule and thepin-github-actionsdogfood are additive.After the tag. The tag runs the full CI matrix, Windows and macOS included.
v0.9.0's run was red on all three Windowsvalidatejobs (run 35519645109); the fixes in #148 were made from that job log without a Windows machine, so thev0.9.1tag run is their confirmation. Check it. Then chock-quickstart#8 can merge.Round-2 review fixes
Twelve findings from the pre-0.9.1 deep review, each in its own commit:
chock addrefuses a catalog pack carrying a symlink before hashing it (critical).chock addrefuses a manifest whoseiddiffers from its folder name.chock add --refhonours a commit id and a local checkout; a path-like artifact id is a clean error, not a traceback.chock check --only verifyattests every packsynccompiles, nested packs included, and fails cleanly on a lockfile that is not valid JSON (high).policies: null, a bare string indisabled, a null or string override,surfaces: 5) are normalised instead of crashing or misreading, and POL-1 holds insynctoo.validatenames the exact config-shape mistake instead of crashing.egress_allowlistreads a URL the way a fetch library will, closing several host-spelling bypasses (medium).validaterejects a gate regex that fails to compile, instead of tracebacking at the first commit it guards (medium).syncwires in-agent hooks only forsupported_agents, not every vendor chock knows (medium).chock review requirejudges a pull request by the stricter of the base's review policy and the head's, so the change being judged cannot choose what it is judged on (critical).spec/policy-spec.md,spec/enforcement-matrix.mdandCHANGELOG.mdrecord all of the above.Verification
Not in this PR
The tag.
release.ymlpublishes to PyPI on av*tag whose version matchespyproject.toml— your click after merge.Definition of done
chock check→ 0 errors, 0 warnings; the two[INFO]SEC-4 lines oninjection-defense/evals/suite.yamlpredate this PRchock check --only matrixpasses; no behaviour changechock sync --repo . --checkcleanchock check --only verifycleanpytest -qgreenpytest acceptance/greenruff check .andruff format --check .cleanClaims
🤖 Generated with Claude Code