From b4ecadaad98fdc91a7f2a2a32fb67681ee85101d Mon Sep 17 00:00:00 2001 From: giles Date: Sun, 7 Jun 2026 10:50:47 +0000 Subject: [PATCH] conformance: migrate feed onto shared driver (counters, 189/189 parity) Feed is the canonical MODE=counters shape: each suite runs in a fresh session with shared preloads and a single feed-test-pass/feed-test-fail pair. Lifted the old script's inline epoch-2 counter + feed-test helper defs into lib/feed/test-harness.sx (preloaded last) so the driver can load them before each suite. conformance.conf + 3-line shim; historical scoreboard schema preserved. No driver change needed. Parity verified 189/189 (0 fail), every suite matching baseline. Co-Authored-By: Claude Opus 4.8 (1M context) --- lib/feed/conformance.conf | 82 ++++++++++++++ lib/feed/conformance.sh | 126 +--------------------- lib/feed/test-harness.sx | 14 +++ plans/agent-briefings/conformance-loop.md | 9 +- 4 files changed, 106 insertions(+), 125 deletions(-) create mode 100644 lib/feed/conformance.conf create mode 100644 lib/feed/test-harness.sx diff --git a/lib/feed/conformance.conf b/lib/feed/conformance.conf new file mode 100644 index 00000000..03aafb76 --- /dev/null +++ b/lib/feed/conformance.conf @@ -0,0 +1,82 @@ +# Feed-on-SX conformance config — sourced by lib/guest/conformance.sh. +# +# Every feed suite runs in a fresh session with the same preloads and a single +# pass/fail counter pair — the canonical MODE=counters shape. The counters and +# the feed-test helper (previously defined inline in the old conformance.sh) are +# preloaded via lib/feed/test-harness.sx. + +LANG_NAME=feed +MODE=counters +COUNTERS_PASS=feed-test-pass +COUNTERS_FAIL=feed-test-fail +TIMEOUT_PER_SUITE=300 + +PRELOADS=( + spec/stdlib.sx + lib/r7rs.sx + lib/apl/runtime.sx + lib/feed/normalize.sx + lib/feed/stream.sx + lib/feed/api.sx + lib/feed/fanout.sx + lib/feed/dedupe.sx + lib/feed/aggregate.sx + lib/feed/rank.sx + lib/feed/acl.sx + lib/feed/fed.sx + lib/feed/content.sx + lib/feed/notify.sx + lib/feed/home.sx + lib/feed/trending.sx + lib/feed/mute.sx + lib/feed/page.sx + lib/feed/thread.sx + lib/feed/test-harness.sx +) + +SUITES=( + "basic:lib/feed/tests/basic.sx" + "fanout:lib/feed/tests/fanout.sx" + "rank:lib/feed/tests/rank.sx" + "integration:lib/feed/tests/integration.sx" + "content:lib/feed/tests/content.sx" + "notify:lib/feed/tests/notify.sx" + "home:lib/feed/tests/home.sx" + "dedupe:lib/feed/tests/dedupe.sx" + "trending:lib/feed/tests/trending.sx" + "mute:lib/feed/tests/mute.sx" + "page:lib/feed/tests/page.sx" + "thread:lib/feed/tests/thread.sx" +) + +# Preserve the historical scoreboard schema so consumers of +# lib/feed/scoreboard.json keep working. +emit_scoreboard_json() { + local n=${#GC_NAMES[@]} i + printf '{\n' + printf ' "suites": {\n' + for ((i=0; i&2 - exit 1 -fi - -SUITES=(basic fanout rank integration content notify home dedupe trending mute page thread) - -OUT_JSON="lib/feed/scoreboard.json" -OUT_MD="lib/feed/scoreboard.md" - -run_suite() { - local suite=$1 - local file="lib/feed/tests/${suite}.sx" - local TMP - TMP=$(mktemp) - cat > "$TMP" << EPOCHS -(epoch 1) -(load "spec/stdlib.sx") -(load "lib/r7rs.sx") -(load "lib/apl/runtime.sx") -(load "lib/feed/normalize.sx") -(load "lib/feed/stream.sx") -(load "lib/feed/api.sx") -(load "lib/feed/fanout.sx") -(load "lib/feed/dedupe.sx") -(load "lib/feed/aggregate.sx") -(load "lib/feed/rank.sx") -(load "lib/feed/acl.sx") -(load "lib/feed/fed.sx") -(load "lib/feed/content.sx") -(load "lib/feed/notify.sx") -(load "lib/feed/home.sx") -(load "lib/feed/trending.sx") -(load "lib/feed/mute.sx") -(load "lib/feed/page.sx") -(load "lib/feed/thread.sx") -(epoch 2) -(eval "(define feed-test-pass 0)") -(eval "(define feed-test-fail 0)") -(eval "(define feed-test (fn (name got expected) (if (= got expected) (set! feed-test-pass (+ feed-test-pass 1)) (set! feed-test-fail (+ feed-test-fail 1)))))") -(epoch 3) -(load "${file}") -(epoch 4) -(eval "(list feed-test-pass feed-test-fail)") -EPOCHS - - local OUTPUT - OUTPUT=$(timeout 300 "$SX_SERVER" < "$TMP" 2>/dev/null) - rm -f "$TMP" - - local LINE - LINE=$(echo "$OUTPUT" | awk '/^\(ok-len 4 / {getline; print; exit}') - if [ -z "$LINE" ]; then - LINE=$(echo "$OUTPUT" | grep -E '^\(ok 4 \([0-9]+ [0-9]+\)\)' | tail -1 \ - | sed -E 's/^\(ok 4 //; s/\)$//') - fi - - local P F - P=$(echo "$LINE" | sed -E 's/^\(([0-9]+) ([0-9]+)\).*/\1/') - F=$(echo "$LINE" | sed -E 's/^\(([0-9]+) ([0-9]+)\).*/\2/') - P=${P:-0} - F=${F:-0} - echo "${P} ${F}" -} - -declare -A SUITE_PASS -declare -A SUITE_FAIL -TOTAL_PASS=0 -TOTAL_FAIL=0 - -echo "Running feed conformance suite..." >&2 -for s in "${SUITES[@]}"; do - read -r p f < <(run_suite "$s") - SUITE_PASS[$s]=$p - SUITE_FAIL[$s]=$f - TOTAL_PASS=$((TOTAL_PASS + p)) - TOTAL_FAIL=$((TOTAL_FAIL + f)) - printf " %-12s %d/%d\n" "$s" "$p" "$((p+f))" >&2 -done - -# scoreboard.json -{ - printf '{\n' - printf ' "suites": {\n' - first=1 - for s in "${SUITES[@]}"; do - if [ $first -eq 0 ]; then printf ',\n'; fi - printf ' "%s": {"pass": %d, "fail": %d}' "$s" "${SUITE_PASS[$s]}" "${SUITE_FAIL[$s]}" - first=0 - done - printf '\n },\n' - printf ' "total_pass": %d,\n' "$TOTAL_PASS" - printf ' "total_fail": %d,\n' "$TOTAL_FAIL" - printf ' "total": %d\n' "$((TOTAL_PASS + TOTAL_FAIL))" - printf '}\n' -} > "$OUT_JSON" - -# scoreboard.md -{ - printf '# feed Conformance Scoreboard\n\n' - printf '_Generated by `lib/feed/conformance.sh`_\n\n' - printf '| Suite | Pass | Fail | Total |\n' - printf '|-------|-----:|-----:|------:|\n' - for s in "${SUITES[@]}"; do - p=${SUITE_PASS[$s]} - f=${SUITE_FAIL[$s]} - printf '| %s | %d | %d | %d |\n' "$s" "$p" "$f" "$((p+f))" - done - printf '| **Total** | **%d** | **%d** | **%d** |\n' "$TOTAL_PASS" "$TOTAL_FAIL" "$((TOTAL_PASS + TOTAL_FAIL))" -} > "$OUT_MD" - -echo "Wrote $OUT_JSON and $OUT_MD" >&2 -echo "Total: $TOTAL_PASS pass, $TOTAL_FAIL fail" >&2 - -[ "$TOTAL_FAIL" -eq 0 ] +# Thin wrapper — see lib/guest/conformance.sh and lib/feed/conformance.conf. +exec bash "$(dirname "$0")/../guest/conformance.sh" "$(dirname "$0")/conformance.conf" "$@" diff --git a/lib/feed/test-harness.sx b/lib/feed/test-harness.sx new file mode 100644 index 00000000..28c84be7 --- /dev/null +++ b/lib/feed/test-harness.sx @@ -0,0 +1,14 @@ +;; lib/feed/test-harness.sx — counter definitions for the feed conformance +;; suites, lifted from the inline epoch-2 defs in the old conformance.sh so the +;; shared driver (MODE=counters) can preload them before each suite. + +(define feed-test-pass 0) +(define feed-test-fail 0) +(define + feed-test + (fn + (name got expected) + (if + (= got expected) + (set! feed-test-pass (+ feed-test-pass 1)) + (set! feed-test-fail (+ feed-test-fail 1))))) diff --git a/plans/agent-briefings/conformance-loop.md b/plans/agent-briefings/conformance-loop.md index b3e16682..fe4c641d 100644 --- a/plans/agent-briefings/conformance-loop.md +++ b/plans/agent-briefings/conformance-loop.md @@ -87,7 +87,7 @@ Blocked with specifics and move to the next candidate next iteration. - [x] common-lisp — migrated 487/487 (counters; driver extended for per-suite counters+preloads) - [x] erlang — migrated 761/761 (dict; pass/count → :failed = count-pass) -- [ ] feed +- [x] feed — migrated 189/189 (counters; test-harness.sx preload for counters+helper) - [ ] forth - [ ] go - [ ] js @@ -100,6 +100,13 @@ Blocked with specifics and move to the next candidate next iteration. ## Progress log (newest first) +- 2026-06-07 — feed: migrated to `MODE=counters`, 189/189 exact parity (basic 30, + fanout 29, rank 24, integration 22, content 15, notify 8, home 6, dedupe 9, trending 11, + mute 9, page 14, thread 12). Canonical counters shape: fresh session per suite, shared + preloads, single feed-test-pass/feed-test-fail pair. Lifted the old script's inline + epoch-2 counter+helper defs into lib/feed/test-harness.sx (preloaded last). No driver + change — only conformance.conf + test-harness.sx + shim. Kept historical scoreboard + schema (suites{name:{pass,fail}}, total_pass/total_fail/total). - 2026-06-07 — erlang: migrated to `MODE=dict`, 761/761 exact parity (tokenize 62, parse 52, eval 408, runtime 93, ring 4, ping-pong 4, bank 8, echo 7, fib 8, ffi 37, vm 78). Erlang exposes pass + *count* (total) counters, not pass/fail, so each suite's