Co-authored-by: mos-dt-0 <[email protected]>
This commit was merged in pull request #1001.
This commit is contained in:
@@ -10,8 +10,9 @@ comparison come from independent code paths.
|
||||
Subcommands (all print sorted, stable output; non-zero exit on any failure):
|
||||
|
||||
expected The expected coordinate set from the ARTIFACT: one
|
||||
"<helper> <file>:<line+3>" row per denominator row (+3 = the
|
||||
uniform header shift the converter applied; converter-verified).
|
||||
"<helper> <file>:<line+7>" row per denominator row (+7 = 3
|
||||
converter header lines + 4 lines from the #984 source guard,
|
||||
uniform across all ten suites).
|
||||
Multi-grep lines stay ONE coordinate.
|
||||
|
||||
static The converted-site inventory from the SOURCE TEXT at the current
|
||||
@@ -23,9 +24,9 @@ Subcommands (all print sorted, stable output; non-zero exit on any failure):
|
||||
from the text.)
|
||||
|
||||
arms The forced-error arm list: the 19 denominator canaries plus one
|
||||
E-form arm (store-ack:733→736, a $(count_lines) capture compared
|
||||
afterward — the A6 shape) plus one F-form arm (quarantine:560→563,
|
||||
the multi-grep pipeline capture), as "<helper> <file>:<line+3>
|
||||
E-form arm (store-ack:733→740, a $(count_lines) capture compared
|
||||
afterward — the A6 shape) plus one F-form arm (quarantine:560→567,
|
||||
the multi-grep pipeline capture), as "<helper> <file>:<line+7>
|
||||
<form>". Both extras are asserted to exist in the artifact with
|
||||
the expected form — a renumber that moved them fails here, not
|
||||
silently downstream.
|
||||
@@ -33,7 +34,8 @@ Subcommands (all print sorted, stable output; non-zero exit on any failure):
|
||||
sweep Residual sweep: the denominator's own classifier (ported from the
|
||||
frozen derivation) over the ten suites at the current tree must
|
||||
find ZERO unconverted verdict-form grep sites; and, IN THE SAME
|
||||
RUN, six per-form specimens planted into a temp copy of a real
|
||||
RUN, eight specimens (six per-form + two absorb-branch probes, #985)
|
||||
planted into a temp copy of a real
|
||||
suite must ALL be found with their correct forms — an instrument
|
||||
that reports zero must first be seen finding what it claims to
|
||||
find (A5).
|
||||
@@ -50,7 +52,9 @@ HERE = Path(__file__).resolve().parent
|
||||
WAKE = HERE.parent
|
||||
ART = HERE / "denominator-089615f.json"
|
||||
|
||||
HEADER_SHIFT = 3 # converter inserted 3 header lines after SCRIPT_DIR in every suite
|
||||
HEADER_SHIFT = 7 # 3 converter header lines after SCRIPT_DIR + 4 lines from the
|
||||
# #984 source guard (1-line `. _wake-common.sh && wake_assert_init` became a 5-line
|
||||
# guarded block) — both uniform across all ten suites, both above every site.
|
||||
|
||||
# The two hand-picked extra arms (base coordinates; forms asserted at load).
|
||||
EXTRA_ARMS = [
|
||||
@@ -68,10 +72,14 @@ RX_ASSIGN_SUB = re.compile(r'=\s*"?\$\(.*grep')
|
||||
RX_IF = re.compile(r"^\s*(el)?if\s+.*grep")
|
||||
RX_GREP = re.compile(r"(^|[^A-Za-z0-9_.-])grep([^A-Za-z0-9_.-]|$)")
|
||||
|
||||
|
||||
def polarity(line):
|
||||
m = RX_FAIL_SAME.search(line)
|
||||
return "OR" if m.group(1) == "||" else "AND"
|
||||
# grep in COMMAND position: at line start or after a command separator / subshell
|
||||
# opener / shell keyword / `!`. Quote-unaware by design — a quoted "grep" after a
|
||||
# separator reads as a command and lands the line in residual, which fails LOUD;
|
||||
# the absorb direction (note) is the one that must never fire on a real verdict.
|
||||
RX_GREP_CMD = re.compile(
|
||||
r"(?:^|[;|&(`]|\$\(|\bif\b|\belif\b|\bthen\b|\belse\b|\bdo\b|\bwhile\b|\buntil\b|!)"
|
||||
r"\s*grep(?:\s|$)"
|
||||
)
|
||||
|
||||
|
||||
def classify(lines):
|
||||
@@ -137,6 +145,28 @@ def classify(lines):
|
||||
return sites, dispo
|
||||
|
||||
|
||||
def residual_sites(lines):
|
||||
"""classify() plus the absorb decision — the ONE path both sweep legs share.
|
||||
|
||||
A classified site is absorbed as a note only when its line carries a wake
|
||||
helper token AND the line shows no grep in command position: a converted
|
||||
line whose PATTERN argument merely contains the word grep. A helper line
|
||||
that also runs a real grep verdict (has_match ... && grep -q SECRET ... &&
|
||||
fail) stays residual (#985). Multi-line forms anchor the site at the line
|
||||
containing grep, so a command-position grep on a continuation line never
|
||||
shares its line with the helper token and stays residual by construction.
|
||||
"""
|
||||
sites, dispo = classify(lines)
|
||||
residual, notes = [], []
|
||||
for ln, form, text in sites:
|
||||
line = lines[ln - 1]
|
||||
if RX_HELPER.search(line) and not RX_GREP_CMD.search(line):
|
||||
notes.append((ln, form, text))
|
||||
else:
|
||||
residual.append((ln, form, text))
|
||||
return residual, notes, dispo
|
||||
|
||||
|
||||
def load_art():
|
||||
art = json.loads(ART.read_text())
|
||||
assert art["total"] == 261 == len(art["rows"]), "artifact self-consistency"
|
||||
@@ -199,13 +229,20 @@ def cmd_arms():
|
||||
return 0
|
||||
|
||||
|
||||
# (expected classify form, expected disposition through residual_sites, snippet)
|
||||
PLANTS = [
|
||||
("A-same-line", ['grep -q needle haystack || fail "plant-A"']),
|
||||
("B-cont-operator", ["grep -q needle haystack ||", ' fail "plant-B"']),
|
||||
("C-cont-backslash", ["grep -q needle \\", ' haystack || fail "plant-C"']),
|
||||
("D-if-form", ["if ! grep -q needle haystack; then", ' fail "plant-D"', "fi"]),
|
||||
("E-count-capture", ['[ "$(grep -c needle haystack)" = "1" ] || fail "plant-E"']),
|
||||
("F-extract-capture", ['val="$(grep needle haystack)"']),
|
||||
("A-same-line", "residual", ['grep -q needle haystack || fail "plant-A"']),
|
||||
("B-cont-operator", "residual", ["grep -q needle haystack ||", ' fail "plant-B"']),
|
||||
("C-cont-backslash", "residual", ["grep -q needle \\", ' haystack || fail "plant-C"']),
|
||||
("D-if-form", "residual", ["if ! grep -q needle haystack; then", ' fail "plant-D"', "fi"]),
|
||||
("E-count-capture", "residual", ['[ "$(grep -c needle haystack)" = "1" ] || fail "plant-E"']),
|
||||
("F-extract-capture", "residual", ['val="$(grep needle haystack)"']),
|
||||
# G: a converted line that ALSO runs a raw grep verdict — the helper token
|
||||
# must not absorb it (#985)
|
||||
("A-same-line", "residual", ['has_match -q needle "$F" && grep -q SECRET "$F" && fail "plant-G"']),
|
||||
# H: negative control — helper whose PATTERN argument is the word grep;
|
||||
# must be absorbed as a note, never residual
|
||||
("A-same-line", "note", ['has_match -q "grep" haystack || fail "plant-H"']),
|
||||
]
|
||||
|
||||
|
||||
@@ -215,22 +252,20 @@ def cmd_sweep():
|
||||
|
||||
# leg 1: real suites at the current tree must be residual-free
|
||||
for f in suite_files(art):
|
||||
lines = (WAKE / f).read_text().split("\n")
|
||||
sites, _dispo = classify(lines)
|
||||
residual = []
|
||||
for ln, form, text in sites:
|
||||
if RX_HELPER.search(lines[ln - 1]):
|
||||
# converted line whose PATTERN argument contains the word grep:
|
||||
# not an unconverted site, but never silently absorbed either
|
||||
print(f"SWEEP-NOTE {f}:{ln} converted line matches grep-token ({form}): {text[:80]}")
|
||||
continue
|
||||
residual.append((ln, form, text))
|
||||
residual, notes, _dispo = residual_sites((WAKE / f).read_text().split("\n"))
|
||||
for ln, form, text in notes:
|
||||
# converted line whose PATTERN argument contains the word grep:
|
||||
# not an unconverted site, but never silently absorbed either
|
||||
print(f"SWEEP-NOTE {f}:{ln} converted line matches grep-token ({form}): {text[:80]}")
|
||||
for ln, form, text in residual:
|
||||
print(f"SWEEP-RESIDUAL {f}:{ln} {form}: {text[:100]}")
|
||||
bad += 1
|
||||
print(f"SWEEP {f}: {len(residual)} residual verdict site(s)")
|
||||
|
||||
# leg 2, SAME RUN: the instrument must find six per-form plants
|
||||
# leg 2, SAME RUN, SAME PATH as leg 1: the instrument must find every plant
|
||||
# with the right form AND the right absorb disposition — plants G/H exercise
|
||||
# the absorb branch itself, so this leg must go through residual_sites(),
|
||||
# not raw classify()
|
||||
donor = suite_files(art)[0]
|
||||
with tempfile.TemporaryDirectory() as td:
|
||||
planted = Path(td) / donor
|
||||
@@ -238,25 +273,28 @@ def cmd_sweep():
|
||||
base_lines = planted.read_text().split("\n")
|
||||
offset = len(base_lines)
|
||||
expect = {}
|
||||
for form, snippet in PLANTS:
|
||||
expect[offset + 1] = form # first physical line of each plant
|
||||
for form, dispo, snippet in PLANTS:
|
||||
expect[offset + 1] = (form, dispo) # first physical line of each plant
|
||||
base_lines.extend(snippet)
|
||||
offset = len(base_lines)
|
||||
planted.write_text("\n".join(base_lines))
|
||||
sites, _ = classify(planted.read_text().split("\n"))
|
||||
found = {ln: form for ln, form, _t in sites if ln in expect}
|
||||
unexpected = [(ln, form) for ln, form, _t in sites if ln not in expect]
|
||||
hits = sum(1 for ln, form in expect.items() if found.get(ln) == form)
|
||||
print(f"SWEEP-PLANTS found={hits}/6 in planted copy of {donor}")
|
||||
if hits != 6:
|
||||
for ln, form in sorted(expect.items()):
|
||||
got = found.get(ln, "<missed>")
|
||||
if got != form:
|
||||
print(f"SWEEP-PLANT-MISS line {ln}: expected {form}, got {got}")
|
||||
residual, notes, _ = residual_sites(planted.read_text().split("\n"))
|
||||
found = {ln: (form, "residual") for ln, form, _t in residual}
|
||||
found.update({ln: (form, "note") for ln, form, _t in notes})
|
||||
unexpected = [(ln, form) for ln, form, _t in residual if ln not in expect]
|
||||
hits = sum(1 for ln, want in expect.items() if found.get(ln) == want)
|
||||
n_plants = len(PLANTS)
|
||||
print(f"SWEEP-PLANTS found={hits}/{n_plants} in planted copy of {donor}")
|
||||
if hits != n_plants:
|
||||
for ln, want in sorted(expect.items()):
|
||||
got = found.get(ln, ("<missed>", "<missed>"))
|
||||
if got != want:
|
||||
print(f"SWEEP-PLANT-MISS line {ln}: expected {want}, got {got}")
|
||||
bad += 1
|
||||
if unexpected:
|
||||
# the donor is a converted suite: any non-plant site the sweep finds
|
||||
# in the copy contradicts the zero it just reported on the original
|
||||
# the donor is a converted suite: any non-plant RESIDUAL site in the
|
||||
# copy contradicts the zero leg 1 just reported on the original
|
||||
# (non-plant notes mirror leg 1's treatment: printed there, not bad)
|
||||
for ln, form in unexpected:
|
||||
print(f"SWEEP-PLANT-UNEXPECTED {donor}(copy):{ln} {form}")
|
||||
bad += 1
|
||||
|
||||
Reference in New Issue
Block a user