Repository navigation
Conversation
First Principles Review (Opus 5.5, fork) — ✅ PASSPremise-level review of First-Principles-Verdict: PASS Nothing blocks. Issue #12631 and a pinning test that is refused on base back the fix, both halves are within the Goal, and the one deeper fix (carrying quote state across lines) is rejected in the description with an oracle row as evidence. What this change shipsInventory (8 items) — 8 justifiedIntent: let an agent chain a multi-line
[FIRST-PRINCIPLES-REVIEWED] 5986a0e Description read[DESCRIPTION-READ] 792c3c1586d617f01443acd645a28203f8683856a11f9ac9a58c4a238aa2ddfc That sha256 names the PR description this verdict read. Recompute it with |
Design Review (Opus 5.5, fork) — ✅ PASSDesign-level review of Design-Verdict: PASS Nothing blocks: the change only loosens the gate, through one closed allowlist. The per-line reading keeps the deny authority, and a real-bash oracle backs it. A revert walks it back cleanly. Notes
[DESIGN-REVIEWED] 6dfccbc Description read[DESCRIPTION-READ] d7f7f548c8b8ef54b10313047181dc3bc23e6f5740e9cd8aa64157a9a9eacf68 That sha256 names everything this verdict read: the PR description, plus the 1 evidence file(s) the workflow collected from it and handed this review. It covers that evidence because the media strip replaces every attachment URL with the same placeholder, so an edit swapping one attachment for another would leave the description's captured text identical while what this review judged changed. Recompute it with |
GPT 6.1 Review (fork) — ✅ no blocking findingsReviewed Review detailsNo findings. |
Opus 5.5 Review (fork) — ✅ no blocking findingsReviewed |
Security Scope Review (fork) — ✅ PASSWhich legitimate operations does adjudicated, zero confirmed regressions Scope-Verdict: PASS Reason: Mostly this change loosens the push floor: a newline inside quotes now stays inside its word. The one stricter part is a per-line pass that runs on every multi-line command and adds each push line's single-line tags back into the result; that is the same reading the base splitter already gave. No regression was handed to this lane. A residual narrowing argument is named under UNADJUDICATED; it does not block. TIGHTENING: the git-publish argv floor ( ADJUDICATED: 16 candidates, 0 confirmed newly-refused PLATFORM COVERAGE: posix -- one row uses a heredoc commit message ( UNADJUDICATED: The refusal's new "Put the push on its own line" hint ( [SCOPE-REVIEWED] 5986a0eSecurity scope review -- adjudicated candidates
No candidate flipped from allowed to refused. |
f96e4f0 to
df753ed
Compare
|
Could a maintainer re-run the failed jobs? A fork contributor can't re-run workflows here. On
Every file that exercises the push floor is green, and the Design, First Principles, GPT 5.6 and Opus 5 reviews pass on this head. The fork Security Scope lane hasn't re-run since |
df753ed to
b5c36ce
Compare
b5c36ce to
bdf1c79
Compare
|
All five AI reviews pass on
Happy to rebase or adjust anything on the takeover branch. |
edfdd75 to
1b87861
Compare
1b87861 to
b992d86
Compare
|
Reviewed, not approving (crew-pr-reviewer). This changes the security command parser, and |
UX Review (Opus 5.5, fork) — ⏭️ skippedRevision |
|
This stops refusing a feature push chained after a multi-line quoted git commit message. It fixes the root cause in _split_push_command_segments: a quoted newline now stays in its word. It keeps main's per-line reading as the deny backstop in _git_publish_floor_tags, and it drops the ungated sentinel only for an inert git commit word that passes a closed allowlist. I tried to break it in three ways. First, 39 hand-made shapes: HEAD:main, +main, :main, refs/heads/main, mai''n, "main", @, --all/--mirror, backslash-newline splices, |
|
This PR stops the push gate from refusing a feature push chained after a multi-line quoted git commit message. It fixes the root cause in _split_push_command_segments, where a quoted newline now stays inside its word. Main's per-line reading stays the deny backstop in _git_publish_floor_tags, and the ungated sentinel is dropped only for an inert git commit word that passes the closed _quoted_word_is_inert allowlist. Head 9e252fc is new since our last review at 1c569f3. I diffed the two heads' production files. The only security-relevant change is in _inert_segment: |
|
This PR stops the push gate from refusing a feature push chained after a multi-line quoted git commit message. It fixes the root cause in |
|
HOLD at 91eeda3. This PR changes the push gate's deny logic in Since our last review at dcb6bfa, the production delta only narrows the allow:
I found no concrete defect. Two things need answers:
The other AI lanes pass, and Security Scope found no confirmed regressions. I did not run the gate or the oracle tests locally. — crew-pr-reviewer (r100) |
|
HOLD at b74bc22. This PR stops the push gate from refusing a feature push chained after a multi-line quoted What blocks approval:
CI is otherwise green on this head. I read the code and tests but did not run them. — crew-pr-reviewer (r103) |
|
Rebased onto current Two asks for a maintainer:
|
|
Re the GPT 6.1 blocking finding on Finding. kiro-cli titles a shell call Fix. Tests (
Sweep. 8,411 commands, each judged bare and prefixed. On this head the title is never weaker than the command (0 cases), and on commands without a prefix of their own the two forms always agree. The title allows only the 122 commands this PR already allows bare. Gates pass; the package budget is 32,244 on Could a maintainer approve the fork workflows on |
|
Rebased onto today's @iamwhatever @bolichen97 @cruisercohen could a maintainer approve the fork workflows on this head, and pick up the upstream copy offered earlier ( |
|
Thanks for approving the runs on Status on this head:
Could a maintainer please re-run those three lanes? After that, the PR needs a human review. Or, as offered earlier, an upstream copy, since Sensitive Change Review cannot pass from a fork. |
_split_push_command_segments ended a segment at every newline, including one inside quotes. The next segment's quote walk then restarted unquoted, so the closing quote of a multi-line commit message opened a new quote that swallowed the following && and turned a literal feature-branch push into one unparseable word, refused as git-publish-target-unverifiable. A literal newline inside quotes is now word data, like the other quoted separators, but only while the quote walk can be trusted across lines. The walk does not model heredocs, command/parameter/process substitution, backticks or comments, where a phantom quote could hide the next line, so a command holding any of them keeps the split at every newline. A merged segment that does not parse as a push (quoted text that merely mentions one, like printed instructions or a PR comment body) is read line by line, exactly as before, so no quoted text is newly refused. Unquoted and escaped newlines still split, so backslash-newline splices keep failing closed, and a protected push after a multi-line message is now reported as push-protected-branch-name. kiro-cli titles a shell call `Running: <command>` and the deny tiers judge that title too, so the inert-word proof reads through ONE exact leading `running: ` (the prefix constant denied_rules already uses for its exceptions, now defined beside the walk) and the live call gets the verdict of the command it displays. The refusal's "put the push on its own line" hint moves a push only at an unquoted separator, and judges the rewritten command over every nested shell payload, as the floor does. A separator inside the quoted message, or a bash -c push to $b, earned the hint, and the command with the push on its own line came back with the same refusal and the same hint. Verdicts do not change. The security spec paragraph and the package line budget are updated to match. Refs kirodotdev#12631
|
Opus 5.5 finding on 6dfccbc (the "Put the push on its own line" hint): confirmed and fixed in 5986a0e, still one commit. The same function's second gap, which Opus raised on #20854, is fixed here too. Cause.
Fix. The rewrite now moves a Verdicts did not change. Against 6dfccbc:
The package line budget is 32,730. @iamwhatever @bolichen97 @cruisercohen could one of you approve the workflow runs on 5986a0e? Once the AI reviews are green, this PR only needs a review, or the upstream copy offered earlier if the fork-only gates are still in the way. |
Problem / Motivation
Goal: A literal feature-branch push in the same shell call as a multi-line
git commitmessage is allowed when the call has exactly the shape the reported #12631 commands use: segmentscd <dir>orgit add/commit/push, flags onlycommit -m/-qandpush -u/-q, and a literal single- or double-quoted message. Every other shape (add -A,commit -am/-s,--set-upstream,cd ~/r, a$in a double-quoted body, a redirect) is refused as onmain, with the "push on its own line" hint where that retry passes; see the event replay under Backwards compatibility.git commit -m '<subject>⏎⏎<body>'; git push -u fork fix/xis refused withrule=git-publish-target-unverifiable, and the reason says "shell substitution or expansion fuses text into the push target" even though nothing does. I hit this today on currentmain. @bolichen97 traced the mechanism on #12631:_split_push_command_segmentsends a segment at every newline, including one inside quotes. The next segment's quote walk restarts unquoted, so the message's closing quote is read as an opener, and that quote swallows the following&&/;. The push segment becomes one unparseable word and lands on the ungated sentinel.Why it matters
Multi-line commit messages are the normal way agents write commits. Every such commit chained to a push is refused with a reason that points at a defect the command doesn't have, so the operator or agent can't tell which token to change. The same misreading also hides the real reason for a protected push:
git commit -m "a⏎⏎b" && git push origin mainis refused astarget-unverifiablerather thanpush-protected-branch-name.Not a goal
$(cat <<'EOF' … EOF )commit-message shape. Its top-level reading is now clean, but the nested-payload extractor mis-delimits the substitution around the heredoc and that payload is still refused. That's a separate defect in the extractor. Tracked in push gate: a feature push after a heredoc commit message is refused #20845.gh pr create --body "notes⏎git push⏎more")._is_git_publishmatches the quoted newline as a command boundary. It's a different detector, and the refusal fails closed. Tracked in push gate: a quoted body with a line that reads as a push is refused #20846.env PATH=… git pushprefix from git-publish-* floor refuses literal git push [flags] origin feature-branch #12631 event 10. Tracked in push gate: an env-prefixed push after a multi-line commit message is refused #20847.This allowlist is the base those follow-ups extend, not a one-off: #20847's fix (PR #20854, stacked on this one) widens
_inert_segmentby one prefix and adds its own oracle rows, and any further shape goes the same way, one allowlist entry plus a real-bash oracle row.These are why the PR uses
Refs #12631rather than a closing keyword.Backwards compatibility
Reader:
argv_floor_git.py:_git_publish_floor_tagsis the splitter's only production caller. The splitter change is load-bearing: with onlykeep_quoted_newlinesoff, the reporter's command and event 7 are refused (test_the_splitter_keeping_a_quoted_newline_is_what_allows_the_reported_command).Compatible: the gate moves in the allow direction only, for a literal unprotected push chained after a
git commitwhose shape fits the allowlist under What changed. An opener inside a plain single quote (git commit -m 'fix `foo` ...') is literal to bash and keeps the trust. Every other shape reads exactly as onmain(see Tests).Still refused, as on
mainplus the new hint (one retry with the push on its own line): commit-s/--signoff,--no-verify,--amend,--no-edit;cd ~/ror an env prefix; a double-quoted body containing`,$or\; any$'…'or$"…"body; a heredoc or$(…)message;git tag,echo,printforghas the owner;git -csettings; commit--message,--quiet,-a/--all;add -A/--all; push-f/--tags,--force-with-lease,--set-upstream,--quiet._INERT_FLAGSis not widened without a security-owner review; the real-bash oracle test and the one-oracle-row-per-entry rule (both enforced by tests,security.md) are the bar for any widening.#12631 events replayed with a multi-line message (
issue_events_probe, head vsmain):git commit -m '<ml>'; git push -u fork fix/x, and event 7 (cd /r && git add f && git commit -m '<ml>'or"<ml>"&& git push origin docs/…).-s,cd ~/ror$in a double-quoted body; event 16 (git push -q origin docs/s 2>/dev/null; gh pr create …: the redirect keeps the sentinel; it passes with the push on its own line).main). Events 9 and 10 (env prefix,| tail, no multi-line text) already pass onmain.The one visible change for a protected push is a more accurate rule id (
push-protected-branch-nameinstead oftarget-unverifiable).What changed (motivation → approach → change)
_split_push_command_segments(shell_normalizer_walk.py): a newline that_iter_shell_charsreports as inside quotes (and not escaped) stays in its word and does not end the segment. Unquoted and escaped newlines still split, as onmain.$(/${/backtick/<(/>(body, a real#comment, or any backslash-newline outside a plain single quote keeps the split at every newline (_QUOTE_WALK_UNMODELLED_RE). The list only narrows allows; no denial depends on it.main, so quoted text that only mentions a push reads as before.Also changed:
_git_publish_floor_tags, the per-line backstop and the one deny authority: when the command has a newline,main's per-line reading also runs on every push line. Its ungated sentinel is dropped only where the whole-text walk enters the line inside a quote that closes before the push and ends its word; the code after that close is judged in full.trap 'x⏎git push origin $b #' EXIT git push origin f(Opus 5.5 review) the walk keeps the$bpush inside a quoted word, so the merged segments show only the feature push; only the per-line reading seesgit push origin $b. That row is in the real-bash oracle corpus. The same row is why the root-cause alternative, carrying the cumulative quote state into each per-line segment instead of restarting it unquoted, was rejected: every reading would then inherit the phantom quote and none would see the$bpush.shell_normalizer_walk._quoted_word_is_inert), one closed allowlist checked whole:git commit(nogit -csetting), the owner git-publish-* floor refuses literal git push [flags] origin feature-branch #12631 names;echo,printf,git tagandghread as onmain;-; the word is'…'or"…": a$'…'or$"…"word is decoded or translated by bash and no reported event needs one, so it is never data (First Principles item 5); a double-quoted body holds no$, backtick or\, so no expansion ("$1⏎"with-- -em) reaches bash as an option. Only a literal body is data;. / : = @ % + , - ; &remain, and no masked word is empty or opens with-(bash dequotes'-e'and''-e);cd <dir>orgit add/commit/pushwith only allowlisted lowercase flags (commit -m/-q,push -u/-q, noaddflag: only the flags the replayed git-publish-* floor refuses literal git push [flags] origin feature-branch #12631 events use, First Principles item 6). Only-mtakes a value, a commit must carry its message, and option scanning stops at--(unless that--is the value of-m), so nogit config,-e/--edit,--amend,--author/--dateor pathspec named--no-editcan open an editor on the text;cd <dir>cannot change which push bash runs (Design review off9313632):<dir>must be one plain word or a literal quoted word (no$,~,\, backtick, glob or brace), so bash expands it to literal text and runs nothing; a function or alias namedcdwould need a definition, which is outside the allowlist; and the push's destination is the literal refspec word the floor judges, the same wordmainalready judges after a single-linecd dir && git push origin feat. A directory's own git config (for example aremote.<name>.pushmapping) is the same exposuremainhas for that single-line form, so the exemption adds none.cd .is the oracle row;source,trap,eval,~path, env prefix) keeps the sentinel, as onmain. A new bypass is closed by the allowlist, not a new clause. The allowlist is meant to stay this narrow (Design review of3eb721db):add -A,commit -s,cd ~/rand a$(cat <<'EOF'…)message stay refused on purpose, and any widening is a maintainer's recorded decision, carrying a real-bash oracle row that a test enforces (security.md).1b0852fc5): kiro-cli titles a shell callRunning: <command>, and the deny tiers judge that title as well as the command (GateFacts.deny_targets), so a proof anchored at offset 0 left the live title refused for an allowed command (fails closed, but the fix missed the real call)._quoted_word_is_inertnow reads through ONE exact leadingrunning:(the text is lowercased there), the same ruledenied_rules._exception_appliesalready applies. Its constant_SHELL_TITLE_PREFIXmoved toshell_normalizer_walk.py(denied_rulesimports it back throughshell_normalizer; the walk cannot importdenied_ruleswithout a load cycle), so there is still one copy.running: running: …,running:git …,xrunning: …,running …and a prefix after a separator are not stripped and read as a command word.main's per-line reading gives a push line, with one exception. The ungated sentinel is the one relaxed deny: it is dropped only on a line the walk enters inside a quote, and only when_quoted_word_is_inertaccepts that word. So the safety of that drop rests on_quoted_word_is_inert(with_inert_maskand_inert_segment) modelling bash correctly, which is why every allowlist entry needs a real-bash oracle row (Design review off66ddd0ec). Each inert check exists because an Opus 5.5, GPT 6.1 or Design review showed such a command;test_every_per_line_deny_survives_the_whole_text_readingpins the bar andtest_no_command_real_bash_runs_as_a_protected_push_is_allowedchecks it against real bash._quoted_line_entries), and which clears the floor once eachgitcommand after an unquoted separator (with anyenvor assignment prefix) is moved onto its own line, judged over every nested shell payload as the floor judges it (deny_gate._own_line_fix_applies), says "the push shares a line with a multi-line quoted argument, so its destination branch cannot be read before the push runs. Put the push on its own line." in place of the substitution reason, which such a command does not have. Every other unverifiable push keeps the substitution reason: a separator inside the quoted message is message text and is not moved, abash -cpush that stays unverifiable keeps the hint out, and so does a walk that fails. Only the reason text depends on this; no verdict does. The hint alone does not fix git-publish-* floor refuses literal git push [flags] origin feature-branch #12631:git commit -m '<multi-line>' && git push origin featis the shape agents emit by default, so a hint-only fix costs a retry turn on every commit. The exemption allows exactly that shape.docs/system-specs/modules/security.md: when a quoted newline stays in its word, the per-line backstop as the one deny authority, the construct list as allow-narrowing only, and the oracle-row rule.test/test_security_gate_liveness.py: the package line budget is re-pinned from 32,526 (currentmain,b9ff59ab5) to 32,730. The per-line inert-head reading lives inshell_normalizer_walk._inert_quoted_line_heads, so the floor's loop stays small, and theargv_floorfacade names it in itsTYPE_CHECKINGimport list (test_argv_floor_facade.pychecks that list); moving it changed no verdict or floor tag on 17,616 generated multi-line commands.518b36d1, which splits the security facade and the shell reader under the 2,000-line cap: the splitter change and the quoted-newline helpers (_QUOTE_WALK_UNMODELLED_RE,_INERT_FLAGS,_quoted_line_entries,_quoted_word_is_inert,_inert_quoted_line_heads,_inert_mask,_inert_segment) moved with_split_push_command_segmentsintoshell_normalizer_walk.py(1,510 lines), whichshell_normalizer.pyre-imports forargv_floor.pyanddeny_gate.py;_own_line_fix_appliesand the hint moved withis_deniedintodeny_gate.py, exported on the facade like its neighbours. The tests that patch the construct list or_opens_commentpatchshell_normalizer_walk, where their readers are. No code changed:is_deniedand_git_publish_floor_tagsgive the same result as12dc6bed3on all 17,617 commands of the move probe corpus (0 differing).2798d6b3, which splitsargv_floor.pyinto owner modules behind a facade: the per-line loop and the kept-newline re-split moved unchanged intoargv_floor_git._git_publish_floor_tags(1,136 lines). Same move probe against01f2d721: 17,617 commands, 0 differing.Tests
In
test/test_push_branch_gate_parser.py(wheremainmoved this class),TestTokenizerContractBetweenTheLayers:test_a_newline_inside_quotes_stays_in_its_word:&&and single-quoted;feature pushes after a multi-line message are allowed;main, and an unquoted newline before a push tomain, both reportgit-publish-push-protected-branch-name.test_a_quote_the_walk_cannot_model_keeps_the_split_at_every_newline:"in its body, a backtick phantom quote, the three backslash-newline bypasses from the Opus and GPT reviews (inside double quotes, before an ANSI-C quote, and inside a heredoc opener), and the four comment phantoms hiding an ungated-only push from thed093e50reviews ($'main',$b,~, and$bbehind a real multi-lineX="...") all stay denied (each fails on the commit it was reported against, passes now);Fixes #1stays whole.test_quoted_text_that_quotes_a_push_line_is_judged_line_by_line: the Security Scope Review's newly refused rows (an echo, a printf, a PR comment body and a commit message that quote a feature push line) are allowed again, and a quoted push tomainstays denied. Fails onf96e4f0, passes now. It also holds the editor and quoted-option rows ('-e',''-e,"--edit") and the refusal-hint row, which fail on3752abf6. The--author -m/--date -mamend rows and the droppedecho/git tag/gh prowners followed by a feature push fail on33f99449and pass now.test_the_unmodelled_construct_list_is_pinned_to_the_quote_walk: pins the_QUOTE_WALK_UNMODELLED_REpattern and checks every listed construct, backslash-newline included, turns quoted-newline trust off, and that the same construct inside a plain single quote does not; a single-quoted message holding a backtick, chained to a feature push, is allowed and the same chain tomaindenied. An earlier revision also pinned a sha256 of_iter_shell_chars's source; it was removed on the First Principles review, since the per-line backstop and its test now guard a construct the list misses.d093e50removed the list on the First Principles review, since the backstop denied every known phantom shape on its own; the Opus and GPT reviews then found that the backstop as written (targets only, every sentinel dropped) let a comment apostrophe hide$'main'/$b, which the list's#check had been covering. Both are restored and the backstop is strengthened, so either guard alone denies every case found. Separately, why both guards stay: the construct list decides where the merged (quote-aware) reading is used at all, so an allow rests only on a walk that models bash for that command; the per-line backstop is the deny guarantee if the list is ever incomplete. At this head, removing a construct from the list no longer lets its phantom case through, because the backstop denies it; that is the backstop's job, andtest_a_phantom_quote_cannot_hide_a_push_even_without_the_construct_listpins it.test_the_shell_title_gets_the_verdict_of_the_command_it_displays(GPT 6.1 review of1b0852fc5): every allow row in_INERT_ALLOW_ROWS(the reported shapes and each oracle row's feature twin, 11 rows) with and withoutRunning:; the 11 prefixed cases fail on1b0852fc5.test_the_shell_title_prefix_is_read_through_once_and_only_exactly: the prefixed oracle rows (protected pushes) stay denied, the prefixed form of every corpus command the bare form denies is denied, and the lookalikes above stay denied.Existing suites:
is_denied,shell_normalizer,_split_push_command_segmentsor_git_publish_floor_tags, plus the line-budget test: 8277 passed, 16 skipped. After the rebase ontoaf1eb61: the push-gate, line-budget, argv-floor and security test files, 5103 passed. After the rebase onto518b36d1: the push-gate, line-budget, argv-floor, denied-commands, split patch-reach and facade test files, 1339 passed, and the substitution/paren/heredoc subset oftest/test_security.py, 65 passed. After the rebase onto2798d6b3: the same files, 1328 passed. On9800507e1, with the split security suites and facade tests added: 1,847 passed, 1 skipped (2 local-onlymarkdown_itimport errors).test_a_phantom_quote_cannot_hide_a_push_even_without_the_construct_listempties the construct list and checks the three reviewer bypasses still deny a hidden push tomain, a hidden bare push and a hidden$bpush; it fails without the per-line backstop.test_every_per_line_deny_survives_the_whole_text_reading(Design Review's differential): over this module's own multi-line push corpus plus generated head × push × tail variants (comment apostrophes,X=",trap ', heredocs, backslash-newlines, backticks ×main/master/bare/$b/$'main'/~/${b}/feature targets), run with the construct list on and emptied, every tag a push line earns judged alone as a single-line command is present in the whole-text verdict; only the ungated sentinel may be absent, and only on a line the whole-text walk enters inside a quote. It fails when the per-line backstop is disabled.test_no_command_real_bash_runs_as_a_protected_push_is_allowed(Design Review's "checked against real bash"): an oracle independent of_iter_shell_chars. Real bash parses and runs every command in the same corpus, read from a script file, withgita shell function (loaded throughBASH_ENV) that only logs its argv (b=main,g=git;sh/bashre-enter the same bash; PATH is an empty directory; scratch cwd). Every command whose executed argv is a protected or bare push (over 100 of them, including thetrap $'\x67it …'shape) must be denied, with the construct list on and emptied. It fails ona4c74349, where bash runs amainpush the filter allowed. It is never skipped: Windows runs it under the shared Git Bash lookup (_find_posix_test_shell), since the function stubs need no shebang or symlink, and a missing bash fails the test on every platform (Design review of33f99449, GPT 6.1 review of30e81bdb). On Linux the function stubs catch exactly the same 194 commands as the earlier PATH stubs. On3752abf6it ran and passed (6.2 s) on Linux, in Backend Tests (3.12, 3). On9f26005bit ran and passed on Windows in Backend Tests (Windows) (3), in 40.79 s against its per-commandtimeout=10. On6c45e3c4it passed on Windows again in Backend Tests (Windows) (3), in 38.80 s. On9eaabe9fit passed on Windows in Backend Tests (Windows) (3), in 38.72 s.test_every_per_line_deny_survives_the_whole_text_reading), Design review of529a6daa8: both are plain tests intest/test_push_branch_gate_parser.py, with no mark or skip.ci.yml'schangesfilter counts every path outsidewebsite/,.github/andscripts/as backend, so any change toshell_normalizer_walk.pyorargv_floor_git.pyruns them in the eight LinuxBackend Tests (3.12, 1-8)shards. The eightBackend Tests (Windows)shards run them under Git Bash on a fork head when a Windows path or Windows-branching line changes (or with theci:windowslabel), and onmainafter every merge. On529a6daa8all 16 Linux and Windows shards ran; the one red, Windows (1), is the knowntest_ci_ipv6_routingcollection timeout.test_every_inert_allowlist_entry_has_a_real_bash_oracle_row(Design Review's "enforced by a test rather than by convention"): reads_INERT_FLAGSand fails on any flag with no row in_INERT_ORACLE_ROWS; the real-bash oracle requires every one of those rows to run a protected push that stays denied. Adding-ntogit addfails it.test_generated_mask_edge_words_never_make_a_dash_word_inert(Design Review's "oracle corpus generates mask-edge words systematically"): 17 mask-edge words (empty, dash-leading, glued, escaped quotes) x 3 commit-message bodies (one a disguised$1push) x 3 positions (before, after, glued to the message), each followed by a feature push. Wherever the exemption allows one, every word bash passes togit commitis non-empty and only-m/-qlead with a dash, so no edge word can open the editor. Mutation check: letting quote marks or anything through the post-mask character class fails it (the existing suite missed the quote-mark weakening).test_the_splitter_keeping_a_quoted_newline_is_what_allows_the_reported_command(Design Review's "a test shows a git-publish-* floor refuses literal git push [flags] origin feature-branch #12631 command whose verdict changes when onlykeep_quoted_newlinesis turned off"): the reporter'sgit commit -m '<multi-line>'; git push -u fork fix/xand event 7 with a single- or double-quoted message are allowed on this head and refused when the splitter is forced to split inside quotes again. The per-line exemption alone does not allow them, so both halves decide and_split_push_command_segmentscannot stay identical tomain. A replay of 26 git-publish-* floor refuses literal git push [flags] origin feature-branch #12631-shaped commands found 15 whose verdict or reason changes with that one switch.Manual verification
26-case replay through
security.is_denied, 0 failures:Fixes #123, and a CRLF one) followed by a feature push;main, and an unquoted newline then a push tomain;bash -cpayload with a newline then a push tomain;main;maininside a feature push's refspec followed by a real push tomain.main: only the four quoted-message-then-feature-push cases differ (they are refused as target-unverifiable).Running:title sweep (GPT 6.1 review of1b0852fc5): 8,411 commands (the parser corpus plus generated heads × messages × separators × pushes, includingrunning:and double-prefix heads), each judged bare and withRunning:, on this head,1b0852fc5andmain(9800507e1). On this head the prefixed title is never weaker than the bare command (0 cases), and on the 5,819 commands without a prefix of their own both forms get the same verdict (0 differ; on1b0852fc5the title was stricter on 122). The title allows 122 commandsmaindenies, all 122 also allowed bare on this head (this PR's intended allows). Bare verdicts against1b0852fc5: the only 36 changes are commands that themselves begin with onerunning:; a double prefix is still refused.Also clean:
black --check,isort --check-only,flake8,mypyonshell_normalizer_walk.py,shell_normalizer.py,denied_rules.py,deny_gate.pyandargv_floor_git.py,scripts/check_file_line_cap.py,scripts/check_comment_history.py,scripts/docs-lint.sh.Screenshots / video
N/A. No UI change.
Related Issues
Refs #12631. This fixes the quoted-newline cause diagnosed there. The issue stays open for:
env PATH=…prefix.Pattern harvest
Rule candidate: review-prompt
Pattern: a splitter that trusts a quote state machine across a boundary the machine does not fully model (a newline ending a comment or heredoc line). Trust the state only where the grammar it models is the whole grammar in play, and fall back to the conservative split otherwise.
Checklist
feat|fix|docs|style|refactor|perf|test|chore|ci|build|revert: ...)Contribution License Agreement