From 41c224afffb9815e13425f13c30e8a4e101e2a1f Mon Sep 17 00:00:00 2001 From: Hermes Agent Date: Thu, 6 Aug 2026 15:39:33 -0500 Subject: [PATCH 1/4] feat(tools/git): explain tea's misleading `user does not exist` error `user does not exist [uid: 0, name: ]` from tea reads as a missing account. It almost always means a revoked or stale token: `tea login` keeps its own copy of the token, so rotating the credential store does not update it. The error points at the account; the cause is the cached credential. Adds explain_tea_user_does_not_exist() to detect-platform.sh and invokes it from the three tea-failure fallback paths, each guarded by `declare -F` so the call site is independent of load order and a no-op if the helper is absent. Purely additive: stderr only, no control flow, no gate or credential changes. Verified both directions: the helper emits when sourced, and the guarded call is a silent no-op (exit 1, no output) when it is not. Refs #1082 Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01Amf1Neca162odgcbCWMk1y --- .../mosaic/framework/tools/git/detect-platform.sh | 15 +++++++++++++++ .../mosaic/framework/tools/git/issue-create.sh | 1 + packages/mosaic/framework/tools/git/issue-view.sh | 1 + packages/mosaic/framework/tools/git/pr-create.sh | 1 + 4 files changed, 18 insertions(+) diff --git a/packages/mosaic/framework/tools/git/detect-platform.sh b/packages/mosaic/framework/tools/git/detect-platform.sh index a06452d1..5a78920c 100755 --- a/packages/mosaic/framework/tools/git/detect-platform.sh +++ b/packages/mosaic/framework/tools/git/detect-platform.sh @@ -246,6 +246,21 @@ PY } >&2 } +# Explain tea's most misleading failure. `user does not exist [uid: 0, name: ]` reads +# as a missing account; it almost always means a REVOKED OR STALE TOKEN. `tea login` +# keeps its OWN COPY of the token, so rotating the credential store does not update it. +# Diagnostic only -- stderr, no control flow, no exit. +explain_tea_user_does_not_exist() { + cat >&2 <<'MSG' +NOTE: `user does not exist [uid: 0, name: ]` from tea usually means a REVOKED OR STALE TOKEN, + not a missing account. A `tea login` stores its OWN COPY of the token; rotating the + credential store does NOT update it. + CHECK: the login's cached copy (`tea login list` -- read the FULL table, never `| head`), + then re-register that login against the current token. + DO NOT probe capability with a mutating request; a POST is the action, not a check. +MSG +} + get_gitea_login_for_host() { local host="${1:-}" local login diff --git a/packages/mosaic/framework/tools/git/issue-create.sh b/packages/mosaic/framework/tools/git/issue-create.sh index 96f890cf..5ab8f871 100755 --- a/packages/mosaic/framework/tools/git/issue-create.sh +++ b/packages/mosaic/framework/tools/git/issue-create.sh @@ -156,6 +156,7 @@ case "$PLATFORM" in exit 0 fi echo "Warning: tea issue create failed, trying Gitea API fallback..." >&2 + declare -F explain_tea_user_does_not_exist >/dev/null && explain_tea_user_does_not_exist fi gitea_issue_create_api ;; diff --git a/packages/mosaic/framework/tools/git/issue-view.sh b/packages/mosaic/framework/tools/git/issue-view.sh index eccab98e..aaf48ae6 100755 --- a/packages/mosaic/framework/tools/git/issue-view.sh +++ b/packages/mosaic/framework/tools/git/issue-view.sh @@ -71,6 +71,7 @@ elif [[ "$PLATFORM" == "gitea" ]]; then exit 0 fi echo "Warning: tea issue view failed, trying Gitea API fallback..." >&2 + declare -F explain_tea_user_does_not_exist >/dev/null && explain_tea_user_does_not_exist fi gitea_issue_view_api else diff --git a/packages/mosaic/framework/tools/git/pr-create.sh b/packages/mosaic/framework/tools/git/pr-create.sh index 46b82a64..380182ff 100755 --- a/packages/mosaic/framework/tools/git/pr-create.sh +++ b/packages/mosaic/framework/tools/git/pr-create.sh @@ -219,6 +219,7 @@ case "$PLATFORM" in exit 0 fi echo "Warning: tea pr create failed, trying Gitea API fallback..." >&2 + declare -F explain_tea_user_does_not_exist >/dev/null && explain_tea_user_does_not_exist gitea_pr_create_api ;; *) -- 2.54.0 From 97dbd1bf4a246c22eea4408197c49d59d255cc46 Mon Sep 17 00:00:00 2001 From: Mos Date: Thu, 6 Aug 2026 18:36:53 -0500 Subject: [PATCH 2/4] fix(tools/git): the tea diagnostic must not suppress the API fallback be-coder-08, reviewing #1086, found the diagnostic is not diagnostic-only. At all three call sites it was written as the last command of an && list: declare -F explain_tea_user_does_not_exist >/dev/null && explain_tea_user_does_not_exist and it sits immediately BEFORE the Gitea API fallback. Under `set -e` a failing diagnostic (stderr closed or full) therefore exits the script and the fallback never runs -- a diagnostic that suppresses the recovery path it exists to explain. The asymmetry is what makes it dangerous: the fault only appears when the helper is PRESENT, so the helper-absent path -- the pre-#1086 behaviour -- keeps working and reads as a passing control. Measured on /dev/full: helper present rc=1 fallback NOT reached helper absent rc=0 fallback reached Wrapping in `{ ...; } || true` makes the diagnostic status-neutral, which is what the PR claimed to be in the first place. test-explain-diagnostic-status-neutral.sh probes all four combinations of {helper present, absent} x {stderr ok, failing} for each of the three call sites, and lifts the construct FROM THE SHIPPED FILE rather than restating it -- a probe that retypes the fixed form passes on a build whose real call sites still carry the bare && form. Verified RED on the pre-fix tree (16 failures, behavioural half included) and GREEN here. Enumerated on test:framework-shell. Reported-by: be-coder-08 --- .../framework/tools/git/issue-create.sh | 2 +- .../mosaic/framework/tools/git/issue-view.sh | 2 +- .../mosaic/framework/tools/git/pr-create.sh | 2 +- .../test-explain-diagnostic-status-neutral.sh | 70 +++++++++++++++++++ packages/mosaic/package.json | 4 +- 5 files changed, 75 insertions(+), 5 deletions(-) create mode 100755 packages/mosaic/framework/tools/git/test-explain-diagnostic-status-neutral.sh diff --git a/packages/mosaic/framework/tools/git/issue-create.sh b/packages/mosaic/framework/tools/git/issue-create.sh index 5ab8f871..d9c0bc24 100755 --- a/packages/mosaic/framework/tools/git/issue-create.sh +++ b/packages/mosaic/framework/tools/git/issue-create.sh @@ -156,7 +156,7 @@ case "$PLATFORM" in exit 0 fi echo "Warning: tea issue create failed, trying Gitea API fallback..." >&2 - declare -F explain_tea_user_does_not_exist >/dev/null && explain_tea_user_does_not_exist + { declare -F explain_tea_user_does_not_exist >/dev/null && explain_tea_user_does_not_exist; } || true fi gitea_issue_create_api ;; diff --git a/packages/mosaic/framework/tools/git/issue-view.sh b/packages/mosaic/framework/tools/git/issue-view.sh index aaf48ae6..81c55965 100755 --- a/packages/mosaic/framework/tools/git/issue-view.sh +++ b/packages/mosaic/framework/tools/git/issue-view.sh @@ -71,7 +71,7 @@ elif [[ "$PLATFORM" == "gitea" ]]; then exit 0 fi echo "Warning: tea issue view failed, trying Gitea API fallback..." >&2 - declare -F explain_tea_user_does_not_exist >/dev/null && explain_tea_user_does_not_exist + { declare -F explain_tea_user_does_not_exist >/dev/null && explain_tea_user_does_not_exist; } || true fi gitea_issue_view_api else diff --git a/packages/mosaic/framework/tools/git/pr-create.sh b/packages/mosaic/framework/tools/git/pr-create.sh index 380182ff..bf60863e 100755 --- a/packages/mosaic/framework/tools/git/pr-create.sh +++ b/packages/mosaic/framework/tools/git/pr-create.sh @@ -219,7 +219,7 @@ case "$PLATFORM" in exit 0 fi echo "Warning: tea pr create failed, trying Gitea API fallback..." >&2 - declare -F explain_tea_user_does_not_exist >/dev/null && explain_tea_user_does_not_exist + { declare -F explain_tea_user_does_not_exist >/dev/null && explain_tea_user_does_not_exist; } || true gitea_pr_create_api ;; *) diff --git a/packages/mosaic/framework/tools/git/test-explain-diagnostic-status-neutral.sh b/packages/mosaic/framework/tools/git/test-explain-diagnostic-status-neutral.sh new file mode 100755 index 00000000..11dc6a3d --- /dev/null +++ b/packages/mosaic/framework/tools/git/test-explain-diagnostic-status-neutral.sh @@ -0,0 +1,70 @@ +#!/bin/bash +# Regression: the tea-failure diagnostic must be STATUS-NEUTRAL. +# +# Found by be-coder-08 reviewing PR #1086. At all three call sites the diagnostic is +# emitted immediately BEFORE the Gitea API fallback. Written as +# declare -F explain_... >/dev/null && explain_... +# it is the last command of an && list, so under `set -e` a FAILING diagnostic exits the +# script and the fallback never runs -- a diagnostic that suppresses the recovery path it +# exists to explain. Worse, it only misbehaves when the helper is PRESENT, so the +# helper-absent path (the pre-#1086 behaviour) silently acts as the passing control. +# +# The control is the point: helper-absent MUST reach the fallback, and helper-present MUST +# reach it too. A test asserting only "helper-present reaches fallback" would pass on a +# build where the diagnostic never ran at all. +set -uo pipefail +fail=0 +GIT_DIR_UNDER_TEST="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +probe() { # $1=present|absent $2=stderr path $3=source file to lift the construct from + local helper="$1" errto="$2" src="$3" + local CONSTRUCT + CONSTRUCT=$(grep -m1 "explain_tea_user_does_not_exist" "$GIT_DIR_UNDER_TEST/$src" | sed "s/^[[:space:]]*//") + [ -n "$CONSTRUCT" ] || { echo "no-construct-found"; return; } + export CONSTRUCT + local out rc + out=$( + set -e + explain_tea_user_does_not_exist() { echo "diagnostic" >&2; } + [ "$helper" = absent ] && unset -f explain_tea_user_does_not_exist + # THE CONSTRUCT IS EXTRACTED FROM THE SHIPPED FILE, NOT RETYPED HERE. + # Retyping it would make this probe correct-by-construction: it would pass on a + # build whose real call sites still carry the bare && form. $CONSTRUCT is set by + # the caller from the actual source line. + eval "$CONSTRUCT" + echo "FALLBACK_REACHED" + ) 2>"$errto" + rc=$? + printf '%s:%s' "$rc" "$(grep -qc FALLBACK_REACHED <<<"$out" && echo yes || echo no)" +} + +check() { # $1=label $2=actual $3=expected + if [ "$2" = "$3" ]; then echo " PASS $1 ($2)"; else echo " FAIL $1: got $2, want $3"; fail=1; fi +} + +echo "== diagnostic must not alter exit status or skip the fallback ==" +# /dev/full makes every stderr write fail -- the real-world shape is a closed/full fd. +# Each call site is probed with the construct lifted from ITS OWN source file. +for src in pr-create.sh issue-view.sh issue-create.sh; do + check "$src stderr OK / helper present" "$(probe present /dev/null "$src")" "0:yes" + check "$src stderr OK / helper absent " "$(probe absent /dev/null "$src")" "0:yes" + check "$src stderr FAILING / helper present" "$(probe present /dev/full "$src")" "0:yes" + check "$src stderr FAILING / helper absent " "$(probe absent /dev/full "$src")" "0:yes" +done + +echo "== all three call sites use the status-neutral form ==" +for f in pr-create.sh issue-view.sh issue-create.sh; do + p="$GIT_DIR_UNDER_TEST/$f" + if grep -q '{ declare -F explain_tea_user_does_not_exist >/dev/null && explain_tea_user_does_not_exist; } || true' "$p"; then + echo " PASS $f guarded" + else + echo " FAIL $f: diagnostic is not status-neutral"; fail=1 + fi + # bare form must be gone entirely + if grep -qE '^\s*declare -F explain_tea_user_does_not_exist >/dev/null && explain_tea_user_does_not_exist\s*$' "$p"; then + echo " FAIL $f: bare && form still present"; fail=1 + fi +done + +[ "$fail" -eq 0 ] && echo "OK diagnostic is status-neutral" || echo "FAILED" +exit "$fail" diff --git a/packages/mosaic/package.json b/packages/mosaic/package.json index 7096794d..0b3bc712 100644 --- a/packages/mosaic/package.json +++ b/packages/mosaic/package.json @@ -6,7 +6,7 @@ "url": "https://git.mosaicstack.dev/mosaicstack/stack.git", "directory": "packages/mosaic" }, - "description": "Mosaic agent framework — installation wizard and meta package", + "description": "Mosaic agent framework \u2014 installation wizard and meta package", "type": "module", "main": "dist/index.js", "types": "dist/index.d.ts", @@ -25,7 +25,7 @@ "lint": "eslint src", "typecheck": "tsc --noEmit", "test": "vitest run --passWithNoTests && pnpm run test:framework-shell", - "test:framework-shell": "bash framework/tools/quality/scripts/check-test-enumeration.sh && bash framework/tools/quality/scripts/test-check-test-enumeration.sh && python3 src/lease-broker/daemon_deadline_unittest.py && python3 src/lease-broker/normative_fragments_unittest.py && python3 src/lease-broker/receipt_challenge_unittest.py && python3 src/lease-broker/context_recovery_unittest.py && python3 src/lease-broker/recovery_runtime_unittest.py && python3 src/lease-broker/recovery_b1_adversarial_unittest.py && python3 src/lease-broker/framework_skill_portability_unittest.py && python3 src/mutator-gate/runtime_tools_unittest.py && python3 src/mutator-gate/runtime_launch_guard_unittest.py && python3 src/mutator-gate/version_coupling_unittest.py && python3 framework/tools/lease-broker/check-runtime-launches.py --root ../.. && bash framework/tools/codex/test-pr-diff-context.sh && bash framework/tools/qa/test-deps-preflight.sh && bash framework/tools/git/test-pr-review-gitea-comment.sh && bash framework/tools/git/test-pr-review-repo-host-override.sh && bash framework/tools/git/test-ci-queue-wait-branch-absent.sh && bash framework/tools/git/test-ci-queue-wait-tristate.sh && bash framework/tools/git/test-ci-queue-wait-github-checks.sh && bash framework/tools/git/test-pr-merge-queue-branch.sh && bash framework/tools/git/test-pr-merge-head-pin.sh && bash framework/tools/git/test-pr-merge-message-field.sh && bash framework/tools/git/test-git-credential-mosaic.sh && bash framework/tools/git/test-gitea-token-identity.sh && bash framework/tools/git/test-detect-platform-outside-repo.sh && bash framework/tools/woodpecker/test-terminal-green-contract.sh && bash framework/tools/_scripts/test-install-ordering-guard.sh && bash framework/tools/tmux/agent-send.test.sh && bash framework/tools/wake/test-wake-store-ack.sh && bash framework/tools/wake/test-wake-store-enqueue-race.sh && bash framework/tools/wake/test-wake-digest-hmac.sh && bash framework/tools/wake/test-wake-digest-quarantine.sh && bash framework/tools/wake/test-wake-detector.sh && bash framework/tools/wake/test-wake-fn-oracle.sh && bash framework/tools/wake/test-wake-reconcile.sh && bash framework/tools/wake/test-wake-beacon.sh && bash framework/tools/wake/test-wake-preimage.sh && bash framework/tools/wake/test-wake-install.sh" + "test:framework-shell": "bash framework/tools/quality/scripts/check-test-enumeration.sh && bash framework/tools/quality/scripts/test-check-test-enumeration.sh && python3 src/lease-broker/daemon_deadline_unittest.py && python3 src/lease-broker/normative_fragments_unittest.py && python3 src/lease-broker/receipt_challenge_unittest.py && python3 src/lease-broker/context_recovery_unittest.py && python3 src/lease-broker/recovery_runtime_unittest.py && python3 src/lease-broker/recovery_b1_adversarial_unittest.py && python3 src/lease-broker/framework_skill_portability_unittest.py && python3 src/mutator-gate/runtime_tools_unittest.py && python3 src/mutator-gate/runtime_launch_guard_unittest.py && python3 src/mutator-gate/version_coupling_unittest.py && python3 framework/tools/lease-broker/check-runtime-launches.py --root ../.. && bash framework/tools/codex/test-pr-diff-context.sh && bash framework/tools/qa/test-deps-preflight.sh && bash framework/tools/git/test-pr-review-gitea-comment.sh && bash framework/tools/git/test-pr-review-repo-host-override.sh && bash framework/tools/git/test-ci-queue-wait-branch-absent.sh && bash framework/tools/git/test-ci-queue-wait-tristate.sh && bash framework/tools/git/test-ci-queue-wait-github-checks.sh && bash framework/tools/git/test-pr-merge-queue-branch.sh && bash framework/tools/git/test-pr-merge-head-pin.sh && bash framework/tools/git/test-pr-merge-message-field.sh && bash framework/tools/git/test-git-credential-mosaic.sh && bash framework/tools/git/test-gitea-token-identity.sh && bash framework/tools/git/test-explain-diagnostic-status-neutral.sh && bash framework/tools/git/test-detect-platform-outside-repo.sh && bash framework/tools/woodpecker/test-terminal-green-contract.sh && bash framework/tools/_scripts/test-install-ordering-guard.sh && bash framework/tools/tmux/agent-send.test.sh && bash framework/tools/wake/test-wake-store-ack.sh && bash framework/tools/wake/test-wake-store-enqueue-race.sh && bash framework/tools/wake/test-wake-digest-hmac.sh && bash framework/tools/wake/test-wake-digest-quarantine.sh && bash framework/tools/wake/test-wake-detector.sh && bash framework/tools/wake/test-wake-fn-oracle.sh && bash framework/tools/wake/test-wake-reconcile.sh && bash framework/tools/wake/test-wake-beacon.sh && bash framework/tools/wake/test-wake-preimage.sh && bash framework/tools/wake/test-wake-install.sh" }, "dependencies": { "@mosaicstack/brain": "workspace:*", -- 2.54.0 From 6f1924f32b9a1ff006a69e8abaa17e8b8573484d Mon Sep 17 00:00:00 2001 From: Mos Date: Thu, 6 Aug 2026 18:53:26 -0500 Subject: [PATCH 3/4] test(tools/git): make the status-neutral probe actually exercise the shipped line be-coder-08 found two defects in the regression test I added with the fix. The source fix is unaffected -- it re-confirmed the blocker closed -- but the test was not measuring what it claimed. 1. `out=$( ... ) 2>"$errto"` applies the redirection to the ASSIGNMENT, not to the command substitution, so the probe's stderr was never pointed at /dev/full and every /dev/full row proved nothing. Verified directly: out=$( echo x >&2 ) 2>/dev/full # leaks to the terminal, rc=0 out=$( { echo x >&2 ; } 2>/dev/full ) # rc=1 The redirect has to be inside the substitution. 2. `eval "$CONSTRUCT"` changes `set -e` semantics for a bare && list, so the probe did not execute the construct the way the shipped file does. It now writes the lifted line into a real script and runs it: same parse, same set -e rules, no eval. Consequence of (1)+(2): run against pre-fix source, the old test failed the six helper-ABSENT rows and passed every helper-PRESENT row -- inverted, and exactly backwards from the defect. It would have gone green on a broken tree for the wrong reason. The construct is still lifted from the shipped file rather than retyped. Faithful control, matching be-coder-08's prediction exactly: fixed source: all 12 behavioural rows 0:yes pre-fix source: 1:no on helper-present + failing-stderr ONLY (3 rows, one per call site); all 9 other rows 0:yes Reported-by: be-coder-08 --- .../test-explain-diagnostic-status-neutral.sh | 80 +++++++++---------- 1 file changed, 37 insertions(+), 43 deletions(-) diff --git a/packages/mosaic/framework/tools/git/test-explain-diagnostic-status-neutral.sh b/packages/mosaic/framework/tools/git/test-explain-diagnostic-status-neutral.sh index 11dc6a3d..9cc831ef 100755 --- a/packages/mosaic/framework/tools/git/test-explain-diagnostic-status-neutral.sh +++ b/packages/mosaic/framework/tools/git/test-explain-diagnostic-status-neutral.sh @@ -1,50 +1,51 @@ #!/bin/bash # Regression: the tea-failure diagnostic must be STATUS-NEUTRAL. # -# Found by be-coder-08 reviewing PR #1086. At all three call sites the diagnostic is -# emitted immediately BEFORE the Gitea API fallback. Written as +# Found by be-coder-08 reviewing PR #1086. At all three call sites the diagnostic is emitted +# immediately BEFORE the Gitea API fallback. Written as the last command of an && list: # declare -F explain_... >/dev/null && explain_... -# it is the last command of an && list, so under `set -e` a FAILING diagnostic exits the -# script and the fallback never runs -- a diagnostic that suppresses the recovery path it -# exists to explain. Worse, it only misbehaves when the helper is PRESENT, so the -# helper-absent path (the pre-#1086 behaviour) silently acts as the passing control. +# under `set -e` a FAILING diagnostic exits and the fallback never runs -- a diagnostic that +# suppresses the recovery path it exists to explain. It misbehaves ONLY when the helper is +# PRESENT, so the helper-absent path (pre-#1086 behaviour) keeps working and reads as a +# passing control. # -# The control is the point: helper-absent MUST reach the fallback, and helper-present MUST -# reach it too. A test asserting only "helper-present reaches fallback" would pass on a -# build where the diagnostic never ran at all. +# TWO DEFECTS IN THE FIRST VERSION OF THIS TEST, both found by be-coder-08: +# 1. `out=$( ... ) 2>"$errto"` applies the redirection to the ASSIGNMENT, not to the +# command substitution, so the probe's stderr was never actually pointed at /dev/full +# and the /dev/full rows proved nothing. Verified: `out=$(echo x >&2) 2>/dev/full` +# leaks to the terminal and returns 0; the redirect must be INSIDE the substitution. +# 2. `eval "$CONSTRUCT"` changes `set -e` semantics for a bare && list, so the probe did +# not exercise the construct as the shipped file executes it. It now writes the line +# into a real script and runs it -- same parse, same set -e rules, no eval. +# The construct is still LIFTED FROM THE SHIPPED FILE: retyping the fixed form makes the +# probe pass on a build whose real call sites still carry the bare && form. set -uo pipefail fail=0 GIT_DIR_UNDER_TEST="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +TMP="$(mktemp -d)"; trap 'rm -rf "$TMP"' EXIT -probe() { # $1=present|absent $2=stderr path $3=source file to lift the construct from - local helper="$1" errto="$2" src="$3" - local CONSTRUCT - CONSTRUCT=$(grep -m1 "explain_tea_user_does_not_exist" "$GIT_DIR_UNDER_TEST/$src" | sed "s/^[[:space:]]*//") - [ -n "$CONSTRUCT" ] || { echo "no-construct-found"; return; } - export CONSTRUCT - local out rc - out=$( - set -e - explain_tea_user_does_not_exist() { echo "diagnostic" >&2; } - [ "$helper" = absent ] && unset -f explain_tea_user_does_not_exist - # THE CONSTRUCT IS EXTRACTED FROM THE SHIPPED FILE, NOT RETYPED HERE. - # Retyping it would make this probe correct-by-construction: it would pass on a - # build whose real call sites still carry the bare && form. $CONSTRUCT is set by - # the caller from the actual source line. - eval "$CONSTRUCT" - echo "FALLBACK_REACHED" - ) 2>"$errto" - rc=$? - printf '%s:%s' "$rc" "$(grep -qc FALLBACK_REACHED <<<"$out" && echo yes || echo no)" +probe() { # $1=present|absent $2=stderr target $3=source file -> "rc:fallback" + local helper="$1" errto="$2" src="$3" construct script out rc + construct=$(grep -m1 'explain_tea_user_does_not_exist' "$GIT_DIR_UNDER_TEST/$src" | sed 's/^[[:space:]]*//') + [ -n "$construct" ] || { printf 'no-construct:no'; return; } + script="$TMP/probe.sh" + { + echo '#!/bin/bash' + echo 'set -e' + echo 'explain_tea_user_does_not_exist() { echo "diagnostic" >&2; }' + [ "$helper" = absent ] && echo 'unset -f explain_tea_user_does_not_exist' + echo "$construct" # the shipped line, parsed by a real shell + echo 'echo FALLBACK_REACHED' + } > "$script" + # redirect INSIDE the substitution so the subshell's stderr really is $errto + out=$( bash "$script" 2>"$errto" ); rc=$? + printf '%s:%s' "$rc" "$(grep -q FALLBACK_REACHED <<<"$out" && echo yes || echo no)" } -check() { # $1=label $2=actual $3=expected - if [ "$2" = "$3" ]; then echo " PASS $1 ($2)"; else echo " FAIL $1: got $2, want $3"; fail=1; fi -} +check() { if [ "$2" = "$3" ]; then echo " PASS $1 ($2)"; else echo " FAIL $1: got $2, want $3"; fail=1; fi; } echo "== diagnostic must not alter exit status or skip the fallback ==" -# /dev/full makes every stderr write fail -- the real-world shape is a closed/full fd. -# Each call site is probed with the construct lifted from ITS OWN source file. +# /dev/full makes every stderr write fail -- the real-world shape is a closed or full fd. for src in pr-create.sh issue-view.sh issue-create.sh; do check "$src stderr OK / helper present" "$(probe present /dev/null "$src")" "0:yes" check "$src stderr OK / helper absent " "$(probe absent /dev/null "$src")" "0:yes" @@ -55,15 +56,8 @@ done echo "== all three call sites use the status-neutral form ==" for f in pr-create.sh issue-view.sh issue-create.sh; do p="$GIT_DIR_UNDER_TEST/$f" - if grep -q '{ declare -F explain_tea_user_does_not_exist >/dev/null && explain_tea_user_does_not_exist; } || true' "$p"; then - echo " PASS $f guarded" - else - echo " FAIL $f: diagnostic is not status-neutral"; fail=1 - fi - # bare form must be gone entirely - if grep -qE '^\s*declare -F explain_tea_user_does_not_exist >/dev/null && explain_tea_user_does_not_exist\s*$' "$p"; then - echo " FAIL $f: bare && form still present"; fail=1 - fi + grep -q '{ declare -F explain_tea_user_does_not_exist >/dev/null && explain_tea_user_does_not_exist; } || true' "$p" \ + && echo " PASS $f guarded" || { echo " FAIL $f: diagnostic is not status-neutral"; fail=1; } done [ "$fail" -eq 0 ] && echo "OK diagnostic is status-neutral" || echo "FAILED" -- 2.54.0 From be4c4e66f1eb8d3ec27d5b432753132bd0d74fa7 Mon Sep 17 00:00:00 2001 From: Mos Date: Thu, 6 Aug 2026 19:11:43 -0500 Subject: [PATCH 4/4] fix: stop re-escaping non-ASCII in package.json when enumerating a test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CI step `format` failed (pipeline 2255, exit 1) -- not on the test, on formatting. I enumerated the new test by loading packages/mosaic/package.json with python's json module and writing it back. json.dumps defaults to ensure_ascii=True, so the em-dash in "description": "Mosaic agent framework — installation wizard and meta package" was rewritten as —. Valid JSON, identical when parsed, and rejected by `pnpm format:check`. Enumeration is a TEXT edit now: read the file, splice the test into the test:framework-shell string, write it back. A JSON round-trip rewrites the whole document to serialiser defaults; the only safe edit to a formatted file is one that touches the bytes it means to touch. diff vs main is now exactly one line in each branch. --- packages/mosaic/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/mosaic/package.json b/packages/mosaic/package.json index 0b3bc712..631b4a88 100644 --- a/packages/mosaic/package.json +++ b/packages/mosaic/package.json @@ -6,7 +6,7 @@ "url": "https://git.mosaicstack.dev/mosaicstack/stack.git", "directory": "packages/mosaic" }, - "description": "Mosaic agent framework \u2014 installation wizard and meta package", + "description": "Mosaic agent framework — installation wizard and meta package", "type": "module", "main": "dist/index.js", "types": "dist/index.d.ts", -- 2.54.0