Skip to content

hardcoded-path-check: Windows repo/user pattern trailing-separator requirement inverts detection (misses bare-path values, flags prose) #1093

Description

@kyle-sexton

Parent

Source: handoff-inbox item 20260723-014618-guardrails-hpp-win-repo-pattern-gaps (Finding 1), producer work-laptop (Windows/PowerShell), guardrails 0.12.1. Prior art: #918/#932 (pattern broadening), standards#250 (upstream mirror).

What to build

The Windows repo-checkout pattern body HPP_WIN_REPO_BODY (and the same shape in HPP_WIN_USER_BODY / HPP_ESCAPED_WIN_REPO_BODY) currently inverts detection:

  • The final (/|\\?) group requires a trailing separator after the child segment, so a real bare value at end of line — root = C:/Dev/GitHub — does NOT match (false negative on exactly the shape the guard exists to catch).
  • The segment class [^/\\$%<{~]+ permits spaces/hyphens/parentheses, so prose can satisfy the trailing-separator requirement by greedily consuming words until a later / appears. Reproduced: # C:/Projects/melodic - personal melodic-software repos (reference/reading only here) matches as C:/Projects/melodic - personal melodic-software repos (reference/ — a false positive reported as "Windows repo path detected".

Net: the hook blocks the wrong line while the actual violations pass clean.

The lib's own design comment states intent as "a match needs at least one child segment past the root" — but the trailing-separator implementation actually requires a child segment PLUS a separator (effectively a grandchild boundary), and the prose-safety rationale for it ("the segment class permits spaces") is defeated by the greedy-span false positive above. Fix the pattern to implement the documented intent:

  • Exclude whitespace from the child-segment character class (e.g. [^/\\$%<{~[:space:]]+), which kills the prose false positive on its own.
  • Replace the mandatory trailing separator with a right boundary that also accepts end-of-line/whitespace (POSIX ERE, no lookarounds — e.g. make the trailing group (/|\\{1,2}|$|[[:space:]]) or equivalent), so a bare X:/<root>/<child> value at EOL matches. A bare root itself (C:/Dev, C:\Users\Alice) must still NOT match — the child-segment requirement stays.
  • Sweep the sibling bodies in the same file for the same shape (HPP_WIN_USER_BODY, HPP_ESCAPED_WIN_REPO_BODY; assess HPP_MACOS_USER_BODY/HPP_LINUX_USER_BODY which share the trailing-/ + space-permitting class) and keep the file's design comments truthful about the new boundary semantics.
  • Keep the cheap pre-filter gate in hardcoded-path-patterns.sh a strict superset of the detailed patterns (widening the body must not out-run the gate).

Distribution constraint: machine-path-patterns.sh is a managed synced component from melodic-software/standards (components/path-detection/machine-path-patterns.sh, currently in sync). The pattern change must land in BOTH: a standards PR (precedent: standards#250) and the matching local copy here, byte-identical, or the next sync bot run reverts the fix.

Acceptance criteria

  • root = C:/Dev/GitHub and root = C:/Projects/melodic (bare value, EOL, no trailing slash) are flagged by HPP_WIN_REPO_BODY
  • The prose comment line # C:/Projects/melodic - personal melodic-software repos (reference/reading only here) does not produce a greedy prose-span match (flagging only a path token itself is acceptable)
  • Bare roots without a child segment (C:/Dev, C:\Users\Alice alone) still do not match
  • Existing separator variants still match: forward slash, single backslash, doubled (JSON-escaped) backslash, 8.3 short names (ALICE~1)
  • Regression cases added to hardcoded-path-check.test.sh for both observed lines; full suite passes
  • Upstream standards PR filed with the identical pattern file; local and upstream copies byte-identical after merge
  • shellcheck clean; guardrails version bump + CHANGELOG entry

Blocked by

None — can start immediately.

Metadata

Metadata

Assignees

Labels

priority: mediumReal value, no hard deadline; normal backlog flow.status: readyTriaged, unblocked, and fully specified; eligible to pick up.

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions