Skip to content

fix(scripts): shell-portability gate never scans skill markdown, where the shell is actually executed #2704

Description

@kyle-sexton

scripts/check-shell-portability.sh selects only **/*.sh in both CI-facing modes, so shell
snippets inside skill markdown are never scanned. Every plugin's skill docs are unscanned today.

Why that matters here

Skill markdown is not documentation about code — for a skill, the markdown IS the executable
surface
. An agent reads a shell snippet in a SKILL.md or a context/*.md and runs it. A GNU-only
construct there fails on macOS exactly as it would in a .sh file, and docs/PLUGIN-PHILOSOPHY.md:503
commits this repository to Windows, macOS, and Linux.

So the gate covers the surface where a portability defect is caught by a shell that errors loudly,
and skips the surface where it is executed by an agent that may improvise around the error instead.

Reproduction

Found live, not hypothesized. While implementing #2678, a draft of
plugins/review/skills/fanout/context/fix-pass-mode.md specified stat -c %Y — GNU-only; BSD/macOS
stat takes -f %m and errors on -c.

  • bash scripts/check-shell-portability.sh --paths plugins/review/skills/fanout/context/fix-pass-mode.md
    fires: PORTABILITY: plugins/review/skills/fanout/context/fix-pass-mode.md:26 ... stat -c
  • bash scripts/check-shell-portability.sh origin/main (the CI-facing diff mode)
    does not fire: No unexcused GNU-only constructs in 1 shell file(s).

The detector works. The selection is what misses.

  • Diff mode: git diff --name-only -- '*.sh'
  • --all mode: find -name '*.sh'
  • --paths mode accepts arbitrary files, which is the only reason this was caught at all — and only
    because it was run by hand.

CI would not have caught it. It was caught by an agent running the gate manually against a file
the gate does not select.

Scope of the gap

Every SKILL.md, context/*.md, and reference/*.md in the marketplace that carries a shell
snippet. Unknown how many currently contain GNU-only constructs — which is itself the point.

Why this is filed rather than fixed in the PR that found it

Widening the selection to include markdown would newly fail any existing skill doc carrying a
GNU-only snippet. That is a separate change with its own blast radius, and folding it into a
findings-coexistence PR would mix an unrelated repo-wide gate change into a contract change.

Suggested shape (owner's call)

  1. Measure first. Run the existing detector across all skill markdown with --paths and count
    the hits. That number decides whether this is a one-PR fix or a staged one.
  2. Widen the selection to skill markdown in both CI-facing modes.
  3. If the count is large, stage it — new and changed files first, backlog behind an allowlist that
    shrinks, rather than a flag day.

Note the detector needs no change; only which files it is pointed at.

Related

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

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions