Skip to content

Evaluate every Bash script and bats suite: port to Go or Python, or optimize, with a 200 SLOC limit #203

Open
0 of 1 issue completed

Description

@tbhb-agent

Every Bash script and bats suite in this repository needs an evaluation. Each one gets ported to a Go command, ported to a Python script, or optimized and kept as Bash, and no Bash script stays longer than about 200 SLOC. Bash and bats are now the main drag on the test suites here and in the consumers. They're also the main source of the portability breakage found while landing v0.11 and v0.12.

Why

  • Size. On main there are 73 unique source scripts totalling about 6,990 SLOC (comments and blank lines excluded), plus about 5,270 lines of bats across 36 suites. The generator and APM deployment multiply every skill script into up to four copies (both harness packages, .claude/skills/, .agents/skills/), so the tree carries about 44,800 lines of .sh.
  • Portability. Most of the failures behind ci: run bats regression suites on Linux and macOS #190 were Bash and bats portability defects that merged unnoticed: GNU-only sed -i and regex alternation that break on BSD userland, [[ -v ]] and empty-array expansion that break on macOS's Bash 3.2 (fix(tasks): handle empty pre-approval scopes on bash 3.2 #197), tests that reached for rg, and tests that didn't pin resolution under MISE_LOCKED=1. Each one was a real defect that a typed language with a standard library would have prevented or caught at build time.
  • Test runtime. bats suites dominate test time here and in the consumers. One consumer's mise run test includes a bats file that takes about 15 minutes on an idle machine (agent-pr and agent-commit: reuse a dry run's gate passes on an unchanged tree #161). bats tests start real processes and scratch repositories for every case, so they're slow by construction and flaky under load.

Scripts over the limit

SLOC Script
402 .devtools/scripts/check-leaks.sh
375 packages/agents/source/skills/pr/shared/scripts/validate-description.sh
332 packages/agents/source/lib/lib.sh
284 packages/agents/source/skills/pr/shared/scripts/preflight.sh
269 packages/agents/source/skills/pr/shared/scripts/create-pr.sh
257 packages/agents/source/skills/fix-prose/shared/scripts/check-suppressions.sh
242 packages/agents/source/skills/rebase/shared/scripts/preflight.sh
213 packages/agents/source/skills/commit/shared/scripts/preflight.sh

The full ranked inventory goes in the evaluation itself. The next group, 100 to 200 SLOC, is about 25 scripts, among them rebase/verify-rebase.sh, tools/skill-tokens.sh, merge-pr/squash-merge.sh, merge-pr/preflight.sh, tools/test-guards.sh and commit/commit.sh.

Scope

  • Every .sh under packages/agents/source/, .devtools/scripts/, tools/ and scripts/ (the generated and deployed copies follow their source).
  • Every bats suite, including tests/test_helper.bash.
  • Inline bash in mise task bodies (mise.toml, .devtools/tasks/*.toml) longer than a few lines.
  • The same review for the consumer repositories' own scripts and suites (o11y, tailnet-policy, space-geese-project, devhub, vale-ai-tells), done after devtools so they can adopt whatever devtools ships instead of keeping their own copies.

Decision criteria

For each script, record its SLOC, what calls it (skill, hook, task, CI, consumer), its test coverage and suite runtime, and one decision:

  • Go command. For logic with real control flow, parsing, git or GitHub API work, or anything that has to behave the same on macOS, Linux and Windows. These belong in cmd/ (or the agent binary program in Move reusable agent tooling into released Go binaries #174), with table-driven tests, and ship as released binaries so consumers don't vendor source.
  • Python script. For text and report processing where Python's standard library fits better and there's no hot path, following the existing tools/*.py pattern (uv script headers, pytest, 100% branch coverage).
  • Keep as Bash, optimized. Only for thin glue: under about 200 SLOC, mostly sequencing other commands, with no parsing beyond trivial. Its bats tests shrink to the cases that need a real shell; everything else moves into unit tests of whatever the glue calls.

Acceptance

  • An inventory of every script and suite with SLOC, callers, runtime and the decision, committed under docs/ or the plan directory.
  • A lint gate in the shared payload that fails on any Bash script over 200 SLOC (comments and blanks excluded). No allowlist or baseline: pre-existing findings are never grandfathered in these projects, so the gate lands only once every script is under the limit, through porting, splitting, or optimizing.
  • A migration order that puts the portability-fragile and slow ones first (check-leaks.sh, validate-description.sh, the preflights, lib.sh), coordinated with Move reusable agent tooling into released Go binaries #174 so Go ports land in the binary program, not as one-off commands.
  • Measured test-time results before and after for this repository's mise run test, and the bats share of it.
  • Consumer follow-ups filed once devtools ships the replacements.

Related

#174 (moving reusable agent tooling into released Go binaries) is the natural home for the Go ports. #161 (reusing gate passes) addresses repeated test runs, not slow ones. #144 (ShellCheck SC2312) still applies to whatever stays Bash, so whatever stays Bash still has to clear #144's sweep.

Activity

  1. added
    change:featPR adds or changes a feature (Conventional Commit type feat)
    on Oct 17, 2026
  2. 14 remaining items

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

    No labels
    No labels

    Fields

    Priority

    Intangible

    Work type

    Planned

    Projects

    • Status
      Backlog

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions