Repository navigation
Conversation
Moves the section-name lookup key (strip, then case-fold) into `spec/fidelity_text.py` as `section_key()` and points every section-name site at it: `section_span` and `strip_sections`, `scripts/build_dist.py` `heading_body`, `scripts/carry.py` `fold()`, `spec/audit.py`, `spec/audit_markdown.py` (including `readme_section_findings`), and `scripts/canonical_review.py`. A future change to heading-key normalization now reaches every lookup path in those files. No behavior change: the existing tests are the proof, plus one new test of the helper. OPERATIONS.md's primitives entry names the new function. Closes on promotion: #3686 Remaining findings: - #3706: `spec/validate.py` compares files.json section names to H2 text exactly, which disagrees with `section_key` (class: pre-existing, filed). - #3710: `section_key` lowercases rather than casefolds (raised by Copilot; class: pre-existing, filed). 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
…te Guard (#3709) ## Summary #3695 asked whether the write guard's requirement 4 reads a push's remote in the repository a `--git-dir` names. It did not. A check with two constructed checkouts, `a` with origin `github.com/alpha/x` and `b` with origin `github.com/beta/y`, run from `a`, showed the guard querying `alpha/x` for each of these: - `git --git-dir=<b>/.git push origin :feature/x`, and the same with a separate `--git-dir` value - `git --git-dir=<b>/.git push origin develop`, where `beta/y` required a pull request on `develop` and the guard allowed the push - `GIT_DIR=<b>/.git git push ...`, `env GIT_DIR=... git push ...`, and `export GIT_DIR=...; git push ...` `-C <b>` and `-C <b> --git-dir=.git` already read `beta/y`, and `--work-tree=<b>` reads `alpha/x`, as git itself does. - `_push_repo` now reads the remote's push URL in the repository git uses: the one a `--git-dir`, or else a `GIT_DIR` the push itself carries (as a prefix or through `env`), names, resolved from the directory after `-C`. - A `GIT_DIR` set earlier in the text reads as undetermined, since whether git sees it turns on an export or an `unset` the reading does not track. - A `-c` or `--config-env` override of a `remote.` or `url.` key, a `GIT_CONFIG` variable in the text, and a `--git-dir` the reading cannot resolve each leave the repository undetermined. That fails closed for `main`, `master`, and `develop`, as the spec already states for an unresolvable origin. - A push naming no refspec now reads its branch in the repository a `--git-dir` or the push's own `GIT_DIR` names, an absolute `--git-dir` even after an unresolved `cd`. It was left unjudged before, so such a push into a `develop` checkout passed. One whose repository stays undetermined is still allowed, as the README now states. - A URL remote now resolves even where the working directory does not. - The self-test gains a live check over two real temporary checkouts, one for each shape's repository and one for each bare push's branch. - `host-setup/agent-safety/README.md` requirement 4 states which repository the remote is read in. ## Verification - `python3 host-setup/agent-safety/claude/gh-write-guard.py --selftest`: SELFTEST PASS, each new repository and branch case `ok`. - Local strict review: one pass raised three findings (the bare-push branch, README wording, a self-test case that also passed on the old code). Each is fixed in the second commit. Copilot's round then raised two (an earlier, unexported `GIT_DIR` trusted, and an absolute `--git-dir` after an unresolved `cd`), fixed in the third. The last local pass, over the full diff at the head, raised none. - ruff format and check, mypy, `tests/test_guard_size.py`, and the prose gate on the diff: clean. Closes on promotion: #3695 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…ishes (#3707) When PyYAML is not importable, the repo gate's `composite-actions` check resolves an interpreter through `uvx --with pyyaml python` and then runs its reader on that interpreter. Under `UV_NO_CACHE` or uv's `no-cache = true`, uv deletes that environment when uvx exits, so the launch failed with a bare "No such file or directory: '<temp path>'". Design: name the cause and the remedy in the existing single host finding, rather than falling back to running the reader inside `uvx` per file. The naming is a few lines and adds no second read path. A per-file fallback would add a path that re-resolves the environment for every action file and is only reachable on this host shape. The finding now reads: "the interpreter uvx provided could not start: [Errno 2] ...: '/gone', because uv deletes the environment when uvx exits under UV_NO_CACHE or uv's no-cache = true (unset UV_NO_CACHE, turn off uv's no-cache setting, or make PyYAML importable), so no run body was shellchecked". Only `FileNotFoundError` gets the explanation (it is also what Windows reports as WinError 2); other start failures keep the plain message. The `load_action` docstring notes the exception. `scripts/repo_gate.py` is a runpy wrapper, so only the action file changed. Tests pin the full rendered sentence with a stubbed `subprocess.run`, and a PermissionError case that must not blame the uv cache. Closes on promotion: #2358 Remaining findings: - #3705: `scripts/README.md` lines 3 and 127 still promise that `uvx` alone suffices; held by open PR #3704, filed for follow-up. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
…alar in the Prose Gate (#3712) ## Summary Two fixes to the prose gate, both in `.github/actions/prose-gate/prose_lint.py`. - **#3684, a moved file's unread source.** In diff mode, `changed_lines` read a renamed file's destination in full only when its source sat under a generated tree or matched an `--exclude`. It now also does so when: - the source sat outside a run narrowed to directories. Only directories narrow, since a file list is what a pre-commit hook passes from a commit, and it names the destination alone. - the move changes the lexer the file reads through. - **#3684, per rule.** A move out of a file some rule never read is read in full for that rule alone, with every other rule keeping the move's own hunks. The rule-to-list mapping is data, a new `file_exemption` key on the `home-path` rule in `prose-gate.json`. The same comparison covers `issue-ref` on a move into instruction text. `rule_home_path` now reads its exemption through the same `exempt_rules` helper. The scope note counts a file read whole for a rule in full. - **#3694, a Compose block scalar.** `container_paths` treats a key or sequence line ending in a `|` or `>` block-scalar indicator, with chomping and indentation indicators, as opening opaque text. It skips deeper lines until indentation returns to the opening node's column. So a `volumes:` key or a mount-shaped line inside a scalar is no longer taken as a volume entry. ## Verification - `tests/test_prose_lint.py`: 655 passed. Each new case fails against `develop`'s gate. - The gate on its own diff exits 0. `mypy` and `ruff` are clean. - Local strict review ran three rounds at the `frontier` tier. Round one found a pre-commit file-list regression, sibling path dependencies, a consolidation, and a stale scope count. Round two found a `dead-path` over-read. Round three found nothing. The consolidation with `nested_spans`' YAML lexer walk was declined: `home-path` reads only the `source` unit, and `check_file` skips Pygments when no requested rule needs more. The reviewer accepted that. ## Deferred - #3711: a move that changes a Compose name, or a Markdown file's directory, changes only part of what `home-path` and `dead-path` report. A whole read there would surface every older finding in the moved file, so these cases are deferred to a finding comparison across the move. ## Pre-existing findings, not filed - `.github/actions/prose-gate/prose_lint.py` `changed_lines`, correctness: an extensionless script whose edited shebang changes its lexer brings only the edited line into scope, with or without a move. Closes on promotion: #3684, #3694 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
|
Important
This repository does not receive automatic reviews because it has fewer than 10 stars.
|
There was a problem hiding this comment.
🟡 Changes recommended
A critical active finding remains because env directory changes can cause the guard to resolve and validate the wrong repository, owner, or branch instead of failing closed.
1 open issue
What changed in this PR
This pull request promotes the third backlog-burndown round from develop to main, improving push repository resolution, prose-gate move and YAML handling, uv diagnostics, and shared section-name normalization.
Changes:
- Resolve push remotes and branches from Git’s effective repository configuration, failing closed when unresolved.
- Expand prose-gate handling for moved files and skip Compose block-scalar contents during volume analysis.
- Clarify uv no-cache interpreter failures.
- Centralize section-name lookup through
section_key(). - Outstanding findings:
- Critical:
env -Candenv --chdirchanges are not reflected when resolvingGIT_DIR. - Moderate: valid YAML mapping keys containing
#are not recognized by block-scalar detection.
- Critical:
| File | Description |
|---|---|
tests/test_repo_gate.py |
Tests uv interpreter startup diagnostics. |
tests/test_prose_lint.py |
Tests moved-file scope and Compose scalar handling. |
tests/test_fidelity_text.py |
Tests section-key normalization. |
spec/fidelity_text.py |
Adds the shared section-name lookup helper. |
spec/audit.py |
Uses the shared section key for section presence checks. |
spec/audit_markdown.py |
Uses the shared section key across Markdown checks. |
scripts/carry.py |
Routes carry section folding through the shared helper. |
scripts/canonical_review.py |
Uses the shared helper for review-unit matching. |
scripts/build_dist.py |
Uses the shared helper for include heading lookup. |
OPERATIONS.md |
Documents the section-key primitive. |
host-setup/agent-safety/README.md |
Documents push repository and branch resolution behavior. |
host-setup/agent-safety/claude/gh-write-guard.py |
Resolves push repositories and branches from Git options and command environments. |
.github/actions/repo-gate/repo_gate.py |
Explains missing uv-resolved interpreters caused by no-cache settings. |
.github/actions/prose-gate/prose-gate.json |
Declares rule-specific file exemption metadata. |
.github/actions/prose-gate/prose_lint.py |
Adds per-rule moved-file scope handling and Compose block-scalar skipping. |
This pull request requires changes.
Review effort: Lite
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #3715 +/- ##
=======================================
Coverage 82.02% 82.02%
=======================================
Files 27 27
Lines 9273 9275 +2
=======================================
+ Hits 7606 7608 +2
Misses 1667 1667
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
…se Gate (#3721) Closes on promotion: #3717 Fixes a finding raised on promotion #3715. The prose gate's Compose reader missed a block scalar opened by a valid mapping key containing `#` (for example `x-a#b: |`, or a quoted key such as `"x #b": |`), so mount-shaped text inside it was read as a volume entry and the home-path rule skipped it. `container_paths` now finds block scalar openers through the existing pygments YAML tokenization, reusing `Starts`, and the hand-written `BLOCK_SCALAR` regex is removed. Tests cover a key with `#`, a trailing comment after the indicator, and quoted keys. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
Fixes a finding raised on promotion #3715, against the change #3709 made. ## What was wrong `_launch` in `host-setup/agent-safety/claude/gh-write-guard.py` read past an `env` launcher without applying it. Constructed scratch repositories reproduced each shape, two checkouts whose origins name different owners, one with a pull-request rule on `main`: - `env -C <dir>`, `env --chdir=<dir>`, and `env --chdir <dir>` left the push judged in the caller's repository. So `env --chdir=/srv/other GIT_DIR=.git git push origin main` read the wrong remote and allowed. - `GIT_DIR=<mine>/.git env -i git -C <other> push origin main` and the same with `env -u GIT_DIR` read the cleared `GIT_DIR`, so the check again ran against the wrong repository. - `env -S '<command>'` and `env -S'<command>'` were never read at all, so every requirement missed the command, including requirements 1, 3, and 6. ## What changed - `_launch` now returns a `_Launch` carrying what an `env` changes. `-C`/`--chdir` moves the directory the command runs in, composed before a git `-C`. A run-time directory leaves it undetermined, which fails closed for `main`, `master`, and `develop` under requirement 4, as #3709 treats an unresolved `GIT_DIR`. `-i`/`-` and `-u`/`--unset` clear what the command inherits, and `-S`/`--split-string` is split into the words it runs. A short cluster such as `-iC dir`, an unambiguous long prefix, and the value-taking `-a` and `-P` read the way `env` reads them. - `_read` applies those to each command's environment, its own assignments, and its directory, and passes the moved directory into an `sh -c` body. - Self-test cases cover each shape, against the real two-owner checkouts for requirement 4's repository and branch, and against the fixtures for requirements 3 and 6. - A push naming no refspec now resolves an absolute `git -C` from an unnamed directory, after a `cd "$X"` or an `env -C "$D"`, as the push's remote already did. Raised in review here, and it was already reachable through `cd "$X"`. - `host-setup/agent-safety/README.md` states how an `env` is read, and that an `env -C` moves the directory requirement 6 tests. ## Local strict review Three recorded passes at the `frontier` tier, the last over the final content with no findings. The first pass's findings on an `env -S` comment, a repeated `-C`, and long-option prefix matching are fixed here, and the second pass's README wording finding is fixed here too. Two more recorded passes covered the review fix on the second push. The first of them caught a signature change that broke `test_install.py`, which is held by #3704, so the signature was restored. ### Pre-existing findings, filed - `host-setup/agent-safety/claude/gh-write-guard.py` `_push_targets`, `pre-existing`: an all-letter word after `-o` is read as a flag cluster, so `git push -ofoo origin develop` is misread. Filed as #3720. ### Deferred - `host-setup/agent-safety/claude/gh-write-guard.py` `_bare_push_branch`, `introduced`, design: its `leading_cd` and `moved` parameters are now reached only from `test_install.py`. Removing them needs that file, so it is filed as #3723. ### Dropped findings - `host-setup/agent-safety/claude/gh-write-guard.py` `_env_option`, `_skips_hooks`, `_push_targets`, consolidation: one shared short-option cluster reader. The other two are untouched code with different value letters, so merging them is a change of its own. - `scripts/docker_lint.py` `shell_shebang_interpreter`, consolidation: it also walks `env` options. Declined because the hook installs as one standalone file and cannot import it. Closes on promotion: #3716 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
|
Answering the first round's outstanding findings on 27870ae. Critical, |
…rite Guard (#3727) ## Summary Fixes a finding raised on promotion #3715. GNU `env` accepts a unique abbreviation of a long option, so a constructed `env --ch=/srv/other GIT_DIR=.git git push origin main` runs git in `/srv/other`. The write guard read `--ch` as a no-op and resolved the push's repository from the working directory. - An `env` option the guard does not recognize, long or short, now leaves the directory a push runs in unnamed for requirement 4. `_push_repo` and the bare-push branch reader take `None` in place of the working directory, the value `env -C "$D"` already produces, so requirement 4 fails closed only for `main`, `master`, and `develop`, as #3709 and #3722 treat a shape they cannot resolve. An absolute `--git-dir`, git's own `-C`, or an absolute `cd` in a wrapper body still names the repository. A `GIT_DIR` behind such an option is not read for the repository, since the option may abbreviate `--unset`. Prefix matching was decided against. - Each unrecognized option may or may not take the next word, and a value it takes may be an `-S` string, which reaches an abbreviated `--split-string`. `_launches` reads every combination of up to four of those choices. A reading past the first adds commands only and changes no shell state, and a subshell restores the body's undetermined state with its directory. - A push naming no refspec or `HEAD` behind such an option, whose branch nothing else names, reads it as if the option were absent. So it is never weaker than before and never newly blocks a feature-branch push. - `--null`, `--debug`, and the GNU signal options are now listed, so they keep reading as no-ops. `-0`, `-v`, `-i` and the value letters keep their behavior. - Requirement 6 keeps judging the working directory. The extra readings can only add a deny there, for example `env --chd /path/wt git commit` run from a primary checkout. - `host-setup/agent-safety/README.md` states the new reading in its lexer paragraph, in requirement 4, and in requirement 6. ## Verification - Reproduced first with `classify` on two constructed scratch checkouts, origins `alpha/x` and `beta/y`, with a stubbed live-rules lookup requiring a pull request on `beta/y`'s `main`. Before the fix `--ch=<b>`, `--chd <b>`, and `-X <b>` pushes to `main` were allowed. After it they are denied, and feature-branch pushes stay allowed. - New self-test cases cover `--ch=<dir>`, `--chd <dir>`, an unknown short letter, two unrecognized options of different arity, an abbreviated split string, an unknown option ahead of `sh -c`, a subshell in the body, absolute `-C`/`--git-dir`/body `cd` re-anchoring, a URL remote, bare and `HEAD` pushes, `GH_REPO` not leaking from a speculative reading, each recognized option unchanged, and allow on `feature/x`. Each new repository and branch row fails with its fix reverted. - `gh-write-guard.py --selftest` passes. Pinned ruff format and check, mypy, `tests/test_guard_size.py`, and the prose gate pass. - Seven local strict-review passes, the last raising nothing. ## Dropped findings - `gh-write-guard.py` `_env_option`, `style`: reading an unknown short letter as unrecognized was named as over-hardening. The decided fix for #3725 names short options explicitly. ## Pre-existing findings, not filed - `gh-write-guard.py` `_bare_push_branch`, `pre-existing`: its `leading_cd` and `moved` parameters answer no caller, since every caller passes `None` and `False`. One caller is in `host-setup/agent-safety/claude/test_install.py`, outside this change. Closes on promotion: #3725 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…n in the Write Guard (#3731) Fixes a finding raised on promotion #3715. Closes on promotion: #3728 ## What changes `_bare_branch` in `host-setup/agent-safety/claude/gh-write-guard.py` read the branch a push naming no refspec (or `HEAD`) writes. Behind an `env` option the guard does not recognize, it fell back to reading that branch in the caller's directory with the command-carried `GIT_DIR`, as if the option were absent. So from a feature branch, `env --ch=/srv/other git push origin` was judged on the caller's feature branch while git may push `main` in `/srv/other`. - The fallback is deleted, along with the `lenient` read of `_named_git_dir` it alone used. - Behind such an option the branch now resolves only where an absolute `--git-dir`, git `-C`, or `cd` names the repository, as the existing reading already does. - Otherwise `_bypass_denial` reads the push as writing every branch protected by default, as `--all` is, so a protected default fails closed. `_push_targets` now takes the branches a bare or `HEAD` push may write as a tuple, so that expansion has one home. - A denial from that expansion carries its own reason, naming the unrecognized option and the ways to anchor the push, rather than claiming a write to `main`. - Requirement 4 in `host-setup/agent-safety/README.md` states the new behavior and its cost once: such a push may be refused even from a feature branch. ## Verification - Reproduced first with the guard's own `classify` on two constructed scratch repositories, one checked out on `main` and the caller on a feature branch. Before: `env --ch=<main repo> git push origin` allowed. After: denied. `env --ch=<main repo> git push origin feature/x` stays allowed, and an absolute `git -C` or `--git-dir` naming the feature-branch repository is judged on its branch and allowed. - New and changed self-test rows: the no-refspec case and `HEAD` behind an unknown option are refused, an absolute `--git-dir` or `git -C` behind one resolves to that repository's branch, an absolute `git -C` naming a feature-branch checkout behind one is allowed, the explicit-refspec feature push stays allowed, and an unresolved bare push with no `env` option stays allowed. - `gh-write-guard.py --selftest`: SELFTEST PASS. Prose gate, ruff check and format, and pyright are clean. ## Local strict-review findings The two findings the opening push listed as open (no row checked which reason a denial carries, and the `_bare_branch` docstring naming a plain `cd` as an anchor) are fixed in later commits on this branch. Self-test rows now mark the dedicated reason, pin both halves of the condition that chooses it, and, through the rules seam, pin that the expansion reaches each of `main`, `master`, and `develop` for a bare, `HEAD`, and `@` push. Each row was checked against a mutant it must kill. The last local pass over the pushed head raised no findings. ## Copilot review on 7a3b468 - Its three threads (docstring anchor scope, the wrapper-body `cd` remedy, a reason-regression self-test) are fixed and resolved. - Its overview also listed, with no thread, "Scope the unresolved-branch denial reason to only the targets produced by the expansion". Fixed: the dedicated reason now applies only to a denied target the push does not name, and named targets are judged first, so `HEAD develop` with a rule on `develop` keeps the ordinary reason. ## Filed separately - #3729: a bare push after an earlier branch switch in the same command is judged on the old branch (pre-existing). - #3732: `pr_review.py` has no spelling for Copilot's `Review findings remain:` label, seen on this PR. - #3730: whether every bare push with an undetermined repository should fail closed, and whether requirements 4 and 6 should read an unrecognized `env` option the same way (decision for the maintainer). 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
🔵 Needs a closer look
Human assessment is needed because a critical hidden finding indicates that multiple ordered env -C directory changes are mishandled, potentially applying the wrong remote and branch rules.
No open issues 🎉
1 resolved since last review
Review effort: Lite
|
Answering the round's Ground truth from GNU coreutils Checked with three constructed repositories whose A separate, pre-existing question surfaced while checking (a no-refspec push whose branch the guard cannot read for a reason other than an unrecognized |
…nd Add a backlog-burndown Away Mode (#3736) Promotes the changes merged into `develop` since promotion #3715 to `main`. - [#3704](#3704): squash-merges a gate-met feature pull request through `pr_review.py merge`, and the host-safety kit now owns its `autoMode` entries. - [#3735](#3735) adds an away mode to `backlog-burndown`: - Parallel file-claimed rounds with no maintainer present, run by a lean orchestrator. - A group meeting a question parks behind a `decision` issue instead of asking. - Merges go only as far as the `develop`, `main` or `release` scope named at invocation. - The end report names each held-back issue's real reason. It also adds the attended `decision-triage` skill, which researches and asks every open question at once, and retires `unattended-handoff`. - [#3737](#3737) reads a pull request that reads back closed after `pr_review.py merge`'s merge call as a refusal that merged nothing, rather than as an unconfirmed merge. It answers this promotion's first review round. After the release, the host-safety installer re-run that #3704 needs is still the maintainer's, per its handoff #3734. That handoff's step 3 names an `unattended-handoff` worker. That skill is retired here, so the verification runs from a `backlog-burndown` worker instead. Fixes #3375 🤖 Generated with [Claude Code](https://claude.com/claude-code)

Promotes the changes merged into
developsince promotion #3703 tomain, the third round of a backlog burndown.#3709 Reads a push's remote, and the branch of a push with no refspec, in the repository git actually uses in the write guard's owner check (requirement 4). Before,
--git-dirand aGIT_DIRon the push were ignored, so a constructedgit --git-dir=<other>/.git push origin developwas checked against the working directory's repository rules and allowed. A shape whose repository the guard cannot determine (an earlierGIT_DIR, a-cor--config-envoverride of aremote.orurl.key, anyGIT_CONFIG*variable, an unresolvable--git-dir) now blocksmain,masteranddevelop. The self-test covers each shape andhost-setup/agent-safety/README.mdstates the requirement.#3712 Reads a file moved out of a source some rule never read in full for that rule in the prose gate: a narrowed directory scope, a
home_path_exemptfile, instruction text forissue-ref, and a move that changes the file's lexer. Skips YAML block scalars when reading a Compose file's volume entries.#3707 Names uv's no-cache setting, and its remedies, when the repo gate's resolved PyYAML interpreter is gone at launch, rather than reporting a bare missing-file error.
#3708 Moves the section-name lookup key into one
section_key()inspec/fidelity_text.py, used by the carry, the audit, the skill build and the review ledger, with no behavior change.#3722 Applies
env's directory and environment options to the command the write guard judges, answering this promotion's first review round:env -C/--chdirmove the repository a relativeGIT_DIRand the remote resolve in,env -i/-uclear aGIT_DIRthat no longer steers the check, andenv -Sis read rather than skipped. A run-time directory reads as undetermined, which blocksmain,masteranddevelop.#3721 Finds a Compose block-scalar opener through the YAML lexer the gate already uses rather than a hand-written pattern, so a key holding
#, quoted or not, opens the opaque region. Also answers this promotion's first review round.#3727 Reads an
envoption the write guard does not recognize, such as GNUenv's--chabbreviation of--chdir, as leaving the push's repository undetermined, somain,masteranddevelopfail closed, answering this promotion's second review round. A feature-branch push is never newly blocked.#3731 Removes the fallback Read an Unrecognized env Option as an Undetermined Directory in the Write Guard #3727 kept for a push naming no refspec behind an unrecognized
envoption, answering this promotion's third review round. Such a push resolves its branch only through an absolute--git-dir, git's own-C, or acdin a wrapper body, and is otherwise read as writing every protected default, so it fails closed even from a feature branch.Deferred review findings: #3705 (
scripts/README.md's uvx promise), #3706 (validate.py's exact section-name match), #3710 (whethersection_keyshould casefold), #3711 (Compose-name and directory moves in the prose gate), #3713 (the review reader'sopen review findings:spelling), #3714 (an edited shebang that changes a file's lexer), #3720 (the write guard's reading ofgit push -o<value>), #3723 (unused_bare_push_branchparameters), #3729 (a bare push after a branch switch earlier in the same command), #3732 (the review reader'sReview findings remain:spelling).For the maintainer: #3730 asks whether every bare push whose repository the guard cannot determine should fail closed, as #3731 now does behind an unrecognized
envoption.Fixes #3695
Fixes #3684
Fixes #3694
Fixes #2358
Fixes #3686
Fixes #3716
Fixes #3717
Fixes #3725
Fixes #3728
🤖 Generated with Claude Code