Compare commits

..
Author SHA1 Message Date
Mos be4c4e66f1 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 23:47:17 -05:00
Mos 6f1924f32b 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
2026-08-06 23:47:17 -05:00
Mos 97dbd1bf4a 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
2026-08-06 23:47:17 -05:00
41c224afff 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 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Amf1Neca162odgcbCWMk1y
2026-08-06 23:46:44 -05:00
@@ -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);
});
});