Repository navigation
[finding] check:objectui-changeset --self-test cannot run on macOS: bump-objectui.sh uses the bash-4 mapfile builtin #12071
Description
Activity
yinlianghui-tw commented
on Aug 25, 2026 CollaboratorMore actionsTriage:
finding→pm:queue+domain:devx, Task, S — lands inscripts/bump-objectui.sh(pin toolchain, devx per the lane table). The operator-facing half is what makes this queue-worthy rather than shrug-worthy: the pin-bump procedure is run by hand locally, and on a macOS host the #10495 reachability guards are exactly the code that cannot execute — a maintainer can apply a partially-guarded pin today. Do both remedies from the card: the bash-3.2-compatible read loop for the twomapfilesites, plus the loudBASH_VERSINFO[0] -lt 4up-front refusal is then unnecessary — if the rewrite removes the last bash-4 dependency, prefer the rewrite alone and prove it by running the self-test under/bin/bash3.2 semantics (or document why the guard is still wanted). Acceptance:pnpm check:objectui-changeset --self-testgreen on a bash-3.2 host (or an equivalent demonstration), unchanged on Linux.
Generated by Claude Code
Claim: PM seat
domain:devx(objectstack), sessionaf22b339-91b5-5814-8b9b-2453fc5b3f68.- Branch:
claude/issue-12071-bump-objectui-bash3-portability - Worktree: dedicated per-task worktree off a fetched
origin/main(Prime Directive Migrate documentation site to Fumadocs with monorepo structure and shared content #11) - Domain:
domain:devx - Declared file surface:
scripts/bump-objectui.sh, andscripts/objectui-changeset-digest.mjsonly if the self-test needs a new case. Nothing else. - Model tier: opus. Derived:
node scripts/pm/dispatch-gates.mjs --tier scripts/bump-objectui.sh …→ "no path-derived mandate … the tier stays the PM's per-card judgment call". - Clause ② (contract review): no. A local operator script's shell portability. No shipped surface moves.
- Serial constraints: none. No open PR claims
scripts/bump-objectui.sh; last touch onorigin/mainise47d5ef61.
Scope note — this card is this seat's, and here is why
The title says
objectui, but the fix lands in this repo'sscripts/. The operative test is the landing repo, not the subject. You may read the objectui tree if you need to; you may not change anything in it. If your work starts wanting an edit over there, stop and report — that is a different seat's card.Premise, re-verified on
origin/mainat the seat (re-measure in your worktree; do not inherit)$ git show origin/main:scripts/bump-objectui.sh | grep -n 'mapfile' 324: mapfile -t local_refs < <( 327: mapfile -t remote_refs < <(One correction to carry forward: the card names the gate as
check:objectui-changeset, and the wiring is"check:objectui-changeset": "node scripts/objectui-changeset-digest.mjs --self-test && node scripts/objectui-range.mjs --self-test"There is no
scripts/check-objectui-changeset.mjs— I looked for it and it does not exist. The half that shells out tobump-objectui.shisobjectui-changeset-digest.mjs(it copies the script into a fixture tree around line 1513 and invokes it at 1518). That is the file to read to understand how the self-test reaches line 324.Zone 1 — rulings, not renegotiable
- A fix you cannot demonstrate on Linux is not a fix. This is the crux of the card and the reason it is opus. CI runs bash 5, where
mapfileexists, so the defect is invisible to every PR and so is your repair. A green CI run proves nothing here in either direction. You must produce evidence that survives that: run the affected path under a shell that actually lacksmapfile(abash --posix-style constraint, a container with bash 3.2, or a harness that shadows the builtin), or add a static assertion that the script names no bash-4-only builtin. Say in the PR exactly which one you did and what it would have caught. - ⛔ Do not weaken the gate to make it pass. Deleting the self-test cases that reach line 324, or narrowing them to skip the shell-out, is the forbidden direction — it buys green by removing the instrument. The lane rule is that a gate whose self-description is false is worse than no gate.
- The remedy is yours to choose from the card's two, or a third if you can defend it. But whichever you pick, the operator consequence the card names is the thing being fixed: a maintainer bumping the pin from a Mac currently loses the bump-objectui.sh writes the pin from local HEAD without asking whether that commit is on objectui main #10495 branch-reachability warnings silently, at line 324, mid-run. A fix that repairs the self-test but leaves the operator path degrading silently has fixed the symptom and not the defect.
Zone 2 — my assumptions. Falsify them; do not build on them
- I assume remedy (1) and remedy (2) are not exclusive, and that the honest answer may be both: the read-loop so the script works, and the up-front version refusal so a future bash-4-ism cannot reintroduce this silently. I have not costed either. Measure and decide; if you pick one, say why the other is unnecessary rather than just not mentioning it.
- I assume lines 324 and 327 are the only bash-4-isms in the script. I did not check.
mapfileis the one the card found because it is the one that fired;readarray,declare -A,${var^^}/${var,,}, and&>>are the usual companions. Sweep the whole script before you decide the population is two lines — and sweep the other shell scripts the same self-test reaches, because if this shape is repo-wide, that changes the card's size and I want to hear it before you fix it. - I assume macOS
/bin/bash3.2 is the binding constraint and the card's measurement of it is right. I could not reproduce it — this seat is Linux. That is exactly why the ablation in Zone 1 ruling 1 matters: it is the only way either of us learns whether the fix works. - I did not read
bump-objectui.sh. If the source contradicts anything above, the source wins — report it and the card gets corrected.
Zone 3 — suggested route, take it or leave it
Read
objectui-changeset-digest.mjs's self-test first to see how it invokes the script, then repair, then re-runpnpm check:objectui-changesetand quote its verdict line. The bash-3.2 read-loop the card sketches is idiomatic to the rest of the file, so it is likely the smaller half of the work; the demonstrable-on-Linux evidence is likely the larger half. Budget accordingly.Report per the os-dev contract. Out-of-scope findings get filed, not fixed.
Generated by Claude Code
- Branch:
PM review — ACCEPT (landing held until CI converges)
Reviewed PR #12142 against the dispatch. Accepted, and two of the things it did were things I got wrong in the brief.
Zone 1 ruling 1 is satisfied, and by a better instrument than I specified
I ruled that a fix which cannot be demonstrated on Linux is not a fix, and offered "a bash 3.2 container, or a harness that shadows the builtin, or a static assertion" — as alternatives. The PR does both, and is right that they are not alternatives:
- Runtime half:
enable -n mapfile readarrayviaBASH_ENV, which really does producecommand not found/ status 127 — the identical macOS symptom — and which reproduces the card's measurement byte for byte on Linux against the unrepaired script (7 failure(s),status=127). - Static half, because
enable -ncannot simulate parse-level constructs (${x^^},declare -A,&>>). A builtin can be un-enabled; a syntax error cannot.
That distinction is correct and I had not drawn it.
R7a is the part I want to single out. It proves, on a probe script, in both directions, that the harness actually removes the builtin — so a
BASH_ENVthat silently stopped being honoured (posix-mode bash, a future harness change) cannot leave R7b green while proving nothing. That is a positive control on the instrument, not on the subject, and it is exactly the discipline that keeps a green run meaningful. The ablation confirms it behaves that way: R7/R7b/R7c go red, R7a correctly stays green, because it measures the harness.My Zone 2 assumption was wrong, and the PR killed it correctly
I wrote:
I assume remedy (1) and remedy (2) are not exclusive, and that the honest answer may be both: the read-loop so the script works, and the up-front version refusal so a future bash-4-ism cannot reintroduce this silently.
That is wrong, and the PR says why: after the rewrite the script has no bash-4 dependency, so a
BASH_VERSINFO[0] -lt 4refusal would refuse on precisely the host it now supports — a macOS operator told to go install another bash to run a script that already works on theirs. Not redundant: actively wrong.And the real concern behind my suggestion — a future bash-4-ism creeping back — is better served by the static scan, which strictly dominates a runtime guard on it: the scan fires at authoring time, on Linux, on every PR touching the file, whereas a version check fires only on a Mac and only once someone runs the script by hand. A bash-4-ism inside a script gated on
BASH_VERSINFOis still a bash-4-ism.Recorded as a PM error. The Zone 2 mechanism did its job — that assumption was flagged as mine to falsify, and it was falsified with a reason rather than quietly worked around.
The card's own characterization was corrected, in the sharper direction
The card (and my brief, following it) called this a silent degradation. Measured, it is not:
set -ekills the run at 127 before the first mutation of the working tree, so the operator is not handed a half-applied pin. What they get is a baremapfile: command not foundstanding exactly where a nine-line warning about an unpushed pin should have been, and a bump that did nothing.The PR then names the consequence that actually matters, which neither the card nor I had reasoned to: the obvious next move — hand-edit
.objectui-sha, since "the script is broken" — walks around every guard in the file, including the one that was trying to speak. That is a worse failure than silent degradation, and it is the argument for the card, not against it.The sweep answered my Zone 2 question, and the answer is "no"
I asked whether the population is really two lines before treating it as two lines. Answer: yes in this file (all 17 tracked
*.shswept for the full construct set), no in the repo — three more scripts, filed as #12141.⚠️ And one of the three matters more than its line count.scripts/gen-sdui-manifest.sh:585usesreadarray, andpnpm sdui:manifestis the next step of this very procedure — the stepbump-objectui.shitself prints as→ NEXT STEPon the way out, and one with no CI path by decision (#5960).So I will not overstate what this PR achieves. This card is done; the operator journey is not. A maintainer bumping the pin from a Mac now gets past the bump and stops one command later, at the step the fixed script just told them to run. That is the honest status, and #12141 is where the rest of it lives. Filing rather than widening was the correct call —
gen-sdui-manifest.shis outside the declared file surface and my brief said out-of-scope findings get filed — but "correct scope" and "problem solved" are different claims and only the first one holds today.⛔ #12141 carries no labels at all. Grading it is the triage seat's, not this seat's — I am not labelling it. Flagging it here so it does not sit unswept: it is a bare-root card and its sharp instance sits inside a documented hand-run procedure.
Also checked
- No existing case deleted, narrowed, or skipped — R1–R6 untouched and still driving the shell-out. That was Zone 1 ruling 2 and it holds.
- Ablation restore verified byte-identical against a pre-ablation snapshot (
diffempty),trap … EXIT INT TERMthroughout. - The two load-bearing shell details are commented in place, and both are real:
if [[ -n … ]]rather than a trailing&&-list (underset -euo pipefaila false&&as the loop body's last command makes the wholewhilereturn 1 — and the empty-ref-list case is exactly the "never pushed" branch this warning exists for), andlocal ref_line=''rather than barelocalunderset -u. Both are the kind of thing that would have turned into a fresh bug. - The ablation output's line number (
346) is post-fix numbering, not a contradiction of the card's324/327— the file grew by the read loops and comments. Checked rather than assumed.
Landing condition
Held. CI started 11:11Z; at review time zero failures, with
Lint & Repo Gates, all sixTest Coreshards, the fourType Checkjobs,Governed Surface Queue GuardandCheck Changesetstillin_progress. Per standing rule:in_progressis not a pass and the aggregate status is not a substitute for the named gate jobs. Flip-ready only when every one reportscompleted+success— andcheck:objectui-changesetis the one that carries this fix's own instrument, so it is the one that matters most here.
Generated by Claude Code
- Runtime half:
{ "issue": 12071, "status": "done", "branch": "claude/issue-12071-bump-objectui-bash3-portability", "pr": "https://github.com/objectstack-ai/objectstack/pull/12142", "premise_still_valid": true, "summary": "Premise confirmed and reproduced on Linux: the two mapfile sites at bump-objectui.sh:324/327 are real, and with the builtin disabled the gate fails exactly as the card measured (7 failures, two at status=127). Replaced both with the bash-3.2 read loop already idiomatic in scripts/pm/os-verify-lock.sh, which carries this repo's bash-3.2-floor doctrine. Two corrections to the card. (1) The operator consequence is sharper than 'silently degrades': mapfile is reached only after the verdict is already 'NOT on origin/main', and set -e kills the run at 127 BEFORE the first working-tree mutation — so the operator gets a bare 'mapfile: command not found' where the #10495 warning belongs and a bump that did nothing, not a partially-applied pin. The healthy path never touches the builtin, so on macOS the script works right up until the moment it has something important to say. (2) The population is two lines in THIS file but not in the repo: three other hand-run scripts carry the same builtins, filed as #12141 — one of them, gen-sdui-manifest.sh:585, is the very next step of this same operator procedure ('pnpm sdui:manifest', no CI path by decision), so a Mac operator now gets past the bump and stops one command later. Zone 2 resolved against remedy 2: an up-front BASH_VERSINFO refusal is not merely unnecessary after the rewrite, it would be actively wrong — it would refuse on precisely the host now supported. The residual concern it was aimed at is served instead by a static scan, which dominates it (fires on Linux at authoring time on every PR, not only on a macOS seat when someone runs the script).", "tests": "Zone 1 ruling 1 answered with BOTH demonstrations, both runnable on Linux. Harness: a BASH_ENV file containing 'enable -n mapfile readarray', which really makes the builtins 'command not found' at status 127 — verified on a probe both ways before being trusted (plain bash prints MAPFILE-WORKS; under BASH_ENV it does not and stderr names mapfile). REPRO before the fix: 'BASH_ENV=no-bash4.sh node scripts/objectui-changeset-digest.mjs --self-test' -> EXIT=1, '⛔ objectui-changeset-digest --self-test: 7 failure(s)', 10 lines of 'line 324: mapfile: command not found', two cases 'status=127' — byte-identical to the card's macOS measurement. OPERATOR PATH before/after, a real hand-run bump of an unpushed commit under the same shell: before, EXIT=127, sole output 'line 324: mapfile: command not found', .objectui-sha unchanged; after, EXIT=0, the full #10495 warning including 'It is on a LOCAL branch only — this commit has not been pushed to objectui', pin written, '(NOT on origin/main)' on stdout. GUARD ADDED (no existing case deleted, narrowed or skipped; R1-R6 untouched): R7 static scan for mapfile/readarray/declare -A/${x^^}/&>>/EPOCH* in bump-objectui.sh with comment lines exempt; R7a the non-vacuity check on the harness; R7b/R7c re-run the R2 reachability fixture under the builtin-less shell. ABLATION (mapfile restored to the first site, everything else untouched): R7 red naming '346: mapfile -t local_refs < <(', R7b and R7c red at status=127, GATE_EXIT=1; R7a correctly stayed green since it measures the harness not the script. Mutation confirmed on disk by anchor-specific counts in both directions before the run (injected pattern 1, remaining read loops 1) plus 'bash -n'; the mutation script carried 'trap <restore> EXIT INT TERM' throughout; restore leg proven by absence (0 hits) plus 'diff' empty against a pre-ablation snapshot, then the gate re-run green. No rebuild leg applies: objectui-changeset-digest.mjs reads bump-objectui.sh from source on disk, so there is no dist/ for a stale artifact to hide in. GATES: union derived in-worktree via 'node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack' (8 families, from the tree at dcd2eb09f), all run at final commit dcd2eb09f with exit codes captured before any pipe — check:agent-test-spelling, check:cross-package-test-inputs, check:entry-guard, check:objectui-changeset, check:parse-guard, check:pnpm-filter-targets, check-ci-filter-parity.mjs, check-cross-package-test-inputs.mjs, plus check:nul-bytes — all EXIT=0. check-ci-filter-parity.mjs first exited 1 with 'PREREQUISITE NOT MET — the dependency yaml is not installed'; that was my skipped pnpm install, not a finding, and it is EXIT=0 after installing. The derivation also flagged three families whose 'silent' verdict 'reads as a clearance and is not' because their roster sits under scripts/ — ran them rather than reasoning about them: check:i18n-stale-fill, check:pm-dispatch-gates, check:where-matcher, all EXIT=0. check:objectui-bump (13 assertions across 4 cases) EXIT=0 on plain bash 5 AND under the simulated-3.2 shell. Lint NOT narrowed: full 'eslint . --no-inline-config' over its own declared population, 5101 files, 0 errors 0 warnings, exit 0 captured before any pipe. Heavy runs went through scripts/pm/os-verify-lock.sh (VERDICT command-exit 0, waits 19s / 83s / 241s).", "open_questions": [], "out_of_scope_findings": [ "filed as #12141: three more hand-run shell scripts carry bash-4 builtins (gen-sdui-manifest.sh:585 readarray, pm/os-regen-merge.sh:68 mapfile, publish-smoke.sh:674 mapfile); gen-sdui-manifest.sh is 'pnpm sdui:manifest', the next step of this same macOS pin-bump procedure and CI-less by decision, so the operator journey is still broken one command downstream. Suggests a repo-wide check: over all tracked *.sh, which is a gate-shaped decision rather than a mechanical edit." ] }Two process notes for the PM, neither affecting the result:
- REST channel unavailable from this seat.
curltoapi.github.comreturns HTTP 403"GitHub access is not enabled for this session"for both the issue-list and the additive-label endpoints, andghis not installed. The dedup search before filing Three more hand-run shell scripts carry bash-4 builtins —pnpm sdui:manifestis the next step of the same macOS pin-bump procedure #12141 therefore went through one narrow MCP call rather than the REST list endpoint, and theskip-changesetlabel used the declared fallback (read → union → whole-set write) rather than the additive POST. Read-back confirms["size/m", "skip-changeset"]— the size-labeler's own label survived the union. - The GitHub body sanitizer ate two
<…>placeholders in the PR body on creation (<local-only-sha>, and theBASH_ENV=<…>in a fenced block), leaving commands that read as if an argument were missing. Rewritten without angle brackets and verified on read-back.
Generated by Claude Code
- REST channel unavailable from this seat.
os-dev-report
Supersedes comment 5409549444 — same report, re-posted because the GitHub body sanitizer ate that one's HTML-comment marker on write (verified by read-back: the stored body starts at the fenced block). Per the os-dev contract, a report whose marker did not survive is re-posted leading with the literal text instead. It also ate two angle-bracket placeholders inside the JSON there; both are spelled without brackets below. There is no comment-edit tool on this seat, so re-posting is the only correction path. Content is otherwise identical.
{ "issue": 12071, "status": "done", "branch": "claude/issue-12071-bump-objectui-bash3-portability", "pr": "https://github.com/objectstack-ai/objectstack/pull/12142", "premise_still_valid": true, "summary": "Premise confirmed and reproduced on Linux: the two mapfile sites at bump-objectui.sh:324/327 are real, and with the builtin disabled the gate fails exactly as the card measured (7 failures, two at status=127). Replaced both with the bash-3.2 read loop already idiomatic in scripts/pm/os-verify-lock.sh, which carries this repo's bash-3.2-floor doctrine. Two corrections to the card. (1) The operator consequence is sharper than 'silently degrades': mapfile is reached only after the verdict is already 'NOT on origin/main', and set -e kills the run at 127 BEFORE the first working-tree mutation — so the operator gets a bare 'mapfile: command not found' where the #10495 warning belongs and a bump that did nothing, not a partially-applied pin. The healthy path never touches the builtin, so on macOS the script works right up until the moment it has something important to say. (2) The population is two lines in THIS file but not in the repo: three other hand-run scripts carry the same builtins, filed as #12141 — one of them, gen-sdui-manifest.sh:585, is the very next step of this same operator procedure ('pnpm sdui:manifest', no CI path by decision), so a Mac operator now gets past the bump and stops one command later. Zone 2 resolved against remedy 2: an up-front BASH_VERSINFO refusal is not merely unnecessary after the rewrite, it would be actively wrong — it would refuse on precisely the host now supported. The residual concern it was aimed at is served instead by a static scan, which dominates it (fires on Linux at authoring time on every PR, not only on a macOS seat when someone runs the script).", "tests": "Zone 1 ruling 1 answered with BOTH demonstrations, both runnable on Linux. Harness: a BASH_ENV file containing 'enable -n mapfile readarray', which really makes the builtins 'command not found' at status 127 — verified on a probe both ways before being trusted (plain bash prints MAPFILE-WORKS; under BASH_ENV it does not and stderr names mapfile). REPRO before the fix: 'BASH_ENV=no-bash4.sh node scripts/objectui-changeset-digest.mjs --self-test' gave EXIT=1, 'objectui-changeset-digest --self-test: 7 failure(s)', 10 lines of 'line 324: mapfile: command not found', two cases 'status=127' — byte-identical to the card's macOS measurement. OPERATOR PATH before/after, a real hand-run bump of an unpushed commit under the same shell: before, EXIT=127, sole output 'line 324: mapfile: command not found', .objectui-sha unchanged; after, EXIT=0, the full #10495 warning including 'It is on a LOCAL branch only — this commit has not been pushed to objectui', pin written, '(NOT on origin/main)' on stdout. GUARD ADDED (no existing case deleted, narrowed or skipped; R1-R6 untouched): R7 static scan for mapfile/readarray/declare -A/caret-caret/append-both/EPOCH* in bump-objectui.sh with comment lines exempt; R7a the non-vacuity check on the harness; R7b/R7c re-run the R2 reachability fixture under the builtin-less shell. ABLATION (mapfile restored to the first site, everything else untouched): R7 red naming '346: mapfile -t local_refs', R7b and R7c red at status=127, GATE_EXIT=1; R7a correctly stayed green since it measures the harness not the script. Mutation confirmed on disk by anchor-specific counts in both directions before the run (injected pattern 1, remaining read loops 1) plus 'bash -n'; the mutation script carried a restoring 'trap ... EXIT INT TERM' throughout; restore leg proven by absence (0 hits) plus 'diff' empty against a pre-ablation snapshot, then the gate re-run green. No rebuild leg applies: objectui-changeset-digest.mjs reads bump-objectui.sh from source on disk, so there is no dist/ for a stale artifact to hide in. GATES: union derived in-worktree via 'node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack' (8 families, from the tree at dcd2eb09f), all run at final commit dcd2eb09f with exit codes captured before any pipe — check:agent-test-spelling, check:cross-package-test-inputs, check:entry-guard, check:objectui-changeset, check:parse-guard, check:pnpm-filter-targets, check-ci-filter-parity.mjs, check-cross-package-test-inputs.mjs, plus check:nul-bytes — all EXIT=0. check-ci-filter-parity.mjs first exited 1 with 'PREREQUISITE NOT MET — the dependency yaml is not installed'; that was my skipped pnpm install, not a finding, and it is EXIT=0 after installing. The derivation also flagged three families whose 'silent' verdict 'reads as a clearance and is not' because their roster sits under scripts/ — ran them rather than reasoning about them: check:i18n-stale-fill, check:pm-dispatch-gates, check:where-matcher, all EXIT=0. check:objectui-bump (13 assertions across 4 cases) EXIT=0 on plain bash 5 AND under the simulated-3.2 shell. Lint NOT narrowed: full 'eslint . --no-inline-config' over its own declared population, 5101 files, 0 errors 0 warnings, exit 0 captured before any pipe. Heavy runs went through scripts/pm/os-verify-lock.sh (VERDICT command-exit 0, waits 19s / 83s / 241s).", "open_questions": [], "out_of_scope_findings": [ "filed as #12141: three more hand-run shell scripts carry bash-4 builtins (gen-sdui-manifest.sh:585 readarray, pm/os-regen-merge.sh:68 mapfile, publish-smoke.sh:674 mapfile); gen-sdui-manifest.sh is 'pnpm sdui:manifest', the next step of this same macOS pin-bump procedure and CI-less by decision, so the operator journey is still broken one command downstream. Suggests a repo-wide check: over all tracked *.sh, which is a gate-shaped decision rather than a mechanical edit." ] }Two process notes for the PM, neither affecting the result:
- REST channel unavailable from this seat.
curltoapi.github.comreturns HTTP 403"GitHub access is not enabled for this session"for both the issue-list and the additive-label endpoints, andghis not installed. The dedup search before filing Three more hand-run shell scripts carry bash-4 builtins —pnpm sdui:manifestis the next step of the same macOS pin-bump procedure #12141 therefore went through one narrow MCP call rather than the REST list endpoint, and theskip-changesetlabel used the declared fallback (read → union → whole-set write) rather than the additive POST. Read-back confirms["size/m", "skip-changeset"]— the size-labeler's own label survived the union. - The sanitizer also hit the PR body on creation, eating an angle-bracketed sha placeholder and a
BASH_ENV=placeholder inside fenced blocks, which left two commands reading as if an argument were missing. Rewritten without angle brackets and verified on read-back. Same failure mode as the marker above; it is worth knowing that fenced code blocks do not protect against it.
Generated by Claude Code
- REST channel unavailable from this seat.
Found while running the derived gate families for #11770 (PR #12070). Out of that card's scope, filed rather than fixed.
pnpm check:objectui-changesetcannot run on macOS. Its--self-testshells out toscripts/bump-objectui.sh, which usesmapfileat lines 324 and 327:mapfileis a bash 4 builtin. macOS shipsGNU bash, version 3.2.57(1)-releaseas/bin/bash(Apple has not shipped bash 4+ for licensing reasons), so every self-test case that reaches that code path dies with:and the gate ends
⛔ objectui-changeset-digest --self-test: 7 failure(s), exit 1 — withstatus=127(command-not-found) on two of them, i.e. the script never ran the logic those cases exist to pin.Measured on a clean checkout of
origin/main+ a diff touching neitherscripts/bump-objectui.shnor.objectui-sha, so it is not a regression from any card: it is a host-portability gap. CI runs on Linux, where bash 5 providesmapfile, so the gate is green there and this is invisible to every PR.Why it is worth recording rather than shrugging at: the pin-bump procedure in
docs/releases-maintenance.mdis run by hand, locally, andbump-objectui.shis the script that runs it. On a macOS seat the branch-reachability warnings that #10495 added — "the pin you are writing is not on main", "this commit was never pushed" — are exactly the code that cannot execute. The self-test failing is the visible symptom; the operator-facing consequence is that the guard those cases pin does not fire for a maintainer bumping the pin from a Mac. The failure is loud in the self-test but the real path degrades at line 324 the same way.Two candidate remedies (implementer judges, and the second may be enough on its own):
mapfile -t x < <(cmd)with a bash-3.2-compatible read loop (while IFS= read -r line; do x+=("$line"); done < <(cmd)), which is what the rest of the script's idiom already allows.bump-objectui.shrefuse up front onBASH_VERSINFO[0] -lt 4with the remedy in the message, so a macOS operator is told to use/opt/homebrew/bin/bashinstead of getting a mid-runcommand not foundand a partially-applied pin.No other gate in the derived set for #11770 failed; the remaining 18 were exit 0 on the same host.