Skip to content

tests/fm-bearings-snapshot.test.sh: BSD sed 'a\' drops the trailing newline, corrupting the fixture backlog and aborting the suite at 28/42 on macOS #1242

Description

@ironerumi

What happens

tests/fm-bearings-snapshot.test.sh aborts at 28 of 42 assertions on macOS, under both Bash 5 and stock Bash 3.2:

not ok - restoring the SSHHIP child did not clear only its narrow warning: { ... }

bin/fm-fleet-snapshot.sh is behaving correctly. The fixture hands it a corrupted backlog.

Cause

Two fixture mutations use sed's a\ append with the appended text as the last thing in the script and no trailing newline — tests/fm-bearings-snapshot.test.sh:1667 and :1706:

  sed '/## In flight/a\
- [ ] unreadable-child - Submit App Store build (repo: sshhip) (kind: ship)' \
    "$sshhip/data/backlog.md" > "$sshhip/data/backlog.next"

BSD/macOS sed emits that appended text without a trailing newline, relying on the script's own newline that isn't there. GNU sed always terminates it. Replayed byte-exactly on macOS:

$ printf '## In flight\n## Queued\n- [ ] reviewer-decision - x\n' > /tmp/in.txt
$ sed '/## In flight/a\
- [ ] unreadable-child - Submit App Store build (repo: sshhip) (kind: ship)' /tmp/in.txt | od -c | tail -4
0000060        S   t   o   r   e       b   u   i   l   d       (   r   e
0000100    p   o   :       s   s   h   h   i   p   )       (   k   i   n
0000120    d   :       s   h   i   p   )   #   #       Q   u   e   u   e
0000140    d  \n   -       [       ]       r   e   v   i   e   w   e   r

(kind: ship)## Queued — the ## Queued header is glued onto the appended line and stops being a section header.

Downstream, reviewer-decision then parses as an in-flight held item instead of a queued one. bin/fm-fleet-snapshot.sh sets captain_actionable only when .state == "queued", so the record drops out of the captain-holds set, decisions_open comes back empty, and the snapshot falls through captain_decision to active_child_work. Every assertion after that point is testing a corrupted fixture.

This is also why CI is green: .github/workflows/ci.yml runs the Bearings assertions on ubuntu with GNU sed.

Reproduce

At 99533c5 on macOS:

bash tests/fm-bearings-snapshot.test.sh
Invocation Runs Result
unmodified, Bash 5.3.9 2 exit=1, 28 ok
unmodified, stock /bin/bash 3.2.57 1 exit=1, 28 ok

Expected: 42 ok, exit 0 (what CI sees on ubuntu).
Actual: aborts at 28 on any BSD-sed host.

Test-only counterfactual

Replacing only those two fixture appends with a portable awk, touching nothing under bin/:

-  sed '/## In flight/a\
-- [ ] unreadable-child - Submit App Store build (repo: sshhip) (kind: ship)' \
+  awk '{print} /## In flight/{print "- [ ] unreadable-child - Submit App Store build (repo: sshhip) (kind: ship)"}' \
     "$sshhip/data/backlog.md" > "$sshhip/data/backlog.next"

(and the same shape for the /unreadable-child/ append at :1667)

runB-fix1 (bash 5)    exit=0 ok=42
runB-fix2 (bash 5)    exit=0 ok=42
runB-fix3 (bash 3.2)  exit=0 ok=42

Proposed minimal fix

The two-line awk '{print} /pattern/{print "..."}' substitution above. It is portable across BSD and GNU, needs no GNU-sed dependency, and is byte-identical in output to what the test intends. An alternative — adding an explicit trailing newline inside the sed script — also works on BSD but is fragile to reformatting, so awk is the safer shape.

Duplicate search

No existing issue or PR reports this. Searched fm-bearings-snapshot, bearings snapshot test, BSD sed, sed portability, sed macOS, test fails macOS. Checked the file lists of every open Bash-3.2/macOS PR (#1200, #1196, #1125, #1117, #1003, #989) and of #1119 — none of them touches tests/fm-bearings-snapshot.test.sh.

Environment

  • firstmate 99533c5d7d3702050e6084429dddff6ea4fe1aa0
  • macOS 15.7.7, Darwin 24.6.0, arm64 — BSD sed
  • Bash 5.3.9 (Homebrew) and stock /bin/bash 3.2.57

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions