Skip to content

fix(guards): fold shell continuations in the hooksPath guard via one shared helper (#15961) - #16414

Merged
mrveiss merged 5 commits into
mainfrom
issue-15961-hookspath-continuations
Sep 13, 2026
Merged

mrveiss merged 5 commits into
mainfrom
issue-15961-hookspath-continuations

Conversation

@mrveiss

@mrveiss mrveiss commented Sep 11, 2026 •

Copy link
Copy Markdown
Owner

Refs #15961

Single-issue rationale: this addresses one named gap in #15961, the shell-continuation escape in its static guard, by extracting the helper #16128 introduced. #15961 stays open: AC1 (runtime detection of a transient -c core.hooksPath= override) is a separate mechanism.

Thinking Path

repo_tests/hooks_path_override_15961_test.py matched physical lines, so a shell \ continuation hid the most harmful spelling, the persistent override:

git config \
  core.hooksPath /tmp/x        -> escaped the guard

#16128 fixed the same defect in the sibling guard, git_merge_rejects_pull_only_flags_15938_test.py, with a _logical_lines() helper. The plan recorded on #15961 was one helper, not a second copy: pasting the fold into this guard would give base two implementations of one rule, and they would drift.

What Changed

  • Review fix: logical_lines no longer folds out of a whole-line comment. Bash ends a comment at the newline, so a trailing \ inside one is text. Folding it glued the next line behind a #, and _offending_lines skips comment lines, so an override written straight after such a comment went unreported. The old physical-line scan caught it. Regression cases are in _scan_helpers_test.py and in the guard's own catch list.

  • New tools/lint/_scan_helpers.py logical_lines(): folds backslash-newline continuations and reports each command's first physical line number. It has its own unit tests in tools/lint/_scan_helpers_test.py, covering no continuation, two lines, three lines, a mid-line backslash that isn't folded, and the reported line number.

  • hooks_path_override_15961_test.py scans logical lines. New fixtures cover both directions:

    • git config \ + core.hooksPath /x is caught.
    • -c split from core.hooksPath= across lines is caught.
    • git config \ + --get core.hooksPath stays allowed.
  • git_merge_rejects_pull_only_flags_15938_test.py imports the shared helper in place of its private copy. The algorithm is byte-identical, so its behaviour is unchanged.

  • repo_tests/slm_frontend_shell_publish_test.py keeps its own _logical_lines. It has different semantics: it returns strings without line numbers, and strips each continued line before joining. Moving it onto the shared shape would change its behaviour, not just remove a duplicate. The difference is documented in its docstring.

  • The helper's docstring and its unit tests use neutral tokens (some-tool … --dangerous-flag). The hooksPath guard scans every tracked file, the helper's own files included, so a real offending spelling there tripped it twice at pre-push. The real spellings stay only in the guards' own fixtures.

Verification

  • Before any edit, the matcher was checked by hand against the five continuation cases, using a copy run outside the repo.
  • py_compile and ast.parse pass on every touched file, and the full pre-commit suite passed at commit.
  • core.hooksPath override silently disables every hook in a worktree #15961 AC status, checked against base:
    • AC2 (the worktree case): already on base in test_a_relative_hookspath_defeats_hooks_in_a_worktree.
    • AC4 (the remediation message): already on base.
    • AC5 (docs beside the --no-verify rule): already on base, in CLAUDE_RULES.md and CLAUDE_GIT.md.
    • AC1 (runtime detection) is not delivered, and no tracked file reads GIT_CONFIG_PARAMETERS.
    • AC3 (named actor exemptions) doesn't apply to a static sweep.
    • The issue stays open for AC1.

Model Used

Claude Opus 5 (coordinator) with a Claude Sonnet 5 subagent for the implementation.

🤖 Generated with Claude Code

@coderabbitai

coderabbitai Bot commented Sep 11, 2026 •

Copy link
Copy Markdown
Contributor

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Advanced

Run ID: 058432b9-acbc-46cf-a828-00cf62b88f82


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@mrveiss

mrveiss commented Sep 11, 2026

Copy link
Copy Markdown
Owner Author

Review of bffba5b97: approve. 1 commit (verify), 0 trailers, 0 behind, includes #16300. Refs #15961 with a single-issue rationale that correctly scopes this to one named gap and leaves AC1 (runtime detection) open — matches the batching-check requirement and doesn't overclaim.

Verified: behavior-preserving extraction, not a rewrite. The #15938 guard's diff is pure deletion of its private _logical_lines plus an import swap to the shared tools.lint._scan_helpers.logical_lines — same docstring content moved wholesale, same fold algorithm. Both guards now import the identical function (hooks_path_override_15961_test.py:65, git_merge_rejects_pull_only_flags_15938_test.py:35), so there's exactly one fold implementation to get right instead of two that can drift apart, which is the whole point of #16128's original finding recurring in #15961.

Verified: the helper itself is well-tested, including the case that actually matters (_scan_helpers_test.py:238 test_logical_lines_does_not_fold_a_mid_line_backslash — a fold that's too eager is as wrong as one that's too narrow) and first-line attribution (:246).

Verified: both directions covered for the consuming guard. hooks_path_override_15961_test.py:235 test_a_continuation_does_not_hide_the_flag and :248 test_a_continuation_in_valid_usage_is_still_left_alone — a continuation that should trip the guard, and one that shouldn't.

Verified: the guard's own dogfooding note is accurate. _scan_helpers.py:288-290 deliberately keeps its docstring example neutral ("the guards that call this scan tracked files, this one included, so a real offending spelling here would trip them") — I checked this is true: both consuming guards scan the guard/helper source tree itself, so a literal dangerous-flag example in this docstring would have tripped its own guard. That's why the pre-push hook needed a third try per the coordinator's note, and the fix (neutral tokens) is the right one, not a suppression.

Nothing else found.

@github-actions

Copy link
Copy Markdown
Contributor

✅ SSOT Configuration Compliance: Passing

🎉 No new hardcoded values of either class — ssot and other both block.

Known backlog in pipeline-scripts/hardcoded_values_baseline.txt is suppressed and tracked in #14371.

@github-actions

Copy link
Copy Markdown
Contributor

Notice: 29 open PRs — past the runaway threshold (25)

There is no PR queue limit, and this is not a request to defer this PR. Work proceeds one issue at a time without a cap on open PRs; review capacity is the constraint.

This notice only means the count is high enough to be worth a glance for a runaway — something opening PRs in a loop, or a merge pipeline that has stalled so nothing is draining.

Currently open:

If the queue is draining normally, ignore this. Otherwise:

  1. Check whether CI is dispatching at all — see the ci-dispatch-watchdog status on these PRs
  2. Merge the ones whose CI has finished and review has passed: gh pr merge <number> --squash --delete-branch
  3. Look for a loop opening near-identical PRs

Warn-only runaway detector — .github/workflows/pr-queue-gate.yml. It never blocks a merge.

@mrveiss mrveiss added this to the v0.9.0 milestone Sep 12, 2026
…okspath-continuations

# Conflicts:
#	tools/lint/_scan_helpers.py
…line into it (#15961)

Bash ends a comment at the newline, so a backslash at the end of one is text. logical_lines folded it anyway, gluing the next line behind a '#'. The hooksPath guard skips comment lines, so an override written straight after such a comment went unreported, though the old physical-line scan caught it (#16414 review). A whole-line comment now always stands alone. Regression cases are in _scan_helpers_test.py and in the guard's own catch list.
@mrveiss

mrveiss commented Sep 12, 2026

Copy link
Copy Markdown
Owner Author

Review at 5a6376c83 requested changes. Both are addressed in 6f222a838; a delta check is running, and it merges on a settled green after #16487.

Finding Fix
Guard bypass. logical_lines folded a whole-line comment ending in \ into the next line. Bash ends a comment at the newline, so # note \ doesn't continue. _offending_lines skips lines starting with #, so a real git config core.hooksPath … straight after such a comment went unreported. The old physical-line scan caught it A whole-line comment now always stands alone in logical_lines. Regression cases: test_logical_lines_never_folds_out_of_a_comment_line, plus a comment-then-override case in the guard's catch list. The #15938 guard, the only other consumer, has no leading-comment case, so its behaviour is unchanged apart from gaining the same protection
Accidental close. The body's rationale line ("…closes one named gap in #15961… It doesn't close #15961…") made closingIssuesReferences include #15961, although AC1 is undelivered Reworded. closingIssuesReferences is now [], checked after the push

The earlier "approve" comment on this PR was a bot notice against bffba5b97, three commits back. It isn't review evidence.

@mrveiss

mrveiss commented Sep 12, 2026

Copy link
Copy Markdown
Owner Author

Delta 5a6376c83..6f222a838: verified, approved. It merges on a settled green after #16487. Traced, not run:

  • logical_lines now emits a comment line reached with an empty buffer as its own entry, so it can't absorb the next line. A real continuation whose later line is comment-shaped still folds, because that branch needs an empty buffer.
  • Both regression cases fail on the old code. test_logical_lines_never_folds_out_of_a_comment_line gets one merged entry instead of two, and the new guard case is skipped as a comment, so _offending_lines returned [].
  • The Keep worktrees and PR-less branches on a fresh base #15938 guard is untouched. Its fixtures are all single-line, so their output is byte-identical.
  • closingIssuesReferences is [] at 6f222a838.

@github-actions

Copy link
Copy Markdown
Contributor

AutoBot Phase Validation Results

System Maturity: 96.5%

Phase Status:

PASS Phase 1: Core Infrastructure: 100.0%
PASS Phase 2: Knowledge Base and Memory: 100.0%
IN PROGRESS Phase 3: LLM Integration: 66.7%
PASS Phase 4: Security and Authentication: 100.0%
PASS Phase 5: Agent Orchestration: 100.0%
PASS Phase 6: Enhanced UI/UX: 100.0%
PASS Phase 7: Testing and Validation: 100.0%
PASS Phase 8: Advanced Features: 100.0%
PASS Phase 9: Multi-Modal AI: 100.0%
PASS Phase 10: Production Readiness: 100.0%

Recommendations:

  • 🟡 MEDIUM: Phase 3: LLM Integration requires attention (66.7% complete): Review and implement
  • ✅ System is production-ready - consider advanced features and scaling: Review and implement

@mrveiss
mrveiss merged commit d17c15b into main Sep 13, 2026
62 checks passed
@mrveiss
mrveiss deleted the issue-15961-hookspath-continuations branch September 13, 2026 13:38
mrveiss added a commit that referenced this pull request Oct 5, 2026
…t is no longer invisible (#17941)

CodeRabbit. The check matched `line.startswith(("import ", "from "))`, which
only ever sees column-zero imports -- so a function-local `import requests`
passed it.

THAT WAS NOT HYPOTHETICAL HERE. `_comment_syntax` has exactly one deferred
import, `from tools.lint._scan_helpers import logical_lines` inside
`_join_continuations` (#16414, delegating the fold rather than re-deriving
it). Its root is `tools`, which is NOT in the allowlist -- so the guard I
added one commit ago passed only because it could not see the import that
would have failed it. A guard blind in exactly the place it is pointed.

Now AST-based, and three shapes are classified rather than skipped:

  * a function-local import is found wherever it sits
  * a relative import is reported as `.` so it can never read as stdlib
  * `tools.lint._scan_helpers` is allowed BY NAME, not by its root --
    permitting `tools` would admit anything that ever lands under it

Ten contrast controls added, because the sweep over the two real files is
clean and proves nothing on its own. The two that matter: the function-local
case, which passed before this change, and `tools.lint.other`, which must be
flagged or the dotted allowance would be a `tools` wildcard wearing a longer
name.

Mutation-verified, and this time I checked the mutation APPLIED before reading
the result -- my previous attempt injected after a line the file does not
contain, so it silently did nothing and the suite passed. With an INDENTED
`import requests` in `_comment_syntax`:

    AssertionError: _comment_syntax reaches beyond stdlib and autobot_shared: ['requests']

Reverted, 34 pass.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: git-hygiene Wave 0 · cluster Y — Git hygiene & stranded work bug Something isn't working infrastructure priority: high

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant