medlab / case

medlab

what got hurt, and what now fails loudly

↑ sublayer

2026-09-27 · fixed: no

An empty backup suffix makes `sed -i` portable

BehavioralEvolutionExample-Based TestsPre-Promotion Invariant Gates

now fails loudly a gate refuses any `sed -i` whose argument is not a `.bak` suffix, so both the GNU-only no-suffix form and the macOS-only empty-suffix form fail the suite before they reach CI

What broke

sed has two in-place-edit conventions. GNU sed -i takes a backup suffix attached to the flag (-i.bak); without one it edits in place and keeps no backup. BSD sed -i, the macOS version, takes the backup name as a separate argument; an empty one (-i '') means "in place, no backup."

A prior case found the GNU-only form (sed -i 'script' file) failing on BSD, and named sed -i.bak 'script' file && rm -f file.bak as the form both accept.

This repo's test suite carried the GNU-only form. Its fix used sed -i '', the BSD form.

How it showed

This repo's CI runner uses GNU sed. The rewritten test failed:

sed: can't read s/^supersedes:.*/supersedes: sound-claim/: No such file or directory

GNU sed reads -i '' as no suffix, then '' as the script, then the edit expression as a file name to open. No such file exists. No commit in the run touched the code under test; the only change was the sed rewrite.

What was watching

Example-based tests — the suite — was on the path and did not catch it. Its assertions run sed -i; nothing checks that the invocation is written in a form both implementations accept.

Pre-promotion invariant gates — CI — fired and blocked the merge. How long the non-portable form had been there is unknown; the wrong fix was caught the day it was proposed.

Where it sits

Behavioral: the suite's behavior depends on which sed the runner has, not on the code it tests. Evolution: the prior case already named the correct form, and this repo's fix used the other non-portable form instead. No check in the repo, or in any shared lint, refused either form before CI ran it.

The example-based tests sensor was present and blind, as in the prior case: a platform-specific tool call inside the test that exercises it. The pre-promotion invariant gates sensor fired. The missing sensor checks the sed -i invocation itself, not the suite's output.

What we did

The proposed fix (a pull request on the affected repo) uses sed -i '', which is not portable to GNU sed. It was returned to its author with the CI log line and the correct form: sed -i.bak 'script' file && rm -f file.bak. The correction is not yet merged, so this case is fixed: no.

What now fails loudly

  • A CI check greps each repo's scripts for sed -i and fails the build when

the argument is not a literal .bak suffix. Both the GNU-only form and the BSD-only form fail before a reviewer sees them.

  • The check's message states the portable form,

sed -i.bak 'script' file && rm -f file.bak, so a fixer is pointed at it directly.