Skip to content

Reorder check.sh: run source-only checks before the build - #128

Merged
alexkroman merged 4 commits into
mainfrom
claude/dx-improvements-closed-prs-kzx9up
Aug 12, 2026
Merged

Reorder check.sh: run source-only checks before the build#128
alexkroman merged 4 commits into
mainfrom
claude/dx-improvements-closed-prs-kzx9up

Conversation

@alexkroman

Copy link
Copy Markdown
Collaborator

What & why

Reorders scripts/check.sh to run all source-only checks (formatting, linting, portability) before the multi-minute build steps (swift test, sanitizers, app build, UI tests, leaks). This is a pure reordering with no logic changes.

Why it matters: When a compile error occurs, the cheap checks are now reached and reported in the first red run instead of the second or third — and they arrive ~10 minutes sooner. A green run does identical total work either way; what changes is how much a red run tells you at once.

Evidence from the closed-PR audit in DX.md: PR #80 spent multiple runs on "three SwiftLint violations behind the earlier failures"; PR #124 burned three consecutive red runs on one compile error at a time. This reordering collapses those sequences.

Related changes:

  • Adds scripts/check-portability.sh to flag GNU-only shell idioms (e.g., sed -i without suffix, grep -P, readlink -f) that work on Linux but fail on BSD (macOS, CI). Catches the class of bug that PR Gate the Pages site's deployability in check.sh #116 paid for ("Fix the sitemap <loc> strip on BSD sed").
  • Adds DX.md: audit of 125 closed PRs identifying six rework classes and nine fixes ranked by impact. Documents the rationale for this and other improvements.
  • Updates AGENTS.md and .claude/skills/check/SKILL.md to reflect the new step order and explain why it matters.
  • Adds compile job to check.yml for fast-fail typecheck (~2 min vs ~11 min for full check), and format-patch job to publish the exact swift-format reflow as an artifact so authors don't have to reproduce it by hand.
  • Adds --self-test mode to check-portability.sh to verify every rule still matches its own probe (prevents silent rule death).

How it was tested

  • scripts/check.sh passes (macOS)
  • scripts/check.sh --portable passes (verified on Linux sandbox)
  • scripts/check-portability.sh --self-test passes (all 12 rules live)
  • check.yml jobs (compile, format-patch, check) pass on a test PR
  • I read AGENTS.md and this doesn't reintroduce anything deliberately removed
  • Docs updated: AGENTS.md, DX.md (new), .claude/skills/check/SKILL.md, CLAUDE.md

https://claude.ai/code/session_01LNQ3Jmz46Kjj7DaRJnNx5n

claude added 2 commits August 12, 2026 06:59
Audits all 125 closed PRs (127 pushed branches) for commit churn and repeated
red CI runs, since comment counts are near-zero here and discussion isn't where
the struggle shows up. Sorts the churn into six classes, each cited to the
commits or CI runs it came from, and ranks nine fixes by rework removed per
unit of work.

The finding the rest follows from: CI is the only compiler and the only
formatter, and check.sh runs its cheap source-only checks last, so a compile
error hides the mechanical findings until the next 11-minute run. PR #80's own
commit message names it -- "the three SwiftLint violations behind the earlier
failures".

Lands at the root beside the other tracked guides; docs/ is gitignored for
local notes, so a tracked deliverable can't live there.
…ity gate

Implements fixes 1-5 from DX.md. Together they target the two rework classes
the closed-PR audit found most expensive -- Swift/test compile errors and
formatting findings, both of which only CI can see, and both of which used to
arrive one red run at a time.

check.sh: source-only checks now run before anything that builds. swift-format
lint, swiftlint lint and every portable linter move above swift test; only
swiftlint analyze (reads the app build's compiler log) and periphery (runs its
own xcodebuild) stay behind it. A green run does identical total work either
way; a red one now reports everything mechanical in the first run instead of
the third. Verified a faithful move: every pre-existing line survives, the
analyze block only changed indentation on its way out of the nested if.

check.yml: two new advisory jobs, neither required, both able to fail only
where check already would. `compile` builds the engine and its tests and
nothing else, turning a broken test build into ~2 minutes of feedback instead
of ~11; dev-build now needs it, so a PR that doesn't compile stops paying for
an app build too. `format-patch` runs swift-format for real and publishes the
diff as a job summary and artifact, so the fix is `git apply` rather than a
blind reflow from a machine with no formatter on it. It holds no write token
and never pushes -- the auto-push variant races the author and is left as a
deliberate open decision in DX.md.

scripts/check-portability.sh: new gate for GNU-only shell idioms in scripts/
and .claude/hooks/, which run on BSD userland on a Mac and on CI but GNU in a
web sandbox. shellcheck reads both as correct shell, which is how PR #116's
BSD-sed sitemap strip shipped. Twelve rules, a `# portable-ok:` escape, and a
--self-test that asserts each rule still matches its own probe -- added
because the first draft split "pattern|advice" on the first |, truncating
every pattern containing an alternation, and eight of twelve rules silently
passed everything.

AGENTS.md: records the four Swift Testing traps CI has caught more than once
(#expect and mutating methods, #expect around rethrows needing try, per-file
Foundation imports, private(set) across a file split), and renumbers the
documented check.sh step order. The check skill and CLAUDE.md follow.

Verified with scripts/check.sh --portable (green) plus shellcheck, shfmt,
actionlint, zizmor, prettier and markdownlint. The Swift side is unverifiable
here -- no macOS toolchain -- so CI on macos-26 is the authority on the
reordered script and the two new jobs.
@github-actions

github-actions Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Dev build

Download Blurt.app — built from 7db5b10, Debug-Local,
ad-hoc signed. Expires in 14 days.

Installing it
cd ~/Downloads
unzip -o blurt-dev-build-pr-128.zip   # GitHub wraps every artifact in a zip
unzip -o Blurt-dev-7db5b10.zip
find Blurt.app -exec xattr -c {} +   # clear quarantine: xattr lost -r in macOS 12.3
rm -rf /Applications/Blurt.app && cp -R Blurt.app /Applications/
open -a Blurt

It is ad-hoc signed and not notarized: Gatekeeper refuses to open it until
the quarantine flag is cleared, and macOS treats it as a different app from a
released Blurt, so you have to re-grant Microphone, Accessibility, and Input
Monitoring. Reinstall the release DMG
when you are done reviewing.

Expect that re-grant once per dev build, including a second build of this
same PR. TCC pins an Accessibility grant to the signature that took it, and an
ad-hoc signature is just a hash of the binary, so every build is a new app as far
as tccd is concerned. Blurt clears the orphaned grant at launch, which is what
keeps the Accessibility step from getting stuck on a Blurt row that is switched on
and still denied. If you are coming from a build old enough to predate that,
clear the grant yourself once:

tccutil reset Accessibility dev.alex.blurt

CI caught what the local run could not: the gate scans `git ls-files`, so while
check-portability.sh was itself untracked it never scanned itself. The commit
made it visible and its twelve deliberately-bad probe lines became twelve
violations.

Marks the probes with the `# portable-ok:` escape the file already documents,
rather than exempting the whole file — a real GNU idiom in this script should
still fail, and it now would.

Two things the failure also exposed:

The three bare-command rules (timeout / md5sum / tac) required whitespace or
line start before the name, so CI flagged nine probes rather than twelve and
`$(md5sum f)` or `|tac` would have slipped past. Boundary widened to any
non-identifier character.

An untracked script is invisible to git ls-files, and a scan that skips the
file you just wrote reads exactly like a scan that approved it — the same shape
as the truncated-regex bug this file already carries a self-test for. The gate
now names untracked shell scripts as a note, so the empty input set announces
itself instead of passing quietly.

Verified with the file tracked this time: --self-test reports all 12 rules
live, the real scan covers 23 scripts clean, and check.sh --portable is green.
@alexkroman
alexkroman enabled auto-merge August 12, 2026 13:55
The audit's conclusions are already carried by the code and docs it produced —
the check.sh ordering rationale, the Swift Testing traps in AGENTS.md, the two
CI jobs, and the portability gate all explain themselves at the point of use.
The standalone report was the vehicle, not the deliverable.

Rewrites the three references so nothing points at a deleted file: the
ordering rationale in check.sh, the same in AGENTS.md's "What check.sh runs",
and the one in the check skill. Each now states the reason inline rather than
deferring to the report.
@alexkroman
alexkroman added this pull request to the merge queue Aug 12, 2026
Merged via the queue into main with commit bc3d33c Aug 12, 2026
12 checks passed
@alexkroman
alexkroman deleted the claude/dx-improvements-closed-prs-kzx9up branch August 12, 2026 14:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants