diff --git a/plans/agent-briefings/sx-gate-loop.md b/plans/agent-briefings/sx-gate-loop.md index 4afc8933..0d69a69b 100644 --- a/plans/agent-briefings/sx-gate-loop.md +++ b/plans/agent-briefings/sx-gate-loop.md @@ -54,7 +54,9 @@ Pin each confirmed-and-fixed finding with a minimal repro. Add suites to sentinel across two tests; a plain assert would inherit the vacuity) - [x] C1/C1b [W3] — command-channel crash guards pinned (`scripts/test-protocol-gate.sh`, seed for section E's fuzz suite) -- [ ] S4 [conformance] — housekeeping repro, pin +- [x] S4 [hosts] — soft error pages not cached (HTTP-mode pin in + `scripts/test-protocol-gate.sh`; NB S4 lives in hosts.md, not + conformance — "housekeeping" was a mislabel from F-15's tag) ### B. Runner/production env unification - [ ] Audit runner-only bindings (`values`/`call-with-values` F7/K42, JS @@ -81,6 +83,16 @@ Pin each confirmed-and-fixed finding with a minimal repro. Add suites to ## Progress log (newest first) +- 2026-07-04 — **S4 error-page-cache pin (item A.7) — section A COMPLETE**. + Extended `scripts/test-protocol-gate.sh` with an HTTP-mode case: fresh + `sx_server.exe --http ` (timeout-bounded, own PID killed at + end), GET the same nonexistent path twice, assert BOTH requests re-render + (2 `[sx-http]` lines — pre-fix the 2nd was cache-served at 0.0005s) and + the `[cache] … error page, not cached` is_err gate line appears. Findings + from prototyping: standalone worktree renders ALL docs pages as soft error + pages (no content), so a positive "real page IS cached" control is not + assertable here — documented in the script; startup takes ~12-15s (poll + loop, 40s budget). 5/5 protocol-gate green + 267/0 sx pins. Test-only. - 2026-07-04 — **C1/C1b command-channel pins (item A.6)**. These are protocol-level, not .sx-suite pins: authored `scripts/test-protocol-gate.sh` — each case spawns its OWN timeout-bounded diff --git a/scripts/test-protocol-gate.sh b/scripts/test-protocol-gate.sh index 9ac7355b..798fcab8 100755 --- a/scripts/test-protocol-gate.sh +++ b/scripts/test-protocol-gate.sh @@ -84,6 +84,62 @@ else fail=$((fail+1)) fi +# --------------------------------------------------------------------------- +# S4 (review, hosts.md): soft error pages must NOT be stored in the HTTP +# response cache. Pre-fix, a routing-failure page was cached as HTTP 200 and +# served byte-identically from cache to every later visitor (cold 2s → warm +# 0.0005s, ONE render line). Post-fix (dc7aa709), http_render_page returns +# (html, is_error) and cache insertion is gated on `not is_err` (the skip is +# logged as "[cache] → error page, not cached"). +# +# Pin: GET the same nonexistent path twice against a fresh --http server and +# assert BOTH requests re-render (two [sx-http] render lines) plus the +# is_err gate line appearing in the log. NB: in a standalone worktree all +# docs pages render as soft error pages (no content), so a positive +# "real page IS cached" control is not assertable here. +# --------------------------------------------------------------------------- +s4_case() { + local port=$((18000 + RANDOM % 2000)) + local log; log=$(mktemp) + timeout 90 "$SERVER" --http "$port" >"$log" 2>&1 & + local srv=$! + local up=0 + for _ in $(seq 1 40); do + if curl -s -o /dev/null "http://localhost:$port/" 2>/dev/null; then up=1; break; fi + sleep 1 + done + if [[ $up -ne 1 ]]; then + echo "FAIL: S4 — http server did not come up on :$port" + kill "$srv" 2>/dev/null; rm -f "$log" + fail=$((fail+1)); return + fi + local miss="/sx/gate-pin-missing-$$-$RANDOM" + curl -s -o /dev/null "http://localhost:$port$miss" + curl -s -o /dev/null "http://localhost:$port$miss" + sleep 1 + local renders + renders=$(grep -c "sx-http\] $miss " "$log") + local ok=1 + if [[ "$renders" -ne 2 ]]; then + echo "FAIL: S4 — expected 2 renders of $miss (not cache-served), got $renders" + ok=0 + fi + if ! grep -q 'error page, not cached' "$log"; then + echo "FAIL: S4 — is_err cache gate line absent from server log" + ok=0 + fi + if [[ $ok -eq 1 ]]; then + echo "PASS: S4 soft error page not cached (both GETs re-rendered)" + pass=$((pass+1)) + else + echo " --- log tail ---"; tail -12 "$log" | sed 's/^/ /'; echo " ---------------" + fail=$((fail+1)) + fi + kill "$srv" 2>/dev/null + rm -f "$log" +} +s4_case + echo echo "protocol-gate: $pass passed, $fail failed" [[ $fail -eq 0 ]]