Skip to content

Route ruleset_id()'s Per-Page Guard Through jqr - #1972

Merged
ptr727 merged 1 commit into
developfrom
feature/auto-1880
Sep 28, 2026
Merged

ptr727 merged 1 commit into
developfrom
feature/auto-1880

Conversation

@ptr727

@ptr727 ptr727 commented Sep 28, 2026 •

Copy link
Copy Markdown
Owner

Summary

Next in the #1123/#1234/#1246/#1247/#1253/#1254/#1880/#1881/#1882 chain.
repo-config/configure.sh's ruleset_id() guards its own single-fetch
assumption with a plain jq, whose output on a native Windows jq carries a
trailing \r that breaks the guard's own integer comparison.

The defect

if [ "$(jq 'length' <<<"$out")" -eq 100 ]; then

On a native Windows jq, the substitution yields 100\r. [ "100\r" -eq 100 ]
reaches bash's arithmetic comparison with a non-numeric string, which prints
integer expression expected to stderr and evaluates false. ['s non-zero
status here is the condition of an if, which bash never treats as a
triggering failure under set -e, so the guard silently reads as false at
exactly the boundary it exists to catch, and its own "100 rulesets returned"
stderr line never fires. A repo that has hit the 100-ruleset per_page cap
would then fall through to the single-fetch id lookup instead of aborting,
which can make apply_ruleset() create a duplicate ruleset by name.

Fix

Route the extraction through the existing jqr() helper
(jqr() { jq -r "$@" | sed $'s/\r$//'; }), matching the treatment the rest
of ruleset_id() already gets after #1253, and matching the existing
count="$(jqr 'length' <<<"$entries")" pattern elsewhere in this file. No
behavior change on a CR-free jq (Linux/macOS).

Verification

  • shellcheck and shfmt -d both clean on repo-config/configure.sh.
  • python3 scripts/prose_lint.py ., python3 scripts/repo_gate.py,
    python3 spec/validate.py, and python3 -m unittest discover -s tests
    (1770 tests) all pass.
  • A standalone probe (bash, no network) shims jq to append a trailing CR
    (the documented native-Windows behavior), then shows the unfixed
    jq 'length' extraction fails the -eq comparison with a silent
    "integer expression expected" (the guard never fires), while the
    jqr-routed extraction strips the CR and the guard fires correctly, and
    that a real (CR-free) jq is unaffected by the fix either way.
  • An adversarial local-review pass over this one-line diff found no defects
    introduced by it, and one pre-existing defect in the same guard's own
    failure-propagation (a non-array gh api response fails the guard open
    regardless of the CR), filed separately as ruleset_id()'s per_page Guard Fails Open on a Non-Array gh api Response #1971 rather than expanding
    this fix's scope.

This defect and its fix are Windows-only, and I cannot reproduce or verify
the actual Windows-jq CRLF behavior on this (Linux) host.
The verification
above proves the CR-stripping mechanism is correct and unchanged on
Linux/macOS; it does not and cannot prove the fix resolves the issue on a
real Windows host. That remains unverified here, same as
#1123/#1229/#1234/#1246/#1247/#1253/#1254.

Closes on promotion: #1880

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes
    • Ruleset lookups now handle responses at the result limit more reliably, avoiding decisions based on potentially incomplete results. When the limit is reached, the lookup stops instead of treating the response as complete. This improves the reliability of configuration checks in that scenario.

## Summary

Next in the #1123/#1234/#1246/#1247/#1253/#1254/#1880/#1881/#1882 chain.
`repo-config/configure.sh`'s `ruleset_id()` guards its own single-fetch
assumption with a plain `jq`, whose output on a native Windows jq carries a
trailing `\r` that breaks the guard's own integer comparison.

## The defect

```sh
if [ "$(jq 'length' <<<"$out")" -eq 100 ]; then
```

On a native Windows jq, the substitution yields `100\r`. `[ "100\r" -eq 100 ]`
reaches bash's arithmetic comparison with a non-numeric string, which prints
`integer expression expected` to stderr and evaluates false. `[`'s non-zero
status here is the condition of an `if`, which bash never treats as a
triggering failure under `set -e`, so the guard silently reads as false at
exactly the boundary it exists to catch, and its own "100 rulesets returned"
stderr line never fires. A repo that has hit the 100-ruleset per_page cap
would then fall through to the single-fetch id lookup instead of aborting,
which can make `apply_ruleset()` create a duplicate ruleset by name.

## Fix

Route the extraction through the existing `jqr()` helper
(`jqr() { jq -r "$@" | sed $'s/\r$//'; }`), matching the treatment the rest of
`ruleset_id()` already gets after #1253, and matching the existing
`count="$(jqr 'length' <<<"$entries")"` pattern elsewhere in this file. No
behavior change on a CR-free `jq` (Linux/macOS).

## Verification

- `shellcheck` and `shfmt -d` both clean on `repo-config/configure.sh`.
- `python3 scripts/prose_lint.py .`, `python3 scripts/repo_gate.py`,
  `python3 spec/validate.py`, and `python3 -m unittest discover -s tests`
  (1770 tests) all pass.
- A standalone probe (bash, no network) shims `jq` to append a trailing CR
  (the documented native-Windows behavior), then shows the unfixed
  `jq 'length'` extraction fails the `-eq` comparison with a silent
  "integer expression expected" (the guard never fires), while the
  `jqr`-routed extraction strips the CR and the guard fires correctly, and
  that a real (CR-free) `jq` is unaffected by the fix either way.

**This defect and its fix are Windows-only, and I cannot reproduce or verify
the actual Windows-jq CRLF behavior on this (Linux) host.** The verification
above proves the CR-stripping mechanism is correct and unchanged on
Linux/macOS; it does not and cannot prove the fix resolves the issue on a
real Windows host. That remains unverified here, same as
#1123/#1229/#1234/#1246/#1247/#1253/#1254.

Closes on promotion: #1880

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Copilot AI lite review requested due to automatic review settings September 28, 2026 05:10
@coderabbitai

coderabbitai Bot commented Sep 28, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository: ptr727/ProjectTemplate/.coderabbit.yaml

Review profile: CHILL

Plan: Advanced

Run ID: 9910dfef-f075-4974-8e5e-be9256bd10b8

📥 Commits

Reviewing files that changed from the base of the PR and between ec9995b and b9ca97d.

📒 Files selected for processing (1)
  • repo-config/configure.sh

Included review availability: This review used your included allowance. Your plan provides up to 1 included review per hour; 0 remain after this review.


📝 Walkthrough

Walkthrough

ruleset_id now uses jqr to count rulesets in the response. It still aborts when the response contains exactly 100 rulesets.

Changes

Ruleset lookup

Layer / File(s) Summary
Count rulesets in response
repo-config/configure.sh
ruleset_id uses jqr instead of invoking jq directly to count returned rulesets. The existing failure at 100 rulesets remains.

Priority: ⬇️ Low

Estimated code review effort: 1 (Trivial) | ~3 minutes

Merge Risk: ⚪ Minimal · up to b9ca9

The change addresses carriage returns in the ruleset count without introducing a new failure; the existing 100-item rejection is unchanged.

Architecture Summary

Architecture risk: 🔵 Low · up to b9ca9

The change affects 1 system.

Changed systems: repo-config

Architecture concerns
No architecture-level concerns identified.

Review details

Systems and components

  • observed — repo-config (service) was modified; 1 changed file maps to changed impact.

Before / after behavior

  • observed — Modified behavior in repo-config/configure.sh: ruleset_id now uses jqr to count the returned rulesets, replacing direct jq invocation; the existing failure at the 100-item cap is unchanged.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: routing the ruleset_id per-page guard through the existing jqr helper.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 1 functions across 1 files.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR

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.

@codecov

codecov Bot commented Sep 28, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
⚠️ Please upload report for BASE (develop@ec9995b). Learn more about missing BASE report.

Additional details and impacted files
@@            Coverage Diff             @@
##             develop    #1972   +/-   ##
==========================================
  Coverage           ?   55.41%           
==========================================
  Files              ?       16           
  Lines              ?     7247           
  Branches           ?        0           
==========================================
  Hits               ?     4016           
  Misses             ?     3231           
  Partials           ?        0           
Flag Coverage Δ
python-3.13 55.41% <ø> (?)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟢 Approval recommended

The change is a minimal, targeted swap from jq to the existing jqr() normalization helper in a guard path, with no functional change expected on CR-free platforms.

Review effort: Lite
Findings: None

What changed in this PR

This PR fixes a Windows-specific failure mode in repo-config/configure.sh where ruleset_id()'s per_page=100 guard could silently fail due to a native Windows jq emitting a trailing \r, causing the numeric -eq comparison to misbehave.

Changes:

  • Route the ruleset_id() length extraction through the existing jqr() helper so trailing \r is stripped before the integer comparison.
File Description
repo-config/​configure.sh Uses jqr() for the length guard to avoid CRLF-tainted numeric comparisons on Windows.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@ptr727

ptr727 commented Sep 28, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Sep 28, 2026 •

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@ptr727
ptr727 merged commit 6d8e7cf into develop Sep 28, 2026
11 checks passed
@ptr727
ptr727 deleted the feature/auto-1880 branch September 28, 2026 05:20
ptr727 added a commit that referenced this pull request Sep 28, 2026
…1988)

## Summary

`repo-config/configure.sh`'s `ruleset_id()` per_page guard fails open on
any non-array 2xx response from the rulesets API, not only the
CR-mangled
case #1880/#1972 fixed, because the guard's own comparison failure
inside
its `$(...)` subshell never reaches `set -e` (no `inherit_errexit`).

## The defect

On a non-array response, `jqr 'length'` prints nothing, so the
`[ "" -eq 100 ]` guard fails silently rather than tripping, and the
following `ids=` extraction likewise falls through empty rather than
aborting. `ruleset_id` returns 0, which both call sites read as "not
found": the apply site would create a duplicate ruleset by name, and the
check site would misreport "not found" instead of aborting.

## Fix

Validate `$out` is a JSON array (`jq_has 'type == "array"' <<<"$out"`,
matching the `entries` guard's own pattern) before either the per_page
guard or the `ids=` extraction reads it, aborting with a "could not read
live ruleset state" message on failure.

## Verification

- New `tests/test_configure_ruleset_id.py` lifts `ruleset_id()`'s own
lines and exercises it through a stubbed `gh`, invoked the way both real
  call sites invoke it (`id="$(ruleset_id ...)"`), which is what exposes
  the subshell-errexit defect.
- Confirmed by reverting the fix: every non-array-response case then
  returns exit 0 with empty stdout (the "not found" misread), and the
  pre-existing per_page-cap, gh-failure, and duplicate-name cases stay
  passing throughout.
- `shellcheck` and `shfmt -d` clean on `repo-config/configure.sh`.
- `python3 -m unittest discover -s tests` (1777 tests), `python3
  scripts/repo_gate.py`, and `python3 spec/validate.py` all pass.
- A local-strict-review pass over the full diff found no blocking
  defects. It flagged one comment-wording inaccuracy (fixed) and one
  pre-existing gap in the `ids=` extraction for a malformed array
  element, filed separately as #1987 rather than expanding this fix's
  scope.

Closes on promotion: #1971

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
ptr727 added a commit that referenced this pull request Sep 28, 2026
… and Setup Tooling (#2000)

## Summary

Promotes develop to main, carrying the 16 pull requests merged to
develop since #1943.

- **Wait-loop guard (requirement 7):**
[#1999](#1999) credits a
comparison bound only inside a `[`, `[[`, or `test` invocation, and
[#1996](#1996) reconciles
the requirement's README count and diagram with its hook.
- **Registry:**
[#1994](#1994) and
[#1976](#1976) record
Vantage-Config's line endings and description.
- **Scripts and tooling:**
- [#1988](#1988) and
[#1972](#1972) harden
`ruleset_id()`.
- [#1974](#1974) and
[#1949](#1949) fix
`pr_review.py` `reply --match` and `wait`.
- [#1969](#1969),
[#1964](#1964),
[#1954](#1954) and
[#1951](#1951) fix
host-setup tool shadowing, shims, hook ownership and dpkg ownership
checks.
- **Gates and audit:**
- [#1980](#1980) triages a
path collision.
- [#1967](#1967) and
[#1962](#1962) tighten
sha-pin and version-literal checks.
- [#1956](#1956) keeps a
folded `if:` visible to the interface audit.

Closes #1636
Closes #1639
Closes #1948
Closes #1971
Closes #1718
Closes #1934
Closes #1877
Closes #1880
Closes #1865
Closes #1889
Closes #1966
Closes #1906
Closes #1935
Closes #1901
Closes #1905
Closes #1866
Closes #1897

🤖 Generated with [Claude Code](https://claude.com/claude-code)
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.

2 participants