Landing blocker for #61 and #63: the deployment checkout is 12 commits behind and `merge --ff-only` refuses while tracked paths are dirty. Discard is not available here (workspace policy: no `checkout`/`stash` on shared state), so the dirt is committed. - `agent/APPEND_SYSTEM.md`: the point-of-use / one-work-log / PR-link doctrine, already byte-identical to `origin/main` (`cmp` against the trunk blob: no difference). No new prose — the commit exists so the trunk merge can proceed. - `agent/config.yml` and `agent/managed-skills/**`: the machine-live tier the heartbeat owns (AGENTS.md § Live-sync tiers). `ALLOW_SECRETS=1` was used, deliberately and after auditing the staged diff by hand: the only credential-shaped strings in it are two copies of `AKIA0000000000000EXAMPLE` — the inert all-zero AWS example id — and they appear inside the two skill pages that teach that exact placeholder shape. This checkout is still running the pre-#61 hook; the merged gate (value-scoped placeholder filter, `71601f7`) exempts this value, so the refusal dies with this landing. No `--no-verify` was used: the gate ran and its verdict is recorded here. Refs #61
3.0 KiB
name, description
Three traps hit while writing red-then-green bats tests for remote-job.sh (Refs #60, dank/command-center PR #61). Each one cost a detour because it makes a test look like a bug report when it is an assertion bug.
1. In [[ $s == pat ]] a quoted * is LITERAL — and it eats the wildcard beside it
s=' x* DONE rc=0 1 lines'
[[ "$s" == *"x*"DONE*"rc=0"* ]] # NOMATCH — and it looks like the tool under test broke
[[ "$s" == " x*"*"DONE rc=0"* ]] # MATCH — quoted literals, UNQUOTED separators
*"x*"DONE* means: anything, then the literal substring x*DONE, then anything. Quoting only makes the * inside the quotes literal; it does not keep the next unquoted token from butting up against it. When the subject has whitespace between the two facts, the pattern must carry an unquoted * between the quoted literals:
"literal-a"*"literal-b" # a, then anything, then b
Existing suites in the repo write *"a"*DONE*rc=0* — unquoted separators throughout — precisely for this reason. Copy that shape; do not quote for "clarity".
Discriminate before believing the test: run the pattern against BOTH the expected line and the line the buggy code produced (correct-red / NOMATCH on the second is the pass condition). A pattern that matches the old output was never a regression test.
2. A bats failure dump prints the TEST's stdout, not $output
not ok 1 status treats a job name as a name, not a pattern
# `[[ "$output" == *"x*"DONE* ]]` failed
# started x*
started x* is the stdout the test body itself printed (from a start call several lines earlier), not what run captured. Reading it as the captured value sends you to debug the tool. To see the real capture, echo it:
run some-command
echo "CAP:[${output}]"
in a scratch copy of the test (/tmp/dbg.bats, same setup/harness) rather than adding diagnostics to the committed test.
3. Reproduce outside bats when green refuses to arrive
When a test fails but the same command works by hand, replicate the exact bats call shape in /tmp with the same env exports (REMOTE_JOBS_DIR etc.) and the same run bash -c '…' _ "$A" "$B" "$C" argument mapping. If the scratch copy passes and the committed one fails, the difference is the assertion, not the system — go back to trap 1.
Red-proof rule
Run the new test against the pre-fix code and read why it failed. Both traps above produced a failure that looked meaningful while proving nothing (one matched the buggy output; one printed the wrong stream). Keep the test only when the failure reason is the defect you set out to pin.
Related: bats-assertion-trust (vacuous assertions), red-verify-without-stash (proving red in a shared worktree).