diff --git a/CHANGELOG.md b/CHANGELOG.md index 1b057dda..137bf94b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ Sections dated before 2026-09-19 predate the cycle and stay as they are. Named m ## Unreleased +- fix(wire-company-brain): **re-wiring a company brain works on macOS, and a rewrite that fails no longer reports "Updated"**: the block was handed to awk as `-v blk=…`, which BSD awk (macOS) refuses when the value holds a newline, so every re-run (a moved brain, a changed block) left the files untouched while printing `Updated: … rewritten in place`; the block now goes through the environment (`ENVIRON["BLK"]`), and a failed rewrite or append prints `FAILED: …`, leaves the file alone and is not counted. Pinned by `tests/wave2/test-company-brain.sh` T2c (a stub awk with BSD's `-v` rule; 4 of its 5 checks fail on 2ba8158) — reported in #598 from a field-run project, macOS 26 / BSD awk 20200816 - fix(doctor, sync-project, speed-setup): **doctor says when the speed fixes never reached a project**: `bin/exec.sh`, `bin/_common.sh` or `bin/lint-gate.sh` without the 2026-10-29 speed code (marker per file) gives a Machine health warning "speed fixes not active", and the Recommended fix leads with the exact `sync-project.sh --upgrade-bin ...` command (exec.sh always with its `_common.sh`; each copy backed up) before any other advice, because every other number measures the old scripts; `sync-project.sh` names the speed fixes on a hand-edited exec.sh / _common.sh that has the exit-code fix but not the speed work; `skills/speed-setup.md` has Claude show each local edit in one line and ask before replacing. Field: an English colleague's ExpenseTracker analysis after the toolkit update still showed a full mxbuild on every change (45.5 min, 57% of all exec time) with 15 drifted scripts in bin/; reproduced here with the real pre-#587 exec.sh / _common.sh / lint-gate.sh — doctor names all three, sync names exec.sh and refreshes the two unedited ones — Maurits (mauritscgvisser) - fix(doctor, speed-setup): **doctor no longer says "None needed" next to a verdict of "5x slower"**: when process starts are fast but one bench step is 5x+ the reference and 2 s+ slower, the Recommended fix names that step (slow git status: `git config core.untrackedCache true` / `core.fsmonitor true`, no admin rights); a project inside a OneDrive / Dropbox / Google Drive / iCloud folder gets a Machine health warning and "move the project to a local folder" as the fix; low-memory advice now offers "restart the computer" before `--cleanup`; the bench's git step runs one untimed warm-up so a stale index after a copy or clone no longer reads as slow (field, ab-581/gk: 18.3 s cold, 31 ms warm); `skills/speed-setup.md` tells Claude never to say deno, mxbuild or helper process to a user. Field run: ab-581/gk on Linux, bench 14.7 s vs 10.9 s, "None needed" correctly; a project under a OneDrive path, move advice shown — Maurits (mauritscgvisser) - fix(gate-check, gate-cache): **the closeout verdict cache also keys on file CONTENT, so an overwrite that keeps an old mtime (`cp -p`, `rsync -t`, `tar x`, `unzip`, `touch -d`) can no longer replay a stale verdict**: a signed triage.md copied over a FAIL one still printed the cached Stage 0 FAIL (PR #589 review); the cache now stores one `find | sort | xargs cksum | cksum` signature of the project files (skipping .git, node_modules, .mxtk, mprcontents, deployment, *.mpr, files over 1 MB) and any difference, or a signature that cannot be computed, re-evaluates; new `tests/wave2/test-gate-cache.sh` (hit, edit/delete/waiver miss, cp -p miss, TTL 0, 'not evaluated' line); `MXTK_GATE_CACHE_TRACE=1` prints `gate-cache: hit|miss` on --closeout. Field run: ab-581/base copy (197 MB, 633 hashed files) - signature 28 ms, cache hit 1464 -> 1498 ms vs 2567-2786 ms full evaluation - PR #589 reviewer, OlafFede (#583) — OlafFede (#583) diff --git a/bin/wire-company-brain.sh b/bin/wire-company-brain.sh index 6ee273e5..ecf5f367 100755 --- a/bin/wire-company-brain.sh +++ b/bin/wire-company-brain.sh @@ -65,14 +65,24 @@ while IFS= read -r CL; do tmp="$(mktemp)" if grep -q "$BEGIN" "$CL"; then grep -q "$END" "$CL" || { echo "$(basename "$CL") has a BEGIN marker but no END — fix by hand" >&2; rm -f "$tmp"; continue; } - awk -v b="$BEGIN" -v e="$END" -v blk="$block" ' - index($0,b)==1 { print blk; skip=1; next } + # The block goes in through the environment, not -v: BSD awk (macOS) refuses a newline in a + # -v value, so every re-run failed there while still printing "Updated" (#598). + if BLK="$block" awk -v b="$BEGIN" -v e="$END" ' + index($0,b)==1 { print ENVIRON["BLK"]; skip=1; next } index($0,e)==1 { skip=0; next } - !skip { print }' "$CL" > "$tmp" && mv "$tmp" "$CL" - echo "Updated: ${CL#$PROJECT/} — company brain block rewritten in place." + !skip { print }' "$CL" > "$tmp" && mv "$tmp" "$CL"; then + echo "Updated: ${CL#$PROJECT/} — company brain block rewritten in place." + else + echo "FAILED: ${CL#$PROJECT/} — company brain block not rewritten; file left unchanged." >&2 + rm -f "$tmp"; continue + fi else - { cat "$CL"; printf '\n%s\n' "$block"; } > "$tmp" && mv "$tmp" "$CL" - echo "Updated: ${CL#$PROJECT/} — company brain block appended." + if { cat "$CL"; printf '\n%s\n' "$block"; } > "$tmp" && mv "$tmp" "$CL"; then + echo "Updated: ${CL#$PROJECT/} — company brain block appended." + else + echo "FAILED: ${CL#$PROJECT/} — company brain block not appended; file left unchanged." >&2 + rm -f "$tmp"; continue + fi fi rm -f "$tmp" written=$((written+1)) diff --git a/tests/wave2/test-company-brain.sh b/tests/wave2/test-company-brain.sh index a8c9b42c..403b7910 100755 --- a/tests/wave2/test-company-brain.sh +++ b/tests/wave2/test-company-brain.sh @@ -14,6 +14,9 @@ # block each, and a surface the project does not have is not created. Added 2026-09-20 # after an eval session read AGENTS.md/CLAUDE.md/PROJECT.md, never opened CLAUDE.local.md, # and so never saw the company brain at all (retrieval 2/3 → 3/3 once this was fixed). +# T2c a re-point under BSD-awk rules (no newline in -v, as macOS awk) still rewrites in place, +# and a rewrite that fails prints FAILED, not Updated, leaves the file alone and is not +# counted (#598) # T3 wire refuses an unshaped folder; wire on a project without CLAUDE.local.md refuses # T4 check-no-private-citations: positive control (planted pointer) exits 1, clean tree exits 0, # the toolkit's own templates/company-brain/ path is not a hit @@ -62,6 +65,38 @@ r=$(grep -c "^$P " "$B/projects.tsv"); [ "$r" -eq 1 ] && ok "re-wire: still regi B2="$WORK/acme-brain-2"; "$INIT" "$B2" >/dev/null 2>&1; "$BIN/wire-company-brain.sh" "$P" "$B2" >/dev/null 2>&1 grep -qF "$B2/ROUTING.md" "$CL" && ! grep -qF "$B/ROUTING.md" "$CL" && ok "re-point rewrites in place" || bad "re-point left the old path" +echo "== T2c: re-point under BSD-awk rules; a failed rewrite never says Updated (#598) ==" +# macOS awk (one-true-awk 20200816) refuses a newline in a -v value ("newline in string"); gawk +# and mawk accept it, so the re-point above passes on Linux whatever the script does. This stub +# enforces the BSD rule and otherwise hands over to the real awk. +REAL_AWK="$(command -v awk)"; SB="$WORK/bsd-awk"; mkdir -p "$SB" +cat > "$SB/awk" <&2; exit 2;; esac; fi + prev="\$a" +done +exec "$REAL_AWK" "\$@" +STUB +chmod +x "$SB/awk" +B3="$WORK/acme-brain-3"; "$INIT" "$B3" >/dev/null 2>&1 +out=$(PATH="$SB:$PATH" "$BIN/wire-company-brain.sh" "$P" "$B3" 2>&1) +grep -qF "$B3/ROUTING.md" "$CL" && ! grep -qF "$B2/ROUTING.md" "$CL" \ + && ok "re-point under BSD-awk rules rewrites in place" || bad "re-point under BSD-awk rules left the old path: $out" +# A rewrite that fails must not report success, and must leave the file and the count alone. +cat > "$SB/awk" <<'STUB' +#!/usr/bin/env bash +echo "awk: simulated failure" >&2; exit 2 +STUB +chmod +x "$SB/awk" +before=$(cksum < "$CL") +out=$(PATH="$SB:$PATH" "$BIN/wire-company-brain.sh" "$P" "$B" 2>&1) +echo "$out" | grep -q "^Updated: CLAUDE.local.md" && bad "failed rewrite still printed Updated: $out" || ok "failed rewrite does not print Updated" +echo "$out" | grep -q "^FAILED: CLAUDE.local.md" && ok "failed rewrite is reported as FAILED" || bad "no FAILED line: $out" +[ "$(cksum < "$CL")" = "$before" ] && ok "failed rewrite leaves the file unchanged" || bad "file changed after a failed rewrite" +echo "$out" | grep -q "written to 0 instruction surface" && ok "failed surface is not counted" || bad "count wrong: $(echo "$out" | grep 'written to')" + echo "== T2b: the pointer reaches every instruction surface that exists ==" M="$WORK/multi"; mkdir -p "$M" "$BIN/init-project.sh" "$M" --ignore-sources >/dev/null 2>&1