Skip to content

hook-utils: membership guard is 8.3 short-name blind, silently skipping in-project files on short-name volumes #1636

Description

@kyle-sexton

Context

lib/hook-utils.sh's hook::physical_path canonicalizes a path with realpath (falling back to readlink -f) before the CLAUDE_PROJECT_DIR membership comparison in hook::read_file_path. GNU realpath under Git Bash does not expand Windows 8.3 short names, so a short-form file_path fails the prefix match and the hook exits silently — no lint, no notice, no telemetry.

actionlint hit this and fixed only itself. Its CHANGELOG 0.6.0 (#1133) records: "a short-form file_path (the shape Claude Code's own scratchpad paths take) failed the prefix match and the hook exited silently — no lint, no notice, no telemetry... The hook now parses the path itself... the synced shared lib is untouched for consumers that need the guard."

So the shared lib is still 8.3-blind, and bash-format and markdown-format still consume it. Both document the out-of-project skip as deliberate defense-in-depth — which is correct for a genuinely out-of-project file, but a false negative on an IN-project file that merely arrives in short form is coverage loss, not defense.

Empirically scoped by volume (via cmd /c dir /x, which needs no elevation — fsutil 8dot3name query requires admin and returns "Error 5: Access is denied"):

  • C: generates short names — CAVEMA~1, CREDEN~1.JSO observed under C:\Users\KyleSexton\.claude.
  • D: does not — the short-name column is empty for every entry under a checkout there.

So the defect is live for repos on volumes that generate 8.3 names and dormant elsewhere. It is a per-volume property, not a per-plugin one — which is why it has stayed invisible: a contributor whose repos live on a non-generating volume cannot reproduce it.

Cost of leaving it: silent, telemetry-free lint gaps in two shipped formatter plugins for any consumer whose checkout sits on a short-name-generating volume.

Proposed work

  • Decide the shared-lib disposition: teach hook::physical_path to expand 8.3 short names on Windows hosts, or document the limitation at the function and have each consumer opt out as actionlint did. Prefer fixing the lib — three consumers have now met the same edge.
  • Apply the outcome to bash-format and markdown-format.
  • Add a regression test that exercises a short-form in-project path, skipping cleanly on volumes that do not generate short names (mirroring the existing conditional-skip pattern in plugins/markdown-format/hooks/markdown-format.test.sh, which already skips when its temp dir lands inside a git tree).
  • Record the volume-scoped nature so a contributor on a non-generating volume does not read the test skip as a pass.

Acceptance criteria

  • A short-form in-project file_path is linted rather than silently skipped, on a volume that generates 8.3 names.
  • A regression test covers the short-form case and skips cleanly (with a visible reason) where short names are unavailable.
  • bash-format and markdown-format either consume the fixed lib or carry a documented, deliberate opt-out.
  • The 8.3 limitation is documented wherever the guard's contract is stated, including that it is volume-scoped.
  • No behavior change for genuinely out-of-project files — the deliberate defense-in-depth skip is preserved.

References

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    priority: highSignificant impact, or blocks an imminent release; staff this cycle.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions