Skip to content

bug(hooks): gsd-validate-commit.sh 112KB padded-command row exceeds time bound on macOS CI (bash 3.2), reds next #5473

Open

Description

@trek-e

GSD Version

next @ fbd09ba (CI)

Runtime

Claude Code

Operating System

macOS (GitHub Actions macos-latest, Node 24, /bin/bash 3.2.57)

Node.js Version

v24

Shell

/bin/bash 3.2

Installation Method

Source checkout (CI)

What happened?

Tests on next is red after merging green PR #5468 (run 38894835669, job conformance test (macos-latest, 24)). tests/hooks-opt-in.test.cjs:1815 ("the suffix window still sees an option after a 112KB command") failed: the hooks/gsd-validate-commit.sh invocation was killed at the 10000 ms bound. #5468 touches no hook code. This recurs every few PRs.

Measured duration_ms of that row on the macOS conformance job (two hook invocations per row):

run padded row "112KB conforming" row
38893639881 5006 337
38879172769 5925 404
38879074054 5525 461
38894835669 (red) 10414 (killed) 416

The padded row costs ~12x a same-size unpadded row on the same runner. Passing runs already use 50-60% of the bound. scripts/run-tests.cjs runs 4 test files concurrently on macOS.

What did you expect?

The hook validates a 112KB command in bounded time that does not depend on runner load.

Steps to reproduce

Run the Tests workflow's macOS conformance job; the row fails intermittently.

Error output / logs

with trailing option: hook was killed at the 10000ms bound — the quadratic suffix scan is back (unfixed cost on this row is ~30s) (expected 'SIGKILL' notStrictEqual actual 'SIGKILL').

How often does this happen?

Intermittently, every few PRs.

Impact

Blocks the required Required tests gate on next.

Plan

Which hook phase is superlinear on whitespace-padded input under bash 3.2 is not yet identified. First step: a macOS-only CI diagnostic that times each phase of the hook on the padded input. Then a linear fix and a non-wall-clock regression test.

Activity

  1. added
    bugSomething isn't working
    needs-triageNew issue awaiting maintainer review
    on Oct 16, 2026
  2. added 14 commits that reference this issue on Oct 16, 2026
  3. reopened this on Oct 16, 2026
  4. trek-e commented on Oct 16, 2026

    @trek-e
    CollaboratorAuthor

    Reopening: #5476 merged the diagnostic only (its Fixes #5473 was required by the issue-link gate). The row still fails intermittently; nothing in the hook or the test was changed.

    What the merged diagnostic measured on macos-latest, bash 3.2.57 (run 38954101023):

    padded 112KB unpadded 112KB body ratio
    idle 199 ms median 196 ms 1.0x
    4 processes forking node 359 ms 403 ms 0.9x

    So the hook is NOT superlinear on whitespace-padded input under bash 3.2, and it is ~10-25x cheaper in isolation than the 2.5-5 s per invocation the conformance job shows for this row. The extra time appears only inside the real conformance tier.

    Next: measure inside the real macOS conformance job (same hook, same contention): the inherited test-runner environment vs a minimal one, and the test's own detached-process-group spawn, then a traced run under the tier's conditions.

  5. added 2 commits that reference this issue on Oct 16, 2026
  6. reopened this on Oct 16, 2026
  7. trek-e commented on Oct 16, 2026

    @trek-e
    CollaboratorAuthor

    Root cause found (measured inside the macOS conformance job, #5480, run 38969807931, job 117205238703; macos-latest, /bin/bash 3.2.57, 3 cpus, load average ~18).

    Row: padded + trailing --cleanup=verbatim (114750 bytes), same hook, one change at a time (2 runs each, ms):

    environment median
    inherited (what the real row passes; LC_ALL=en_US.UTF-8) 6344 (spawnSync) / 5841 (async + detached, as the real row)
    minimal env (PATH, HOME) 596
    LC_ALL=C 547
    LC_ALL=en_US.UTF-8 5603
    without NODE_* 5158
    without GSD_* / RUN_TESTS_* 6128

    The unpadded 112KB body row is ~0.4 s in every column. So: not the interpreter (the PATH bash IS /bin/bash 3.2.57), not the node/GSD environment, not the spawn mode. It is the locale: in a UTF-8 locale bash 3.2's pattern matching on a 112KB value is ~10x slower.

    The traced run under the same environment (4817 ms over 98 commands) puts 3.5 s of it in one place: hooks/gsd-validate-commit.sh 574-589, the RESOLVE=0 fallback (this row is RESOLVE=0 because of the trailing --cleanup=verbatim):

    SUBJECT="${MSG%%$'\n'*}"        # 112KB MSG, longest-suffix match of `\n*`
    

    The earlier standalone diagnostic (#5476) never reached this line: its padded rows exited 0 (the classifier took the fail-open path), where the real row exits 2.

    Fix: do the first-line extraction in the C locale (byte matching), which the LC_ALL=C cell shows brings the whole row to ~0.55 s.

  8. added 2 commits that reference this issue on Oct 16, 2026
  9. added a commit that references this issue on Oct 17, 2026
  10. reopened this on Oct 17, 2026
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

    bugSomething isn't workingneeds-triageNew issue awaiting maintainer review

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions