Skip to content

finding(devx): no bash-version floor gate here — this repo's own shell (scripts/, e2e/) is held to nothing, while the sibling repo gates every shell file at bash 3.2 #7692

Description

@claude

Found while fixing #7260 (the guard-main-checkout.sh path-key defect). Out of scope for that
card, which is confined to two hook files. Filed rather than fixed.

The gap

The sibling repo runs a gate that holds every tracked shell file to a bash 3.2 floor:

✓ check-bash32-floor: 27 tracked shell file(s) under scripts/**, .claude/hooks/**, .githooks/**
  name no bash 4+ construct outside a comment, a guarded ${VAR:-} read, or a non-command position.
  census: 25 by .sh extension, 2 by shebang alone; 19 constructs checked, floor bash 3.2.

This repo has no equivalent. Reading the check:* scripts in package.json and
grep -l '\.claude' scripts/*.mjs, the two that come closest are neither:
check:control-bytes scans tracked text files for control bytes, and
check:shell-escape-residue scans markdown fences in AGENTS.md, CLAUDE.md, skills,
.claude/skills and content/docs — its own verdict line reports "206 file(s) and 1307
fenced block(s)", i.e. documentation, not shell programs.

Why #7263 does not cover it

#7263 (upstream-port-pin.json covers only scripts/, so the .claude/hooks/** verbatim
ports have no drift gate) covers the ported half and, if fixed, would cover it well: every
one of this repo's eight .claude/hooks/*.sh files has a same-named counterpart in the
sibling repo, so pinning them to that repo inherits its bash floor transitively. Measured,
not assumed — the two hook directories were listed and compared, and the set difference is
empty.

What neither card reaches is this repo's own shell, which is not a port of anything:

e2e/live/ci/start-backend.sh
e2e/live/ci/stop-backend.sh
scripts/ensure-chromium-ready.sh
scripts/setup.sh

All four are in the sibling gate's declared scan roots (scripts/**, and e2e/** would need
adding), so in that repo they would be held to the floor. Here nothing holds them.

Severity: a ratchet, not a live defect — with a bounded reading

Grepping those four files for ten of the constructs the sibling gate names (declare -A,
local -A, ${var^^}, ${var,,}, mapfile, readarray, &>>, |&, globstar,
[[ -v , coproc) returns no match. So there is no violation to fix today.

⚠️ That reading is bounded and should not be quoted as a clean bill: the sibling gate checks
19 constructs and this grep covered ten of them, so "clean today" here means "clean under
a partial scan". The point of the card is the missing ratchet, not a present breakage —
scripts/setup.sh runs on contributor machines where /bin/bash is still 3.2 (macOS), which
is exactly the host the floor exists for, and it is the one file of the four whose shebang is
#!/bin/bash rather than #!/usr/bin/env bash.

Shape of the fix (not asserted)

Port scripts/check-bash32-floor.mjs from the sibling repo with this repo's scan roots
(scripts/**, .claude/hooks/**, e2e/**), wire it into the check:* scripts and the lint
workflow. The gate ships with its own self-test (153 cases in the sibling), so the port has a
red/green of its own.

Filed unassigned, finding.


Generated by Claude Code

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

    domain:devxobjectui devx stream: fix lands on .github/, scripts/ or release pipeline — devx lane cross-repoenhancementNew feature or requestfindingpriority:p3

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions