#!/usr/bin/env bash # forliteralcheck.sh — THE LITERAL ANCHOR of a --for answer whose task names a literal (lane MF1-LIT; src/forliteral.h). # # THE CONTRACT THIS PINS (arms tagged [P] are RED on the pre-change binary 251ac97c; [G] arms are twins and near-miss negatives # that hold on both binaries and must keep holding — PROCESS rule 4, no gate erosion): # # TRIGGER CLI --for (XML, default regime, one root) whose task carries a "double-quoted message" (two words or more) or a # CONST_SHAPED word (APPTOOL_RENDER_MODE, PYTHONASYNCIODEBUG). # WHAT a legend comment + just before : # source sites (sites in a function first), owner bodies, excerpts. # NOT a comment, a docstring, a test path or a doc file is never an row (counted only); no literal, no hit, # --json and --token-budget carry none (byte-identical to RIPWIRE_NO_LITERAL_ANCHOR=1). # # Arms: # L1 [P] --for=APPTOOL_RENDER_MODE: shape="const"; config.py k="string" in="read_mode", runner.py in="debug_mode" and the # a_table.py rows (src="3", several sites listed, never one picked); the read_mode body is printed whole # L1b [P] the first row is the site inside a function (config.py), not a_table.py, which the scan's path order puts first # L1c [P] the a_table.py string site (outside any function) carries lines="3-5" and the entry after it # L2 [P] the counts: comment="2" (a # comment and a docstring), test="1", doc="1"; none of those files' lines is an row # L3 [P] a quoted message ("dependency cycle detected" error): shape="quoted", the check_cycle row; the ranked answer already # prints check_cycle's body, so the row says body="above" and the body appears exactly once # L4 [P] a body over the cap (big_dispatch): an excerpt holding the site line, of= the body's lines, next="--expand=…big_dispatch" # L2b [P] a triple-quoted string is a docstring only as the FIRST statement of a module / class / def body (Python): a `"""` # opening a call-argument line (raise ValueError(\n """msg""")), a bare string in an `if` body and a Kotlin raw # string and a GDScript triple-quoted string at the top of the file are string SITES (rows), the four real docstrings stay # counted (comment="4") # L4b [P] two far-apart sites in one over-ten-line body: ONE window per site, joined by an ellipsis, never the span between # L4c [P] a ten-line body is printed whole (); an eleven-line body is an excerpt [site-1, site+5] plus its signature line # L4d [G] a ten-line body over 3000 bytes is still an excerpt (the byte cap on a short body), the long line left out # L10 [G] caps: ten one-site readers: shown="8" capped="1", exactly three / bodies # L11 [P] the compact legend (the default) is a short form; --legend=full keeps the long text; the payload is the same # L5 [G] no literal: a lower-case word, a ONE-word quoted value ("full", and "detected", which a source string holds), a # backticked span, a 4-byte CONST (ABC_D): no … equals RIPWIRE_NO_LITERAL_ANCHOR=1 byte for byte # L8 [G] --json, --token-budget=3000, --format=candidates and a two-root call (--format=columnar is refused with --for): no ) # X1 [G] determinism X2 [G] xmllint well-formed X3 [G] no "--" inside the lit legend comment # N2 [train 42b T-5] `x = x + 1 # note:` is no def header: the bare string after it is a row, not a docstring (twin: the same without the comment) # N1 [train 42b T-3] an excerpt line over 200 bytes is cut on a UTF-8 boundary with cut="1" (a 48.9 KB minified body); twin: none # # Usage: bash test/forliteralcheck.sh [BIN] | RIPWIRE_BIN=asan/ripwire bash test/forliteralcheck.sh # BIN is $1, else RIPWIRE_BIN, else build/ripwire; the first output line names the binary used. Exits non-zero on any failure. set -u ROOT="$( cd "$( dirname "$0" )/.." && pwd )" . "$ROOT/test/lib/clean-env.sh" unset RIPWIRE_NO_LITERAL_ANCHOR BIN="${1:-${RIPWIRE_BIN:-$ROOT/build/ripwire}}" [ "${BIN#/}" = "$BIN" ] && BIN="$ROOT/$BIN" echo "forliteralcheck: BIN=$BIN" fail=0 ok(){ printf ' PASS %s\n' "$*" || { fail=1; printf ' FAIL could not write the PASS line for: %s\n' "$*"; }; return 0; } no(){ printf ' FAIL %s\n' "$*"; fail=1; } [ -x "$BIN" ] || { no "binary not executable: $BIN"; exit 1; } TMP="$( mktemp -d )"; trap 'rm -rf "$TMP"' EXIT export HOME="$TMP/home" TMPDIR="$TMP/"; mkdir -p "$HOME" FIX="$ROOT/test/forliteralfix" run(){ ( cd "$FIX" || exit 1; "$BIN" "$FIX" --no-cache "$@" /dev/null ); } off(){ ( cd "$FIX" || exit 1; RIPWIRE_NO_LITERAL_ANCHOR=1 "$BIN" "$FIX" --no-cache "$@" /dev/null ); } lit(){ python3 -I -c 'import sys;s=sys.stdin.read();i=s.find("",i)+6] if i>=0 else "")'; } has(){ grep -qF -- "$2" <<<"$1"; } # ── L1 ── A="$( run --for=APPTOOL_RENDER_MODE )"; rc=$? L="$( lit <<<"$A" )" if [ "$rc" -eq 0 ] && [ -n "$L" ] && has "$L" 's="APPTOOL_RENDER_MODE" shape="const"' && has "$L" 'src="3"' \ && has "$L" ''; then ok "L1 const literal: every source site listed (src=3), read_mode body printed" else no "L1 const literal block wrong (rc=$rc): ${L:0:600}"; fi # ── L1b / L1c ── first="$( grep -o '' && has "$L" '"OTHER": "flag",'; then ok "L1c a string site outside any function carries the lines after it (lines=3-5)" else no "L1c table row context wrong: ${L:0:600}"; fi # ── L2 ── if has "$L" 'comment="2"' && has "$L" 'test="1"' && has "$L" 'doc="1"' && ! has "$L" 'p="tests/' && ! has "$L" 'p="README.md"' \ && ! has "$L" 'in="helper"' && ! has "$L" 'l="7" k='; then ok "L2 comment/docstring/test/doc sites counted (2/1/1), never an row" else no "L2 counts or rows wrong: ${L:0:600}"; fi # ── L3 ── QA="$( run '--for="dependency cycle detected" error' )"; Q="$( lit <<<"$QA" )" nb="$( grep -o 'n="check_cycle">]*>",sys.argv[1],re.S);print(len(m.group(1).split(chr(10))) if m else -1)' "$F" )" if has "$F" '' && ! has "$T10" '()","",s,flags=re.S),end="")' )" b="$( off "--for=$t" )" if [ -n "$b" ] && [ "$a" = "$b" ]; then ok "L7 prefix identity vs RIPWIRE_NO_LITERAL_ANCHOR=1: $t"; else no "L7 answer outside the block changed: $t"; fi done # ── L8 ── for f in --json --token-budget=3000 --format=candidates; do a="$( run --for=APPTOOL_RENDER_MODE "$f" )"; b="$( off --for=APPTOOL_RENDER_MODE "$f" )" if [ -n "$a" ] && [ "$a" = "$b" ] && ! has "$a" ' "$TMP/root2/second.py" a="$( cd "$FIX" || exit 1; "$BIN" "$FIX" "$TMP/root2" --no-cache --for=APPTOOL_RENDER_MODE /dev/null )" b="$( cd "$FIX" || exit 1; RIPWIRE_NO_LITERAL_ANCHOR=1 "$BIN" "$FIX" "$TMP/root2" --no-cache --for=APPTOOL_RENDER_MODE /dev/null )" if [ -n "$a" ] && [ "$a" = "$b" ] && ! has "$a" '",sys.stdin.read(),re.S);print(m.group(0) if m else "MISSING",end="")'; } noLegend(){ python3 -I -c 'import sys,re;print(re.sub(r"","",sys.stdin.read(),flags=re.S),end="")'; } LC="$( run --for=APPTOOL_RENDER_MODE | legendOf )"; LF="$( run --for=APPTOOL_RENDER_MODE --legend=full | legendOf )" PC="$( run --for=APPTOOL_RENDER_MODE | lit | noLegend )"; PF="$( run --for=APPTOOL_RENDER_MODE --legend=full | lit | noLegend )" # train 42b T-4 (rv-mf1-lit-delta N3): the compact form names every attribute the element prints (hits= src= shown= capped= shape= # bytes= next= and T-3's cut=) and says of= is the body's line range, never "whole": the ceiling moved 340 -> 450 for those names only if [ "${#LC}" -le 450 ] && [ "${#LC}" -ge 250 ] && [ "${#LF}" -ge 700 ] && has "$LF" 'docs, shell, config' && [ "$LC" != "MISSING" ] \ && has "$LC" 'k=' && has "$LC" 'comment=' && has "$LC" 'floor=' && has "$LC" 'body=above' && [ -n "$PC" ] && [ "$PC" = "$PF" ] \ && has "$LC" 'hits=' && has "$LC" 'src=' && has "$LC" 'shown=' && has "$LC" 'capped=' && has "$LC" 'shape=' && has "$LC" 'bytes=' \ && has "$LC" 'next=' && has "$LC" 'cut=' && has "$LF" 'cut=' && has "$LC" 'of= body lines' && ! has "$LC" 'of= whole'; then ok "L11 compact legend ${#LC} B (the default); --legend=full keeps ${#LF} B naming docs, shell, config; same payload" else no "L11 legend sizes wrong: compact=${#LC} full=${#LF} samepayload=$([ "$PC" = "$PF" ] && echo y || echo n)"; fi # ── N1 (train 42b T-3, rv-mf1-lit-delta N1): an excerpt cuts each line at 200 bytes (a minified one-line body printed 48.9 KB) ── MIN="$TMP/min"; mkdir -p "$MIN/web" python3 -I -c 'import sys;b="".join("var v%d=a+%d;"%(i,i) for i in range(3000));open(sys.argv[1],"w").write("function minFail(a){"+b+"throw new Error(\"bad things happened here minified\")}\n")' "$MIN/web/m.js" printf 'export function ok(){ return 1 }\n' >"$MIN/web/ok.js" MO="$( cd "$MIN" && "$BIN" "$MIN" --no-cache '--for=where is the "bad things happened here" error raised' /dev/null )" MX="$( python3 -I -c 'import sys,re;m=re.search(r"]*>.*?",sys.stdin.read(),re.S);print(m.group(0) if m else "MISSING",end="")' <<<"$MO" )" if [ "$MX" != "MISSING" ] && [ "${#MX}" -le 3000 ] && has "$MX" 'cut="1"' && has "$MX" 'l="1"' && has "$MX" 'function minFail(a)'; then ok "N1 a minified body's excerpt is cut per line (${#MX} B), cut=\"1\" disclosed, the site line kept" else no "N1 excerpt not capped: ${#MX} B, cut=$(has "$MX" 'cut="1"' && echo y || echo n)"; fi SX="$( run --for=APPTOOL_RENDER_MODE | lit )" if ! has "$SX" 'cut="1"'; then ok "N1 twin: short lines are never marked cut"; else no "N1 twin: a short-line answer says cut=\"1\""; fi # ── N2 (train 42b, rv-mf1-lit-delta N2): a trailing comment ending in a colon is no def header, so the next bare string is no docstring ── DOC="$TMP/doc"; mkdir -p "$DOC/app" printf 'def colon_comment(x):\n x = x + 1\n """bad things happened here in a bare mid-body string"""\n return x\n\n\ndef colon_comment2(x):\n x = x + 1 # note:\n """bad things happened here in a bare mid-body string two"""\n return x\n' >"$DOC/app/d.py" DO="$( cd "$DOC" && "$BIN" "$DOC" --no-cache '--for=where is the "bad things happened here" string' /dev/null | lit )" if has "$DO" '/dev/null 2>&1; then if printf '%s' "$A" | xmllint --noout - 2>/dev/null; then ok "X2 xmllint well-formed"; else no "X2 not well-formed"; fi else ok "X2 SKIP: xmllint not installed"; fi C="$( python3 -I -c 'import sys,re;m=re.search(r"",sys.stdin.read(),re.S);print(m.group(1) if m else "MISSING")' <<<"$A" )" if [ "$C" != "MISSING" ] && ! has "$C" '--'; then ok "X3 the lit legend comment holds no --"; else no "X3 legend comment missing or holds --"; fi [ "$fail" -eq 0 ] && echo "forliteralcheck: OK" || echo "forliteralcheck: FAILED" exit "$fail"