Compare commits

..
Author SHA1 Message Date
Mos aa2774a1fe fix: stop re-escaping non-ASCII in package.json when enumerating a test
ci/woodpecker/pr/ci Pipeline was successful
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.
2026-08-06 19:11:43 -05:00
Mos 353294b3a9 test(tools/git): make the status-neutral probe actually exercise the shipped line
ci/woodpecker/pr/ci Pipeline failed
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
2026-08-06 18:53:26 -05:00
Mos 14a9954401 fix(tools/git): the tea diagnostic must not suppress the API fallback
ci/woodpecker/pr/ci Pipeline failed
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
2026-08-06 18:36:53 -05:00
Hermes AgentandClaude Opus 5 5394e284bd feat(tools/git): explain tea's misleading user does not exist error
ci/woodpecker/pr/ci Pipeline was successful
`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 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Amf1Neca162odgcbCWMk1y
2026-08-06 15:39:33 -05:00
4 changed files with 5 additions and 81 deletions
@@ -245,21 +245,9 @@ describe('EnrollmentService.createToken', () => {
const after = Date.now();
const expiresMs = new Date(result.expiresAt).getTime();
// The property under test is CLAMPING: a 9999s request must come back as 900s.
// The gap between clamped and unclamped is 9_099_000 ms, so the tolerance below
// only has to exceed CI scheduling jitter — it does not need to be tight to keep
// the assertion discriminating. A 5s allowance consumes 0.05% of that margin and
// an unclamped result still misses by three orders of magnitude.
//
// It was 100ms and failed on a loaded agent at 900_106 — 6ms over (#1090). A
// wall-clock budget sized to a fast machine is a flake, not a tighter test.
const CI_JITTER_MS = 5_000;
expect(expiresMs - before).toBeLessThanOrEqual(900_000 + CI_JITTER_MS);
// Should be at most 900s from now
expect(expiresMs - before).toBeLessThanOrEqual(900_000 + 100);
expect(expiresMs - after).toBeGreaterThanOrEqual(0);
// Explicitly pin the clamp itself, independent of any timing allowance:
// unclamped (9999s) would exceed this by ~9_099_000 ms.
expect(expiresMs - before).toBeLessThan(1_000_000);
});
});
@@ -5,10 +5,7 @@
detect_platform() {
local remote_url
# `|| true` is load-bearing under `set -e`: outside a git repo this returns 128 and
# kills the CALLER before the -z check below can run, so the error message that is
# already written here was unreachable. Same idiom as get_gitea_repo_args() below.
remote_url=$(git remote get-url origin 2>/dev/null) || true
remote_url=$(git remote get-url origin 2>/dev/null)
if [[ -z "$remote_url" ]]; then
echo "error: not a git repository or no origin remote" >&2
@@ -42,10 +39,7 @@ detect_platform() {
get_repo_info() {
local remote_url
# `|| true` is load-bearing under `set -e`: outside a git repo this returns 128 and
# kills the CALLER before the -z check below can run, so the error message that is
# already written here was unreachable. Same idiom as get_gitea_repo_args() below.
remote_url=$(git remote get-url origin 2>/dev/null) || true
remote_url=$(git remote get-url origin 2>/dev/null)
if [[ -z "$remote_url" ]]; then
echo "error: not a git repository or no origin remote" >&2
@@ -1,58 +0,0 @@
#!/bin/bash
# Regression: detect_platform / get_repo_info must FAIL LOUDLY outside a git repo,
# not kill the caller silently.
#
# Both functions already contained the right error path:
# if [[ -z "$remote_url" ]]; then echo "error: not a git repository..." >&2; return 1; fi
# but under `set -e` -- which every wrapper in this directory uses -- the preceding
# assignment `remote_url=$(git remote get-url origin 2>/dev/null)` returns git's 128
# outside a repo and terminates the CALLER first. The message was unreachable.
#
# Observed cost: pr-review.sh invoked from a non-repo cwd exits 128 with NO stdout and
# NO stderr, even when -r/--repo and -H/--host are supplied -- the flags documented as
# "skips git-remote inference". Two reviewer seats hit this and correctly reported
# `blocked` with no diagnostic to report.
#
# The control that matters is the LOUD one: asserting "rc != 0" passes on the broken
# build too, because 128 is also non-zero. The test must assert the MESSAGE.
set -uo pipefail
fail=0
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
TMP="$(mktemp -d)"; trap 'rm -rf "$TMP"' EXIT
run_outside() { # $1=function name -> "rc:sawmessage"
local fn="$1" out rc
out=$( cd "$TMP" && bash -c "set -e; source '$HERE/detect-platform.sh'; $fn" 2>&1 ); rc=$?
printf '%s:%s' "$rc" "$(grep -qi 'not a git repository' <<<"$out" && echo yes || echo no)"
}
check() { if [ "$2" = "$3" ]; then echo " PASS $1 ($2)"; else echo " FAIL $1: got $2, want $3"; fail=1; fi; }
# $TMP must not be inside a git repo. Do not SKIP on failure: be-coder-07 showed the
# original SKIP exited 0, so pointing TMPDIR beneath a git worktree made this test PASS
# against unchanged main. A skip that exits 0 is indistinguishable from a pass.
# GIT_CEILING_DIRECTORIES stops git walking above $TMP, making the condition hold
# regardless of where TMPDIR lives, rather than merely detecting when it does not.
# GIT_CEILING_DIRECTORIES is matched against the PHYSICAL path -- a symlinked TMPDIR
# (/tmp is commonly one) makes the logical path never match, and the ceiling silently
# does nothing. Resolve it before exporting.
TMP="$(cd "$TMP" && pwd -P)"
export GIT_CEILING_DIRECTORIES="$TMP"
if ( cd "$TMP" && git rev-parse --git-dir >/dev/null 2>&1 ); then
echo " FAIL scratch dir is inside a git repo even with GIT_CEILING_DIRECTORIES set;"
echo " the outside-a-repo precondition cannot be established -- refusing to report a result"
exit 1
fi
echo "== outside a git repo: rc=1 AND the diagnostic is emitted =="
check "detect_platform" "$(run_outside detect_platform)" "1:yes"
check "get_repo_info" "$(run_outside get_repo_info)" "1:yes"
echo "== inside a git repo the functions still work =="
git init -q "$TMP/repo" 2>/dev/null
git -C "$TMP/repo" remote add origin https://git.mosaicstack.dev/mosaicstack/stack.git 2>/dev/null
out=$( cd "$TMP/repo" && bash -c "set -e; source '$HERE/detect-platform.sh'; detect_platform" 2>&1 ); rc=$?
if [ "$rc" -eq 0 ] && grep -qi 'gitea' <<<"$out"; then echo " PASS detect_platform in-repo (rc=0, $out)"
else echo " FAIL detect_platform in-repo: rc=$rc out=$out"; fail=1; fi
[ "$fail" -eq 0 ] && echo "OK detect-platform fails loudly outside a repo" || echo "FAILED"
exit "$fail"
+1 -1
View File
@@ -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-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"
"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/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:*",