Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ Sections dated before 2026-09-19 predate the cycle and stay as they are.

## Unreleased

- fix(obligations): **LOOK staleness reads the newest `VALID AT` stamp, anywhere in the report.** Both pickers took the first stamp inside the first 8000 bytes, so a review file with dated per-build addenda went false-STALE; now `tail -1` over the whole file. Field run 2026-09-26, requirements-driven build (#155) — MendixMau
- perf(routing): **CLAUDE.local.md routing carries triggers only.** The `baseline` view (the copy loaded into every session of every project) now cuts each row's "when" cell at its first " — " and replaces the three-paragraph preamble with two lines pointing at `ROUTING.md` for the full text; `readme-baseline` (README, runbook) is unchanged. Fresh scaffold: CLAUDE.local.md 17.5 KB → 11.7 KB (−33%). Field run 2026-10-07 (existing app, one main session per run, 2 runs per side, an entity + 2 microflows + 2 pages): 10/10 steps applied each side, new mx errors 5 vs 5 (the same CE2421 page-binding slip in both), cost $1.39 vs $1.38; the slim lanes opened the MDL pre-flight skill as often or more. Also drops two stray merge-artifact lines from this section. — MendixMau
- perf(ui-loop): **fewer mxbuilds, no fixed sleeps, legible screenshots.** Field run 2026-10-07 (a requirements-driven build): `exec.sh` was 22% of tool time at two mxbuilds per exec, journeys and page waits slept a fixed time, and screenshots were 11.5% of re-read tokens. `exec.sh` now skips the pre-flight baseline mxbuild when the model is byte-identical to its last *clean* pass: the verification stamp gains an `errors:` field (`model-stamp.sh check --clean` accepts only `errors: 0`, so a delta-gate pass over pre-existing errors never counts; `SKIP_BASELINE=0` is unchanged). New `tests/e2e/settle.js` waits for the page's own state (no visible Mendix progress/loading indicator, DOM size stable) instead of a fixed time; `journey-runner.js` uses it after each action (an explicit `settleMs` still wins) and `page-audit.js` per page. Probed in a real browser: on a page that loads in 1.8 s the old 1200 ms sleep and networkidle+600 both captured it half-loaded, settle waited for it; on a fast page settle took 775 ms vs 1200. `SETTLE_MODE=fixed` restores the old waits for an A/B run. `page-audit.js` also saves a viewport shot `page-audit-<Page>.top.png` beside the full-page one, and `ui-loop.md` says to look at viewport shots: a tall full-page shot reaches the model at ~1024 px high, text unreadable. `shrink-image-read.sh` now works off macOS (ImageMagick or Pillow, python when jq is absent; it was a silent no-op without `sips`). — MendixMau
- perf(ui-loop): **a stale app reloads instead of restarting, journeys stop sleeping, worst pages are looked at first.** Field run 2026-10-07, existing app. `test-stack-up.sh` records which model the running app was built from (`.claude/loop/served-model`); when the model has changed since, it hot-reloads with `mxcli docker reload` (adding `--skip-check` when the exec.sh gate already passed this model) and restarts only if that fails: 93 s instead of 179 s. `ui-loop.md` and the script header carry the measured table and point at `mxcli run --local --watch` as the fast loop (~18 s per page change). `journey-runner.js` waits on page state after a combobox action instead of fixed pauses, and a combobox step now opens the widget it names: it used to open the page's first combobox, so on a form with three every step but the first failed. `page-audit.js` clicks the first copy of a nav item a user could actually click, not just the first match: with the sidebar collapsed, every in-nav page faulted while the same item sat in the top menu bar. It also writes `page-audit-look-order.txt`, all pages worst first (`module-review.md` §4a: order, not selection, every page is still looked at). `design-audit.js` drops its fixed 1.2 s wait. Same 7-step journey on the same app: 56 s with `SETTLE_MODE=fixed`, 24 s with settle, 11/11 pass both ways, 3 runs each. — MendixMau
Expand Down
8 changes: 4 additions & 4 deletions bin/lib/obligation-check.sh
Original file line number Diff line number Diff line change
Expand Up @@ -246,9 +246,9 @@ _ob_has_denominator() {
# "expired".
_ob_stale() {
local root="$1" hit="$2" hash n
hash="$(head -c 8000 "$hit" 2>/dev/null | tr -d '\r' \
hash="$(cat "$hit" 2>/dev/null | tr -d '\r' \
| grep -oiE 'VALID[[:space:]]+AT:?[[:space:]]*[0-9a-f]{7,40}' \
| grep -oiE '[0-9a-f]{7,40}$' | head -1)"
| grep -oiE '[0-9a-f]{7,40}$' | tail -1)"
[ -n "$hash" ] || return 1
git -C "$root" cat-file -e "${hash}^{commit}" 2>/dev/null || return 1
n="$(git -C "$root" log --oneline "${hash}..HEAD" -- '*.mpr' mdlsource 2>/dev/null | wc -l | tr -d ' ')"
Expand Down Expand Up @@ -303,9 +303,9 @@ _ob_look_proof_broken() {

# VALID AT commit time, for freshness. Unresolvable → freshness unmeasured, not failed.
local vhash vtime=""
vhash="$(head -c 8000 "$hit" 2>/dev/null | tr -d '\r' \
vhash="$(cat "$hit" 2>/dev/null | tr -d '\r' \
| grep -oiE 'VALID[[:space:]]+AT:?[[:space:]]*[0-9a-f]{7,40}' \
| grep -oiE '[0-9a-f]{7,40}$' | head -1)"
| grep -oiE '[0-9a-f]{7,40}$' | tail -1)"
[ -n "$vhash" ] && vtime="$(git -C "$root" show -s --format=%ct "${vhash}^{commit}" 2>/dev/null || true)"

reldir="$(dirname "$hit")"
Expand Down
48 changes: 48 additions & 0 deletions tests/wave2/test-obligation-valid-at.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#!/usr/bin/env bash
# Usage: bash tests/wave2/test-obligation-valid-at.sh [path-to-gate-check.sh | path-to-obligation-check.sh]
#
# test-obligation-valid-at.sh — the LOOK obligation's staleness check must judge a review report
# by its NEWEST "VALID AT: <hash>" stamp, wherever in the file it sits.
#
# THE BUG (#155): _ob_stale and the proof check picked `head -c 8000 | grep ... | head -1` — the
# FIRST stamp in the first 8000 bytes. A report that keeps one dated headline and appends an
# addendum per build row (each with its own VALID AT) was judged by the headline's original
# stamp and went STALE after later model commits, though every page was re-looked-at.
# Subject: obligation-check.sh. Producer of the stamp: module-review.md stage 5 (the reviewer).
set -uo pipefail
ARG="${1:?usage: test-obligation-valid-at.sh /path/to/bin/gate-check.sh (or lib/obligation-check.sh)}"
case "$(basename "$ARG")" in
obligation-check.sh) OBCHECK="$ARG" ;;
*) OBCHECK="$(cd "$(dirname "$ARG")" && pwd)/lib/obligation-check.sh" ;;
esac
[ -r "$OBCHECK" ] || { echo "no obligation-check.sh at $OBCHECK"; exit 2; }
WORK="$(mktemp -d "${TMPDIR:-/tmp}/ob-validat.XXXXXX")"
trap '[ -n "${KEEP:-}" ] || rm -rf "$WORK"' EXIT
PASS=0; FAIL=0
ok() { PASS=$((PASS+1)); printf ' ok %s\n' "$1"; }
bad() { FAIL=$((FAIL+1)); printf ' FAIL %s\n' "$1"; }

P="$WORK/p"; mkdir -p "$P/architecture/modules/ModuleX" "$P/mdlsource" "$P/design/ui-reviews"
git -C "$P" init -q . 2>/dev/null
git -C "$P" config user.email t@t; git -C "$P" config user.name t
echo a > "$P/mdlsource/a.mdl"; git -C "$P" add -A; git -C "$P" commit -qm one
OLD="$(git -C "$P" rev-parse --short HEAD)"
echo b >> "$P/mdlsource/a.mdl"; git -C "$P" commit -qam two
NEW="$(git -C "$P" rev-parse --short HEAD)"
REP="$P/design/ui-reviews/ui-review-2026-01-01.html"
stale() { "$OBCHECK" "$P" 2>&1 | grep -c 'STALE'; }

echo "== T1: only the old stamp, model built past it -> STALE (control) =="
printf '<p>ModuleX: 1 of 1 pages reviewed</p>\nVALID AT: %s\n' "$OLD" > "$REP"
[ "$(stale)" -gt 0 ] && ok "old-only stamp reports STALE" || bad "control: expected STALE"

echo "== T2: old stamp, then an addendum stamped at HEAD -> newest wins, not STALE =="
printf '<p>ModuleX: 1 of 1 pages reviewed</p>\nVALID AT: %s\n<h2>Addendum</h2>\nVALID AT: %s\n' "$OLD" "$NEW" > "$REP"
[ "$(stale)" -eq 0 ] && ok "later stamp wins" || bad "first stamp was used (STALE)"

echo "== T3: newest stamp sits past byte 8000 -> still seen =="
{ printf '<p>ModuleX: 1 of 1 pages reviewed</p>\nVALID AT: %s\n' "$OLD"; head -c 9000 /dev/zero | tr '\0' 'x'; printf '\nVALID AT: %s\n' "$NEW"; } > "$REP"
[ "$(stale)" -eq 0 ] && ok "stamp past 8000 bytes seen" || bad "stamp past 8000 bytes ignored (STALE)"

echo "pass=$PASS fail=$FAIL"
[ "$FAIL" -eq 0 ]
Loading