Skip to content

fix(hook): stop refusing every Git command that follows another on one line - #37

Merged
rmyndharis merged 1 commit into
mainfrom
fix/guard-over-refusal
Aug 12, 2026
Merged

fix(hook): stop refusing every Git command that follows another on one line#37
rmyndharis merged 1 commit into
mainfrom
fix/guard-over-refusal

Conversation

@rmyndharis

Copy link
Copy Markdown
Owner

The defect

policyTransitionRisk is one running boolean. It was raised unconditionally
after every commit candidate and after every policy-transition or ref-mutation
verb, then stamped onto every later candidate on the same shell line and vetoed
before the candidate was examined.

So the second Git command on a line was refused whatever it was, and lines with
no commit in them were still told to "run the Git commit separately". Measured
against a freshly initialised repo on clean: git commit -m wip && git push,
git fetch && git rebase origin/main, git remote -v && git push,
git tag -l && git push --tags, git stash list && git commit -m x and
git branch --show-current && git commit -m x all denied.

What decides it now

Whether the earlier command moved something a later policy resolution reads:
HEAD, the index, or the worktree that .aimhooman.json comes from.

That is a new third set, GIT_POLICY_INPUT_COMMANDS, and the flag is raised only
when the verb is not in a read-only listing form. It is deliberately a separate
set rather than a narrowing of the two beside it: GIT_POLICY_TRANSITION_COMMANDS
and GIT_REF_MUTATION_COMMANDS also decide whether a verb becomes a modelled
candidate at all, so dropping push from ref mutation would take the
installed-guard check for git push with it.

commit is absent from the set. The state a commit moves is state this tool has
just supervised through pre-commit, commit-msg and the reference-transaction
guard. symbolic-ref, update-ref, rm --cached and stash pop are present:
they move HEAD or the index unwatched, so a commit after them resolves against
state nothing checked. stash is in the set for that reason even though it is
absent from the transition list, and rm and update-ref are there because
resolveStagedPolicy reads both the staged index and headPolicy.

pull stays in the set, so git pull --rebase && git push still denies. Pull
genuinely changes the worktree the next command resolves policy from; narrowing
cannot fix that case and pretending otherwise would be the wrong trade.

Three sites, not one

The alias resolver re-stamped the same flag after the parser had run, so the
narrowing had to be applied in resolveGitAliases as well. Applied in only one
place, none of the commit cases change.

The read-only listing carve-out moved above the transition veto. A command that
only reads cannot be made unsafe by whatever ran before it. It stays below the
checks that fire when the repository cannot be seen at all — GIT_INDEX_FILE,
target environment assignments, and dynamic targets.

git branch --show-current now counts as a listing form; it was missing from
the read-only flag set.

Tests

Two new cases, one for each direction: a second Git command is allowed when the
first moved no policy input, and denied when it did, with symbolic-ref,
update-ref, rm --cached, stash pop and checkout as the guardrails.

The read-only listing test is now exercised under strict as well as clean.
It only ever ran on clean, which is how a change that turns the carve-out off
under strict could ship green — the strict path denies through a different
branch (uncertainShell with no candidates), so it needs its own coverage.

Full suite green, npm run check green.

…e line

One running flag was raised after every commit and every ref-affecting verb,
then stamped onto every later candidate on the same shell line. The second Git
command was refused whatever it was, and lines with no commit in them were still
told to "run the Git commit separately". `git commit && git push`,
`git fetch && git rebase`, `git remote -v && git push` and
`git stash list && git commit` all denied.

What matters is whether the earlier command moved something a later policy
resolution reads: HEAD, the index, or the worktree `.aimhooman.json` comes from.
That is now a third command set, raised only when the verb is not in a read-only
listing form. It is a separate set rather than a narrowing of the two beside it,
because those also decide whether a verb becomes a modelled candidate at all —
dropping `push` from ref mutation would take the installed-guard check for
`git push` with it.

`commit` does not raise it. The state a commit moves is state this tool just
supervised through pre-commit, commit-msg and the ref guard. `symbolic-ref`,
`update-ref`, `rm --cached` and `stash pop` do raise it: they move HEAD or the
index unwatched, and a commit after them resolves against state nothing checked.
`stash` is in the set for that reason, though it is absent from the transition
list.

The alias resolver re-stamped the same flag after the parser had run, so the
narrowing is applied in both places or it takes effect in neither.

The read-only listing carve-out moved above the transition veto. A command that
only reads cannot be made unsafe by whatever ran before it. It stays below the
checks that fire when the repository cannot be seen at all.

`git branch --show-current` now counts as a listing form.

Tests: a second Git command is allowed when the first moved no policy input, and
denied when it did, with `symbolic-ref`, `update-ref`, `rm --cached`,
`stash pop` and `checkout` as the guardrails. The listing carve-out is now
exercised under strict as well as clean — it only ran on clean, which is how a
change that turned it off under strict could have shipped green.
@rmyndharis
rmyndharis force-pushed the fix/guard-over-refusal branch from 0c9db7e to d79d7a6 Compare August 12, 2026 10:04
@rmyndharis
rmyndharis merged commit fe1930b into main Aug 12, 2026
8 checks passed
@rmyndharis
rmyndharis deleted the fix/guard-over-refusal branch August 12, 2026 10:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant