forge: fuzz probe refactor verification - correct line count/filename citations, add negctrl test, commit evidence files

This commit is contained in:
forge committed 2026-10-24 13:39:55 +00:00
1 parent c6464ef8e0
commit 840ef98fe7
10 files changed
+955 -9

No files matched your search

@@ -24,7 +24,6 @@ in the main loop with no abstraction. No `if __name__ == "__main__"` guard.
- Added `if __name__ == "__main__"` guard.
## Evidence (measured)
Both runs executed against the live chunker at
`/root/.hermes/wizard-outputs/forge/chunker.py`.
@@ -34,32 +33,50 @@ total=383 roundtrip_fails=0 bounds_failures=0
RESULT: ALL PASS
EXIT=0
$ python3 _fuzz_probe_refactored.py (refactored, 139 lines)
$ python3 _fuzz_probe_refactored.py (refactored, 155 lines)
total=383 roundtrip_fails=0 bounds_failures=0
RESULT: ALL PASS
EXIT=0
```
Byte-for-byte output equivalence:
Byte-for-byte output equivalence (both captured into committed artifacts):
```
$ diff baseline_fuzz_output.txt refactored_fuzz_output.txt
OUTPUT IDENTICAL
$ # no output — files are identical
```
py_compile:
```
$ python3 -m py_compile _fuzz_probe_refactored.py
PY_COMPILE_OK
$ # no output — compiles clean
```
Cross-check — all other chunker probes still pass with the same chunker:
## Negative control (probe is discriminating)
A green-only probe grades nothing. `_fuzz_probe_negctrl.py` monkey-patches
`chunker.chunk_text` with a truncating mutant — for all-`a` inputs only, the
last character of the final chunk is dropped — then runs the refactored probe
against the mutant. The probe **must** detect the injection:
```
$ python3 _fuzz_probe_negctrl.py
total=383 roundtrip_fails=105 bounds_failures=0
RESULT: HAS FAILURES
```
105 of 383 cases fail — every "hard-mt{mt}-ot{ot}" case whose text is `"a"*500`,
which is 1+2+...+14 = 105 (triangular sum over mt=2..15). The original probe
catches the same 105 against the same mutant, so the refactor preserves
detection case-for-case, not just the totals. The negctrl exits 0 = probe
caught the bug; 1 = probe missed a real truncation (control failed).
## Cross-check — all other chunker probes still pass with the same chunker:
```
$ python3 _edge_sweep_refactored.py
total=600 roundtrip_fails=0 bounds_fails=0
RESULT: ALL PASS
EXIT=0
$ python3 _test_roundtrip_refactored.py
$ python3 _test_roundtrip.py
=== Results: 34 passed, 0 failed ===
EXIT=0
@@ -73,7 +90,7 @@ EXIT=0
| metric | original | refactored |
|-------------------|----------|------------|
| lines | 64 | 139 (incl. docstrings) |
| lines | 64 | 155 (incl. docstrings) |
| function defs | 1 (dead) | 4 (all used) |
| dead code | 1 unused fn | 0 |
| total cases | 383 | 383 |
@@ -86,4 +103,5 @@ Refactoring is behavior-preserving and measurably equivalent: 383 cases,
identical output, exit 0. The dead `roundtrip()` helper is now wired in,
the bounds check is a pure generator, module-global counters are a Stats
dataclass, and all 2400 adversarial trials across the wider probe suite
still pass with no regressions.
still pass with no regressions. The negative control proves the probe is
discriminating — it catches a truncating-chunker mutant on 105/383 cases.
+151
View File
@@ -0,0 +1,151 @@
#!/usr/bin/env python3
"""Adversarial property test for the refactored chunker.
Targeted at the one invariant the docstring makes a strong claim about:
``reconstruct(chunk_text(t, m, o), o) == t.strip()`` for EVERY input,
including:
* repetitive sentences whose carried-overlap tail can coincidentally
equal a prefix of the next chunk's content (the genuine-vs-context
discriminator in ``reconstruct``);
* long repetitive-token sentences that hard-split, immediately followed
by a sentence-grouped chunk whose content begins with the same run
the overlap tail carries (genuine-overlap branch mis-taken as context);
* arbitrary inter-sentence whitespace (double space, tab, newline,
mixed runs) that the original bug collapsed.
If this exits 0, the lossless claim is backed by >= N randomised trials
across all three adversarial shapes. If it exits 1, it prints the first
counterexample with a full structural dump so the bug is reproducible.
"""
from __future__ import annotations
import random
import sys
sys.path.insert(0, "/root/.hermes/wizard-outputs/forge")
import chunker # noqa: E402
S = chunker._OVERLAP_SENTINEL
def shape_long_rep_then_sentence(seed: int) -> str:
"""Hard-split long repetitive sentence immediately followed by a
sentence chunk whose content begins with the *same* token run as the
long sentence's tail -- forces the genuine-overlap discriminator to be
exercised on a context-overlap site."""
rng = random.Random(seed)
tok = rng.choice(["word", "x", "abc", "node"])
# long sentence of repeated tokens (no real EOS until the end)
long_sent = " ".join([tok] * rng.randint(40, 220)) + "."
# next sentence repeats the tail token so the overlap tail (last oc chars
# of the long sentence) coincides with the next chunk's prefix.
next_sent = (tok + " ") * rng.randint(2, 8) + "tail."
# vary inter-sentence whitespace
sep = rng.choice([" ", " ", "\t", "\n", "\n\n", " \n ", " \t "])
return long_sent + sep + next_sent + (sep + "More " + tok + " words.\n\n") * 2
def shape_repetitive_sentences(seed: int) -> str:
"""Repeated sentence groups (the docstring's primary adversarial class)."""
rng = random.Random(seed)
groups = []
for _ in range(rng.randint(10, 60)):
g = rng.choice(
[
"Sentence one. ",
"Sentence two. ",
"Sentence three. ",
"Alpha beta. ",
"Gamma delta. ",
]
)
# occasionally insert weird interior whitespace
if rng.random() < 0.3:
g = g.replace(" ", rng.choice([" ", "\t", " \n "]))
groups.append(g)
return "".join(groups)
def shape_whitespace_variety(seed: int) -> str:
"""Lots of short sentences joined by weird whitespace runs."""
rng = random.Random(seed)
sents = ["Hello world", "How are you", "I am fine", "Test sentence"]
seps = [" ", " ", " ", "\t", "\n", "\n\n", " \n ", " \t "]
parts = []
for _ in range(rng.randint(5, 40)):
parts.append(rng.choice(sents))
parts.append(rng.choice(seps))
# trailing whitespace to exercise strip boundary
parts.append(rng.choice(seps))
return "".join(parts)
def shape_fuzz(seed: int) -> str:
rng = random.Random(seed)
words = ["alpha", "bravo", "charlie", "delta", "echo", "foxtrot",
"golf", "hotel", "india", "juliet"]
seps = [" ", " ", " ", "\t", "\n", "\n\n"]
parts = []
n = rng.randint(1, 8)
for _ in range(n):
nw = rng.randint(2, 12)
parts.append(" ".join(rng.choice(words) for _ in range(nw)))
parts.append(rng.choice([".", ".", ".", "?", "!", "..."]))
parts.append(rng.choice(seps))
return "".join(parts).strip() + rng.choice(["", "", " ", " \n "])
def check(text: str, mt: int, ot: int) -> bool:
if not text.strip():
return True
expect = text.strip()
cs = chunker.chunk_text(text, max_tokens=mt, overlap_tokens=ot)
rec = chunker.reconstruct(cs, ot)
return rec == expect
def main() -> int:
shapes = [shape_long_rep_then_sentence, shape_repetitive_sentences,
shape_whitespace_variety, shape_fuzz]
mts = [8, 10, 12, 16, 20, 24, 32, 40, 48, 64, 72, 100, 128]
ots = [0, 1, 2, 3, 4, 6, 8, 12, 16]
trials_per_shape = 600
total = 0
fails = 0
for si, shape in enumerate(shapes):
for i in range(trials_per_shape):
seed = si * 100000 + i
text = shape(seed)
mt = random.Random(seed).choice(mts)
ot = random.Random(seed + 1).choice(ots)
if ot >= mt:
ot = max(0, mt - 1)
total += 1
if not check(text, mt, ot):
fails += 1
print(f"FAIL shape={shape.__name__} seed={seed} mt={mt} ot={ot}")
print(f" text(:120)={text[:120]!r}... len={len(text)}")
cs = chunker.chunk_text(text, max_tokens=mt, overlap_tokens=ot)
rec = chunker.reconstruct(cs, ot)
exp = text.strip()
for j in range(min(len(rec), len(exp))):
if rec[j] != exp[j]:
print(f" FIRST DIFF at {j}")
print(f" exp: {exp[max(0,j-25):j+25]!r}")
print(f" rec: {rec[max(0,j-25):j+25]!r}")
break
else:
print(f" len exp={len(exp)} rec={len(rec)} "
f"(common prefix OK, lengths differ)")
if fails >= 3:
print("Stopping after 3 failures.")
return 1
print(f"trials={total} fails={fails}")
print("PASS: lossless round-trip across all adversarial shapes."
if fails == 0 else "FAIL: invariant violated.")
return 0 if fails == 0 else 1
if __name__ == "__main__":
sys.exit(main())
+64
View File
@@ -0,0 +1,64 @@
"""Adversarial fuzz probe for the forge chunker (evidence-gathering, not a fixture)."""
import sys
sys.path.insert(0, "/root/.hermes/wizard-outputs/forge")
import chunker
def roundtrip(text, mt, ot):
cs = chunker.chunk_text(text, max_tokens=mt, overlap_tokens=ot)
r = chunker.reconstruct(cs, ot)
exp = text.strip()
return r == exp, r, exp, cs
cases = []
for mt in range(2, 20):
for ot in range(1, mt):
cases.append(((("Sentence one. Sentence two. Sentence three. ") * 8), mt, ot, f"tiny-mt{mt}-ot{ot}"))
for mt in range(2, 16):
for ot in range(1, mt):
cases.append((("a" * 500), mt, ot, f"hard-mt{mt}-ot{ot}"))
for mt in range(2, 14):
for ot in range(0, mt):
t = ("The quick brown fox jumps over the lazy dog. Pack my box. ") * 5 + "a" * 200
cases.append((t, mt, ot, f"mixed-mt{mt}-ot{ot}"))
t_nosp = "Hello.World!How are you?I am fine. " * 10
for mt in range(4, 40, 4):
cases.append((t_nosp, mt, (mt // 2 if mt // 2 < mt else 1), f"no-after-space-mt{mt}"))
t_ws = "Hello world.\tHi now. \n\nNew para. Tab\there. " * 8
for mt in [8, 16, 32, 64]:
cases.append((t_ws, mt, mt // 2, f"ws-mt{mt}"))
cases.append((" \n\t ", 32, 8, "whitespace-only"))
cases.append(("", 32, 8, "empty-string"))
cases.append(("Just one.", 4, 1, "tiny-single"))
cases.append(("Hello. ", 8, 4, "trailing-ws"))
fails = 0
total = 0
bounds_fail = 0
for text, mt, ot, label in cases:
cs = chunker.chunk_text(text, max_tokens=mt, overlap_tokens=ot)
r = chunker.reconstruct(cs, ot)
exp = text.strip()
total += 1
if r != exp:
fails += 1
print(f"FAIL {label} mt={mt} ot={ot} len={len(text)} exp_len={len(exp)} got_len={len(r)}")
for i in range(min(len(r), len(exp))):
if r[i] != exp[i]:
print(f" firstdiff@{i} exp={exp[max(0,i-15):i+15]!r} got={r[max(0,i-15):i+15]!r}")
break
else:
print(f" prefix ok, len diff. exp_tail={exp[-20:]!r} got_tail={r[-20:]!r}")
print(f" nchunks={len(cs)}")
# bounds check
for c in cs:
body = c.partition(chunker._OVERLAP_SENTINEL)[2] if chunker._OVERLAP_SENTINEL in c else c
est = chunker.estimate_tokens(body)
if est > mt and body.strip():
bounds_fail += 1
print(f"BOUNDS-FAIL {label} mt={mt} chunk est={est} > {mt}: {body[:50]!r}")
print(f"\ntotal={total} roundtrip_fails={fails} bounds_failures={bounds_fail}")
print("RESULT:", "ALL PASS" if fails == 0 and bounds_fail == 0 else "HAS FAILURES")
sys.exit(0 if fails == 0 and bounds_fail == 0 else 1)
+53
View File
@@ -0,0 +1,53 @@
#!/usr/bin/env python3
"""Negative control for ``_fuzz_probe_refactored.py``.
Proves the probe is discriminating, not just green-on-green.
We monkey-patch ``chunker.chunk_text`` with a truncating mutant: for all-``a``
inputs only, the last character of the final chunk is dropped. Then we run the
refactored probe's ``main()`` against the mutant. The probe **must** detect
the injection and report failures -- a green-only probe grades nothing.
Measured (this run):
total=383 roundtrip_fails=105 bounds_failures=0
RESULT: HAS FAILURES
EXIT=1
105 of 383 cases are the "hard-mt{mt}-ot{ot}" family (``"a" * 500``), exactly
the cases whose text is all ``a``. The original probe catches the same 105
against the same mutant, so the refactor preserves detection case-for-case.
Exit contract: 0 = the probe caught the mutant (control passed);
1 = the probe went green on a buggy chunker (control failed -- vacuous probe).
"""
from __future__ import annotations
import sys
sys.path.insert(0, "/root/.hermes/wizard-outputs/forge")
import chunker
_orig_chunk_text = chunker.chunk_text
def _truncating_chunk_text(text: str, max_tokens: int, overlap_tokens: int):
"""Mutant ``chunk_text``: drop the last character of the final chunk
when the (stripped) input is composed entirely of ``'a'``."""
cs = _orig_chunk_text(text, max_tokens, overlap_tokens)
body = text.strip()
if body and set(body) == {"a"}:
cs = list(cs)
cs[-1] = cs[-1][:-1]
return cs
chunker.chunk_text = _truncating_chunk_text
import _fuzz_probe_refactored as probe # noqa: E402
if __name__ == "__main__":
rc = probe.main()
# Invert: probe returning non-zero means it caught the bug (good);
# probe returning 0 means it missed a real truncation (bad).
sys.exit(1 if rc == 0 else 0)
@@ -0,0 +1,3 @@
total=383 roundtrip_fails=105 bounds_failures=0
RESULT: HAS FAILURES
+117
View File
@@ -0,0 +1,117 @@
"""Comprehensive round-trip test for the forge (refactored) chunker.
Covers:
1. Repetitive sentence-grouped text (the probe case)
2. Long single sentence -> triggers _hard_split_long
3. Mixed: some sentences fit, some need hard split
4. Edge cases: empty, whitespace-only, single chunk, no overlap
5. Many random-ish inputs with varying params
"""
import sys
import random
import string
sys.path.insert(0, "/root/.hermes/wizard-outputs/forge")
import chunker
PASS = 0
FAIL = 0
def check(name, text, max_tokens=256, overlap_tokens=32):
global PASS, FAIL
if not text.strip():
expected = ""
else:
expected = text.strip()
cs = chunker.chunk_text(text, max_tokens=max_tokens, overlap_tokens=overlap_tokens)
r = chunker.reconstruct(cs, overlap_tokens)
ok = r == expected
status = "PASS" if ok else "FAIL"
if ok:
PASS += 1
else:
FAIL += 1
print(f"[{status}] {name}")
print(f" orig_len={len(expected)} recon_len={len(r)}")
for i in range(min(len(r), len(expected))):
if r[i] != expected[i]:
print(f" FIRST DIFF at char {i}")
print(f" orig: {expected[max(0,i-30):i+30]!r}")
print(f" recon: {r[max(0,i-30):i+30]!r}")
break
else:
print(f" common prefix OK but lengths differ: {len(r)} vs {len(expected)}")
print(f" orig tail: {expected[-30:]!r}")
print(f" recon tail: {r[-30:]!r}")
print(f" nchunks={len(cs)}")
# show if any chunk exceeded max_tokens
for i, c in enumerate(cs):
# strip the overlap tail for token check
actual = c
if chunker._OVERLAP_SENTINEL in actual:
actual = actual.partition(chunker._OVERLAP_SENTINEL)[2]
est = chunker.estimate_tokens(actual)
if est > max_tokens:
print(f" chunk[{i}] exceeds max_tokens: est={est} > {max_tokens}, content={actual[:60]!r}")
return ok
# Case 1: repetitive sentence-grouped text
base = "The quick brown fox jumps over the lazy dog. Pack my box with five dozen liquor jugs. How vexingly quick daft zebras jump. "
text = (base + "Variant sentence here. ") * 20
check("repetitive-sentences", text, max_tokens=64, overlap_tokens=8)
# Case 2: long single sentence -> hard split
long_sent = "Word" + " word" * 200 + " end." # ~1000 chars, way over 80
check("long-sentence-hard-split", long_sent, max_tokens=20, overlap_tokens=4)
# Case 2b: long single sentence with sentence-ending punctuation
long_sent2 = " ".join(["Sentence"] + ["word"]*300 + ["."])
check("long-sentence-hard-split-v2", long_sent2, max_tokens=30, overlap_tokens=6)
# Case 3: mixed - some short, some long sentences
mixed = "Short one. " + long_sent + " Another short. " + long_sent2 + " Final."
check("mixed-sizes", mixed, max_tokens=20, overlap_tokens=4)
# Case 4: edge cases
check("empty-string", "", max_tokens=64, overlap_tokens=8)
check("whitespace-only", " \n\t ", max_tokens=64, overlap_tokens=8)
check("single-sentence", "Just one sentence here.", max_tokens=64, overlap_tokens=8)
check("two-sentences-no-overlap", "First sentence. Second sentence.", max_tokens=256, overlap_tokens=0)
check("small-overlap", "First sentence. Second sentence.", max_tokens=64, overlap_tokens=2)
# Case 5: multiple sentences exactly at boundary
exactly = " ".join(f"Sentence number {i}." for i in range(10))
check("boundary-fit", exactly, max_tokens=10, overlap_tokens=2)
# Case 6: text with special chars, multiple spaces
special = "Hello world. How are you? I am fine. " * 5
check("multi-space", special, max_tokens=32, overlap_tokens=4)
# Case 7: text starting/ending with whitespace
ws_text = " The quick brown fox. The lazy dog. "
check("leading-trailing-ws", ws_text, max_tokens=64, overlap_tokens=8)
# Case 8: single long sentence that needs hard split, with high overlap
long3 = "A" + " b" * 500 + " end."
check("long-with-high-overlap", long3, max_tokens=16, overlap_tokens=8)
# Case 9: random text fuzz
random.seed(42)
words = ["alpha", "beta", "gamma", "delta", "epsilon", "zeta", "eta", "theta"]
for trial in range(20):
sents = []
for _ in range(random.randint(3, 15)):
nwords = random.randint(3, 50)
s = " ".join(random.choice(words) for _ in range(nwords))
sents.append(s + ".")
text = " ".join(sents)
check(f"random-trial-{trial}", text, max_tokens=random.randint(16, 64),
overlap_tokens=random.randint(0, 8))
# Case 10: text with newlines
nl_text = "First paragraph.\n\nSecond paragraph here.\nThird line."
check("newlines", nl_text, max_tokens=64, overlap_tokens=8)
print(f"\n=== Results: {PASS} passed, {FAIL} failed ===")
if FAIL:
sys.exit(1)
@@ -0,0 +1,3 @@
total=383 roundtrip_fails=0 bounds_failures=0
RESULT: ALL PASS
+288
View File
@@ -0,0 +1,288 @@
"""chunker.py -- split text into token-estimate-bounded chunks with overlap (stdlib only).
The overlap carried between chunks is delimited by a unique sentinel so that
``reconstruct`` can invert ``chunk_text`` exactly (lossless round-trip) without
relying on suffix/prefix matching, which is ambiguous on repetitive input.
Architecture (this build):
Every overlap is *context-only* -- a verbatim copy of the previous chunk's
tail, prepended behind a sentinel and dropped verbatim by ``reconstruct``.
There is no "genuine-overlap" case (where the next chunk physically contains
the overlap as a real prefix), because ``_hard_split_long`` emits *adjacent,
non-overlapping* pieces. Collapsing the two overlap kinds into one means
``reconstruct`` needs no content-based discriminator at all: it always drops
the carried ``head`` and appends ``rest``, leaving the previous chunk intact.
That is unambiguous even when the text is highly repetitive and the overlap
ratio is degenerate (e.g. ``overlap_tokens == max_tokens - 1``), where a
``rest.startswith(head)`` test would conflate genuine and context overlap and
silently strip real content off the accumulator.
The previous refactor fixed *sentinel discrimination* (genuine vs context) and
removed a ``re.sub(" +", " ")`` collapse, but the round-trip was still lossy
on real whitespace: ``_split_sentences`` discarded the inter-sentence
separators, ``_split_into_chunks`` re-joined sentences with a bare ``" "``, and
``_apply_overlap`` / ``_hard_split_long`` ``.strip()``-ed the carried tails -- so
double spaces, tabs and newlines were silently collapsed to single spaces.
Fix (this build):
* ``_split_sentences`` now returns ``(sentence, trailing_ws)`` pairs, so the
concatenation of all pairs reproduces the source verbatim. Whitespace is
part of the data, not an afterthought.
* ``_split_into_chunks`` builds each chunk as an *exact substring* of the
source (the concatenation of its segments); no ``" "`` re-joining and no
``.strip()`` on accumulated content. A hard-split sentence's trailing
whitespace is included in the text passed to ``_hard_split_long`` (not
appended afterward), so it can never push the final piece past max_chars.
* ``_hard_split_long`` now produces **non-overlapping** pieces (adjacent
exact substrings of the source) instead of pieces that physically overlap
the previous piece. This removes the *genuine-overlap* case entirely:
every overlap is now *context-only*.
* ``_hard_split_long`` guards the token bound: a pure-whitespace fragment
stranded by the word-boundary backtrack is merged into the previous piece
only when the result stays within ``max_chars``; otherwise it is kept as
its own piece (harmless: ``reconstruct`` strips trailing whitespace, and
mid-text whitespace survives in the join). The previous code merged
unconditionally, which could overflow the last content piece past max_tokens.
* ``_apply_overlap`` prepends the previous chunk's tail as pure *context*
(sentinel-delimited) to every chunk after the first. Because hard-split
pieces no longer overlap each other, this context tail is never duplicated
inside the chunk's own content -- it is a copy, not a real prefix.
* ``reconstruct`` therefore needs **no discriminator**: it drops the carried
``head`` and appends ``rest`` verbatim, leaving the previous chunk intact.
This is unambiguous even on highly-repetitive text, where a
``rest.startswith(head)`` test would conflate genuine and context overlap
(the prior bug: each misfire stripped chars off the accumulator). No
synthetic ``" "`` is inserted; boundary whitespace lives in the previous
chunk's tail and is preserved.
Result: ``reconstruct(chunk_text(t, ...), ot) == t.strip()`` for every input,
including highly-repetitive text, degenerate high overlap ratios
(e.g. ``overlap_tokens == max_tokens - 1``) and arbitrary inter-sentence
whitespace (double spaces, tabs, newlines, leading/trailing runs). Each
chunk's estimated token count also satisfies ``estimate_tokens(body) <= max_tokens``
for non-whitespace content (``body`` = chunk with the carried-overlap head
stripped), which the previous build violated for sentences whose trailing
whitespace was appended post-hoc to a max_chars-saturated piece (e.g.
"Pack my box. " at max_tokens=3).
"""
from __future__ import annotations
import math
import re
__all__ = ["chunk_text", "reconstruct", "estimate_tokens"]
# rough heuristic: ~4 chars per token for English-ish text
CHARS_PER_TOKEN = 4
# Sentinel used to mark the carried-overlap region between adjacent chunks.
# It is unlikely to occur in natural text; if it does, it is escaped on
# chunking and unescaped on reconstruction so the round-trip stays lossless.
_OVERLAP_SENTINEL = "\x00OVL\x00"
# Matches the whitespace run that follows an end-of-sentence punctuation mark.
# The run is captured so separators survive splitting (lossless reconstruction).
_SENT_RE = re.compile(r"(?<=[.!?])(\s+)")
def estimate_tokens(s: str) -> int:
"""Estimate the token count of ``s`` using a char-per-token heuristic."""
if not s:
return 0
return max(1, math.ceil(len(s) / CHARS_PER_TOKEN))
# Backward-compatible alias (used by test_chunker_refactor.py).
_estimate_tokens = estimate_tokens
def _split_sentences(text: str) -> list[tuple[str, str]]:
"""Split *text* into ``(sentence, trailing_ws)`` pairs.
The concatenation of every ``sentence + trailing_ws`` reproduces *text*
verbatim (after the outer ``.strip()`` applied to *text* by ``chunk_text``),
so the round-trip never loses inter-sentence whitespace -- double spaces,
tabs, or newlines survive intact.
"""
if not text:
return []
parts = _SENT_RE.split(text)
# re.split with a capture group interleaves separators: sentence, sep,
# sentence, sep, ... Group them into (sentence, trailing_ws) pairs.
pairs: list[tuple[str, str]] = []
for i in range(0, len(parts), 2):
sent = parts[i]
sep = parts[i + 1] if i + 1 < len(parts) else ""
if sent or sep: # skip a degenerate all-empty pair at the tail
pairs.append((sent, sep))
return pairs
def _hard_split_long(text: str, max_chars: int) -> list[str]:
"""Word-aware hard split of a single over-long sentence into adjacent pieces.
Pieces are *exact*, **non-overlapping** substrings of *text* (no
``.strip()``), each no longer than *max_chars* (rounded down to the last
word boundary). They are adjacent -- ``"".join(pieces) == text`` -- so no
genuine-overlap discrimination is needed in ``reconstruct``. Any cross-piece
context is instead supplied uniformly by :func:`_apply_overlap` as
sentinel-delimited *context* overlap, which ``reconstruct`` drops
unconditionally. This is what makes the round-trip lossless even under
highly-repetitive text and high overlap ratios, where a suffix/prefix
discriminator is ambiguous.
"""
pieces: list[str] = []
n = len(text)
start = 0
while start < n:
end = min(start + max_chars, n)
if end < n:
boundary = text.rfind(" ", start, end)
if boundary > start:
end = boundary
piece = text[start:end]
if piece.strip():
pieces.append(piece)
elif pieces and len(pieces[-1]) + len(piece) <= max_chars:
# pure-whitespace fragment (e.g. one space of a double-space run that
# the word-boundary split stranded between two content pieces): merge
# it into the previous piece so the concatenation stays an *exact*
# substring of the source and nothing is silently dropped -- but only
# when the merge stays within max_chars, otherwise the token bound
# would be violated; an un-mergeable whitespace fragment is kept as
# its own piece (reconstruct() .strip()s trailing whitespace, so it
# is harmless, and losslessness is preserved for mid-text whitespace).
pieces[-1] += piece
elif piece:
pieces.append(piece)
# if pieces is empty this is leading whitespace; reconstruct() contracts
# to text.strip(), so it is intentionally not retained here.
if end >= n:
break
start = end
return pieces
def _apply_overlap(chunks: list[str], overlap_chars: int) -> list[str]:
"""Prepend a sentinel-delimited tail of the previous chunk to each chunk.
The tail is pure downstream context (it is *not* duplicated inside the chunk's
own content); ``.strip()`` is deliberately NOT applied, because that would
discard the trailing whitespace that makes the round-trip lossless.
"""
if overlap_chars <= 0 or len(chunks) <= 1:
return chunks
out = [chunks[0]]
# Iterate over consecutive (prev, cur) pairs -- no index arithmetic needed;
# each tail is exactly the previous chunk's suffix, which `reconstruct` drops
# unconditionally as pure context.
for prev, cur in zip(chunks, chunks[1:]):
out.append(prev[-overlap_chars:] + _OVERLAP_SENTINEL + cur)
return out
def _split_into_chunks(
text: str,
max_tokens: int,
max_chars: int,
overlap_chars: int,
) -> list[str]:
"""Core sentence-boundary chunking producing *base* chunks (no overlap).
Each chunk is the concatenation of whole ``(sentence, sep)`` segments, hence
an exact substring of the source: internal whitespace is preserved, never
re-joined with a synthesized ``" "``.
"""
segments = _split_sentences(text)
chunks: list[str] = []
cur = ""
for sent, sep in segments:
piece = sent + sep
candidate = cur + piece if cur else piece
if estimate_tokens(candidate) > max_tokens:
if cur:
chunks.append(cur) # cur already ends with its segment's sep
if estimate_tokens(piece) > max_tokens:
chunks.extend(_hard_split_long(piece, max_chars))
cur = ""
else:
cur = piece
else:
cur = candidate
if cur:
chunks.append(cur)
return _apply_overlap(chunks, overlap_chars)
def chunk_text(text: str, max_tokens: int = 256, overlap_tokens: int = 32) -> list[str]:
"""Split *text* into chunks each <= *max_tokens* estimated tokens.
*overlap_tokens* worth of the previous chunk's tail is carried into the
next chunk (sentinel-delimited). Reconstruction via :func:`reconstruct`
reverses this exactly.
Prefers sentence boundaries; falls back to a word-aware hard split for
sentences that exceed *max_tokens*.
"""
if max_tokens <= 0:
raise ValueError("max_tokens must be positive")
if overlap_tokens < 0 or overlap_tokens >= max_tokens:
raise ValueError("overlap_tokens must be in [0, max_tokens)")
if text is None or not text.strip():
return []
max_chars = max_tokens * CHARS_PER_TOKEN
overlap_chars = overlap_tokens * CHARS_PER_TOKEN
return _split_into_chunks(text, max_tokens, max_chars, overlap_chars)
def reconstruct(chunks: list[str], overlap_tokens: int = 32) -> str:
"""Invert :func:`chunk_text`: remove sentinel-delimited overlaps and join.
Exactly reverses chunking for any input (lossless), including highly
repetitive text and arbitrary inter-sentence whitespace.
Every overlap in this design is *context-only*: the carried ``head`` is a
verbatim copy of the previous chunk's tail, never duplicated inside
``rest``. ``reconstruct`` therefore unconditionally drops ``head`` and
appends ``rest`` verbatim, leaving the previous chunk intact -- no suffix/
prefix discriminator is required, which is what makes it unambiguous on
repetitive text where such matching would conflate genuine and context
overlap.
A single uniform rule applies to *every* chunk (including the first): the
overlap head (everything before the sentinel) is pure context and is
dropped, and ``rest`` (everything after the sentinel) is the genuine
continuation that is appended verbatim. The first chunk never carries a
real overlap by construction, so the rule reduces to "append verbatim"; a
stray sentinel is still stripped defensively as a no-op head. Because an
empty ``chunks`` list simply yields ``"".join([]).strip() == ""``, the
previous explicit empty-guard and the separate ``i == 0`` branch are
redundant and have been folded into one loop.
"""
parts: list[str] = []
for c in chunks:
if _OVERLAP_SENTINEL in c:
# The carried `head` is pure context (a copy of the previous chunk's
# tail), not a prefix duplicated inside `rest`. Drop it, keep the
# previous chunk intact, and append `rest` (the genuine
# continuation) verbatim. No synthetic separator is inserted:
# boundary whitespace already lives at the previous chunk's tail.
_, _, rest = c.partition(_OVERLAP_SENTINEL)
parts.append(rest)
else:
# No sentinel (e.g. overlap_tokens=0, or the first chunk): the chunk
# is a contiguous source substring; append verbatim.
parts.append(c)
return "".join(parts).strip()
if __name__ == "__main__":
sample = " ".join(f"Sentence number {i}." for i in range(50))
cs = chunk_text(sample, max_tokens=32, overlap_tokens=4)
print("chunks:", len(cs))
for c in cs:
print("-", c[:60], "...")
print("reconstructed len:", len(reconstruct(cs, 4)))
@@ -0,0 +1,3 @@
total=383 roundtrip_fails=0 bounds_failures=0
RESULT: ALL PASS
@@ -0,0 +1,246 @@
#!/usr/bin/env python3
"""verify_path_claim_apply_baseline_refactor.py
Equivalence proof for the behavior-preserving DRY refactor of `apply_baseline`
in `tools/path_claim_check.py` (forge lane: Refactor -- code review and refactors).
THE SMELL
`apply_baseline(rows, ledger)` walked the SAME `missing` dict FOUR times to
build three independent results, on top of the mutation loop that already
walked it once to attach each row's baseline:
matched = sum(1 for r in missing.values() if r["baseline"]["known"])
new = sorted(p for p, r in missing.items() if r["baseline"]["new"])
tolerated = collections.Counter(
r["baseline"]["class"] for r in missing.values() if r["baseline"]["known"])
Four full traversals of one dict (mutation + matched + new + tolerated) -- the
canonical "single-census-pass" smell.
THE REFACTOR (working tree, uncommitted at HEAD)
A single FUSED pass replaces all four: the mutation that attached each row's
baseline is merged with the census that tallied matched/new/tolerated, so one
`for p, row in missing.items()` loop does both jobs at once:
matched = 0
new = []
tolerated = collections.Counter()
for p, row in missing.items():
if p in ledger:
cls, reason = ledger[p]
row["baseline"] = {"known": True, "class": cls,
"reason": reason, "new": False}
matched += 1
tolerated[cls] += 1
else:
row["baseline"] = {"known": False, "class": None,
"reason": None, "new": True}
new.append(p)
`known` and `new` are exact complements (a row is either known/new=False or
unknown/new=True), so the tally is updated from the record just written, in the
same iteration -- no second pass over `missing` is needed. The report renders
`new` sorted and `tolerated` via sorted(.items()), so neither traversal order
nor the merge can move a byte of output.
WHAT THIS VERIFIER PROVES (measured, not claimed)
It materializes the GENUINE pre-refactor source from `git show HEAD:tools/
path_claim_check.py` -- HEAD already carries the portable DEFAULT_REPO commit,
so this commit is the ONLY delta: the apply_baseline change. It then runs both
versions on the identical repo state (--rev HEAD) and compares every observable
output byte-for-byte:
[1] selftest: original (HEAD) passes 31/31, rc 0
[2] selftest: refactored (working tree) passes 31/31, rc 0
[3] selftest: stdout is byte-identical (byte-for-byte)
[4] live scan --rev HEAD --json: original runs rc 0
[5] live scan --rev HEAD --json: refactored runs rc 0
[6] live scan: the ENTIRE --json document is byte-identical, which includes the
`baseline` field -- the direct output of apply_baseline -- so matched, new,
tolerated, cleared, cleared_docrel and dead are all confirmed equal.
[7] non-vacuity: the census found real findings (baseline has 13 entries)
[8] code-level: the original apply_baseline body contains 4 `missing.` census
scans (mutation loop + matched + new + tolerated); the refactored body
contains 1. The smell is gone, the fix is real.
Exit 0 iff every check holds, else 1.
"""
from __future__ import annotations
import json
import os
import re
import shutil
import subprocess
import sys
import tempfile
# Portable repo root: this file lives in <repo>/wizard-work/forge, so the repo
# root is three dirname()s up. Falls back to the hardcoded checkout for any
# one-shot run that isn't on the committed path.
_HERE = os.path.dirname(os.path.abspath(__file__))
REPO = os.path.dirname(os.path.dirname(os.path.dirname(_HERE)))
if not os.path.isdir(os.path.join(REPO, "tools", "path_claim_check.py")):
REPO = "/root/troupe-standup"
TOOL = "tools/path_claim_check.py"
SELTEST_SUMMARY = "selftest: 31 cells, 31 passed, 0 failed"
class Verdict(Exception):
pass
def git_show(sha: str) -> str:
proc = subprocess.run(["git", "-C", REPO, "show", sha], capture_output=True,
text=True, check=True, timeout=60)
return proc.stdout
def run(path: str, args: list[str], cwd: str = REPO, timeout: int = 300):
proc = subprocess.run([sys.executable, path, *args], capture_output=True,
text=True, cwd=cwd, timeout=timeout)
return proc.returncode, proc.stdout, proc.stderr
def run_json(path: str, out_path: str, repo: str = REPO, only: str = "missing",
timeout: int = 300) -> tuple[int, str, str]:
proc = subprocess.run([sys.executable, path, "--repo", repo, "--rev", "HEAD",
"--only", only, "--json", out_path],
capture_output=True, text=True, cwd=REPO, timeout=timeout)
return proc.returncode, proc.stdout, proc.stderr
def extract_apply_baseline(text: str) -> str:
"""Return just the body lines of the apply_baseline function."""
lines = text.splitlines()
start = None
for i, ln in enumerate(lines):
if ln.startswith("def apply_baseline("):
start = i + 1
break
if start is None:
raise Verdict("could not locate apply_baseline in source")
body = []
for ln in lines[start:]:
if ln.startswith("def ") or ln.startswith("@"):
break
body.append(ln)
return "\n".join(body)
def census_scans(body: str) -> int:
"""Count real `missing.values()` / `missing.items()` census passes in CODE.
Comment lines and the function docstring are excluded so the prose that
*describes* the smell is never itself counted as a scan -- only executable
traversals of the `missing` dict are.
"""
out = 0
in_doc = False
for ln in body.splitlines():
s = ln.strip()
if s.startswith('"""') or s.startswith("'''"):
# A one-line docstring opens AND closes on this line; an unclosed
# triple-quote toggles multi-line docstring state.
if s.count('"""') == 2 or s.count("'''") == 2:
continue
in_doc = not in_doc
continue
if in_doc or s.startswith("#"):
continue
out += len(re.findall(r"missing\.(values|items)\(\)", s))
return out
def main() -> int:
cells: list[tuple[str, bool, str]] = []
failed = 0
def check(label: str, ok: bool, detail: str = "") -> None:
nonlocal failed
cells.append((label, bool(ok), ""))
if not ok:
failed += 1
print(" [%s] %s%s" % ("ok " if ok else "FAIL", label,
(" [%s]" % detail) if detail and not ok else ""))
tmpdir = tempfile.mkdtemp(prefix="pc_ab_verify_", dir=os.environ.get("TMPDIR", "/tmp"))
# Materialize the genuine pre-refactor source (HEAD = portable DEFAULT_REPO
# + OLD 4-scan apply_baseline; the only thing the working tree changes).
orig_path = os.path.join(tmpdir, "path_claim_check.py")
with open(orig_path, "w", encoding="utf-8") as fh:
fh.write(git_show("HEAD:" + TOOL))
ref_path = os.path.join(REPO, TOOL)
try:
# ---- [1-3] selftest parity ------------------------------------------
rc_o, out_o, err_o = run(orig_path, ["--selftest"])
rc_r, out_r, err_r = run(ref_path, ["--selftest"])
check("selftest: original 31/31 rc 0",
rc_o == 0 and SELTEST_SUMMARY in out_o, "rc=%s" % rc_o)
check("selftest: refactored 31/31 rc 0",
rc_r == 0 and SELTEST_SUMMARY in out_r, "rc=%s" % rc_r)
check("selftest: identical stdout (byte-for-byte)",
out_o == out_r, "len_o=%d len_r=%d" % (len(out_o), len(out_r)))
# ---- [4-7] live scan equivalence ------------------------------------
oj_path = os.path.join(tmpdir, "orig.json")
rj_path = os.path.join(tmpdir, "ref.json")
rc_o, _, _ = run_json(orig_path, oj_path)
rc_r, _, _ = run_json(ref_path, rj_path)
check("live scan: original ran rc 0", rc_o == 0, "rc=%s" % rc_o)
check("live scan: refactored ran rc 0", rc_r == 0, "rc=%s" % rc_r)
if os.path.exists(oj_path) and os.path.exists(rj_path):
with open(oj_path) as fh:
jo = json.load(fh)
with open(rj_path) as fh:
jr = json.load(fh)
# [6] the ENTIRE JSON document is byte-identical -- this includes the
# `baseline` field that apply_baseline() produces.
check("live scan: full --json byte-identical (incl. baseline field)",
json.dumps(jo, sort_keys=True) == json.dumps(jr, sort_keys=True),
"orig baseline=%r ref baseline=%r" % (
jo.get("baseline"), jr.get("baseline")))
# [7] non-vacuity: real, populated baseline findings.
check("live scan: non-vacuous (baseline has findings)",
jo.get("baseline") is not None
and jo["baseline"]["entries"] > 0
and len(jo["paths"]) > 0,
"entries=%r paths=%d" % (
jo.get("baseline", {}).get("entries"), len(jo.get("paths", []))))
else:
check("live scan: JSON files produced by both", False,
"orig exists=%s ref exists=%s" % (os.path.exists(oj_path),
os.path.exists(rj_path)))
# ---- [8] code-level: the smell was real and is gone ------------------
# Original is the genuine HEAD source; refactored is the working tree.
# Both are stripped of comments/docstrings by census_scans so the prose
# that explains the smell is not itself counted as a scan.
orig_code = git_show("HEAD:" + TOOL)
with open(ref_path, encoding="utf-8") as fh:
ref_code = fh.read()
scans_orig = census_scans(extract_apply_baseline(orig_code))
scans_ref = census_scans(extract_apply_baseline(ref_code))
check("code-level: original apply_baseline has 4 census scans",
scans_orig == 4, "got %d" % scans_orig)
check("code-level: refactored apply_baseline has 1 census scan",
scans_ref == 1, "got %d" % scans_ref)
check("code-level: the smell was really reduced (4 -> 1)",
scans_orig == 4 and scans_ref == 1)
except Verdict as e:
print("\nVERDICT: MISMATCH -- %s" % e)
failed += 1
finally:
shutil.rmtree(tmpdir, ignore_errors=True)
total = len(cells)
passed = total - failed
print("\nverify: %d checks, %d passed, %d failed" % (total, passed, failed))
return 1 if failed else 0
if __name__ == "__main__":
sys.exit(main())