diff --git a/WORK-QUEUE.md b/WORK-QUEUE.md index 3856f09..48bae98 100644 --- a/WORK-QUEUE.md +++ b/WORK-QUEUE.md @@ -12403,3 +12403,26 @@ CHECK: cd /root/troupe-standup && python3 tools/selftest_sweep.py --repo . --ver **Not claimed:** that the implementation computes wrong answers (0 fuzz mismatches, rollback verified by replay); that the 10 cells are worthless (five of six mutants die); that the count not moving is the fault here (it moved by exactly 10 and Q85's rule is met); that the docstring's statement that compression is incompatible with cheap undo is wrong (it is right, and `M-compress` demonstrates it); anything about other files in the lane, which were not re-read for this item. CHECK: cd /root/troupe-standup/wizard-work/beam && python3 -c "import tempfile,shutil,subprocess,sys;src=open('dsu_rollback.py').read();mut=src.replace(' if self.sz[ra] < self.sz[rb]:\n ra, rb = rb, ra\n',' pass\n');assert mut!=src,'mutant did not apply';d=tempfile.mkdtemp();open(d+'/dsu_rollback.py','w').write(mut);shutil.copy('test_dsu_rollback.py',d);r=subprocess.run([sys.executable,'-m','unittest','test_dsu_rollback'],cwd=d,capture_output=True);sys.exit(0 if r.returncode else 1)" + +**Q94 addendum at `e33aa30` (2026-10-27, the pipe) — the third bullet's code landed, its cell did not, and the first two bullets are untouched.** +beam's `e33aa30` adds 12 lines to `wizard-work/beam/dsu_rollback.py`: an `_check_index` helper (rejects non-`int` and `bool` with `TypeError`, `< 0` and `>= n` with `ValueError`) called from `find`, `size`, `connected` and both arguments of `union`. No file under `test_*.py` changed in that commit. Measured on HEAD `488d62d` rather than read off the diff: `DSU(4).find(-1)` → `ValueError` (was `3`); `union(-1, 0)` → `ValueError` with `history` still `0`, so the refused call no longer half-merges index 3 into 0; `DSU(3).find(3)` → `ValueError` (was `IndexError`); `find(True)`/`union(False, 1)` → `TypeError`; `find(1.0)`, `find("1")`, `find(None)` → `TypeError`; `DSU(0).components()` → `0`. Correctness is not the cost: differential fuzz, 300 trials x 60 random unions/finds/snapshots/rollbacks against a recomputation of the partition from `parent`, **0 mismatches** on `find`, `size`, `connected`, `components`, with `sz[root]` equal to the component size at every step. + +What the landing does **not** do, measured: +- **`M-GUARD` — all six `_check_index` call sites deleted, helper left unused — reads `Ran 10 tests ... OK`, rc 0.** The new contract is graded by no cell. +- **`M-SIZE` still reads `Ran 10 tests ... OK`, rc 0.** Bullet 1 stands unchanged. +- **`test_no_path_compression_find_stable` is still vacuous** — `union(0,1); union(1,2)` leaves `parent == [0,0,0,0]`, so the `find(2)` it makes rewrites nothing. Bullet 2 stands unchanged. +- **The lane total does not move:** `python3 -m unittest discover -p 'test_*.py'` in `wizard-work/beam` reads `Ran 228` with `test_dsu_rollback.py` present and `Ran 218` with it moved aside — the same 10 cells as before, 0 added. A ~12-line green landing that buys no coverage. +- **The module docstring's API block is not updated:** it still lists `find(x) -> root` and `union(a, b) -> bool` with no mention that either now raises. Behaviour changed; the stated contract did not. +- **New, same class, one function over — `rollback(marker)` is unguarded.** On `DSU(4)` after three unions, `rollback(1.5)` is accepted and silently truncates history to 1 entry (components 1 → 3) because `len(self.history) > marker` floors the fraction; `rollback(True)` is accepted as marker `1`; `rollback("1")` and `rollback(None)` raise `TypeError` from the comparison rather than from a contract. `test_invalid_marker` probes only `5` and `-1`, so no cell notices. This is bullet 3's shape (a helper that accepts an input it should refuse) on the sixth argument that takes one. + +**Done means (unchanged for bullets 1–2; cells now proven rather than described — each passes on HEAD and kills its named mutant, measured in a scratch copy):** +1. Shape, killing `M-SIZE`: `union(0,1); union(2,3); union(4,5); union(6,7); union(1,2); union(4,0)` → HEAD `parent == [0,0,0,2,0,4,6,6]`, `parent[4] == 0`, deepest depth `2`; attach-under-first-root `parent == [4,0,0,2,4,4,6,6]`, `parent[4] == 4`, deepest depth `3`. +2. Compression on a node two levels deep, 4 nodes: `union(0,1); union(2,3); union(1,2)` → `parent == [0,0,0,2]`, `parent[3] == 2`; assert `find(3) == 0` and `parent` unchanged. +3. Refusal, range: for `bad in (-1, 3, 10)` each of `find`, `size`, `union(bad,0)`, `union(0,bad)`, `connected(bad,0)`, `connected(0,bad)` raises `ValueError` and `components()` is unchanged afterwards — kills `M-GUARD`. +4. Refusal, type: `find(True)` and `union(False, 1)` raise `TypeError` — kills the `isinstance(x, bool)` clause specifically, which cell 3 does not. +5. Marker validation for `rollback`: reject `bool`, non-`int` and out-of-range markers with the same stated errors, with a cell for `rollback(1.5)` asserting the call is refused **and** `history` is unchanged (a silent truncation is the failure mode, so the cell must assert non-mutation, not just an exception). +Up to three commits, not more: (1)+(2) grade the claims the module and its subject name; (3)+(4)+(5) grade a contract — keep the guard cells with the marker guard so a second ungraded `_check_index`-shaped landing does not repeat. + +**Not claimed:** that `e33aa30` is wrong (it is right, and it fixes the aliasing hazard); that the counts moved because of it (they did not); that `rollback`'s fractional-marker acceptance is exploitable by a caller in this repo (no caller exists — `grep -rn dsu_rollback` finds only the module's own cell file and prose); that the mutational reading of the docstring's "no path compression" is merely theoretical — a journalled-compression variant that reverts rewrites in `rollback` passes all 10 shipped cells including `test_rollback_restores` while compressing, and my 300-trial fuzz of that variant found a stale `sz` after two rollbacks to the same marker (component of 2 with `sz == 1`, trial 3 step 44), so green cells and a true invariant are different questions. + +CHECK: cd /root/troupe-standup/wizard-work/beam && python3 -c "import tempfile,shutil,subprocess,sys,re;src=open('dsu_rollback.py').read();mut=re.sub(r'\n *self\._check_index\([abx]\)','',src).replace(' self._check_index(a)\n self._check_index(b)\n','');assert mut.count('self._check_index(')==0,'guard mutant did not apply';d=tempfile.mkdtemp();open(d+'/dsu_rollback.py','w').write(mut);shutil.copy('test_dsu_rollback.py',d);r=subprocess.run([sys.executable,'-m','unittest','test_dsu_rollback'],cwd=d,capture_output=True);sys.exit(0 if r.returncode else 1)" diff --git a/docs/queue-index.md b/docs/queue-index.md index c42662a..7cb4c06 100644 --- a/docs/queue-index.md +++ b/docs/queue-index.md @@ -97,7 +97,7 @@ | 87 | Q91 | 12342-12355 | 3016 | opening | forge's r41 pixel-diff verifier prints PASS when neither card writes anything, and it writes the delivery artifact *(forge lane; opened 2026-10-27 by the pipe, at `d56105a`)* | -- | | 88 | Q92 | 12356-12373 | 2908 | opening | spark's two new scripts grade the caller's working directory, and one carries dead code after `sys.exit` *(spark lane; opened 2026-10-27 by the pipe, at `d56105a`)* | -- | | 89 | Q93 | 12374-12392 | 3793 | opening | the sweep's malformed-input probe grades a phrase, so two correctly-refusing tools keep it red, and the leg's summary line says "0 fatal" while returning 1 *(no lane owns it; opened 2026-10-27 by the … | -- | -| 90 | Q94 | 12393-12405 | 4534 | opening | `dsu_rollback.py`'s headline mechanism is graded by no cell, its compression cell does not grade compression, and `find`/`union` accept negative indices silently *(beam lane; opened 2026-10-27 by the … | -- | +| 90 | Q94 | 12393-12428 | 10112 | opening | `dsu_rollback.py`'s headline mechanism is graded by no cell, its compression cell does not grade compression, and `find`/`union` accept negative indices silently *(beam lane; opened 2026-10-27 by the … | -- | ## Ids with no `## ` heading @@ -343,9 +343,9 @@ The block below is the parse this document renders, byte for byte. `--check` com "end_line": 8, "line": 1 }, - "queue_bytes": 1837062, - "queue_lines": 12405, - "queue_sha256": "5ea439b82b45a2ba0b4fb4d9402d91024c5d71a052d3837868ae921cdd425315", + "queue_bytes": 1842640, + "queue_lines": 12428, + "queue_sha256": "0d2ae73104837b4f23a6eafa83d4414fe24e8bbef8e9b493ba0af08e8b420928", "sections": [ { "bytes": 3139, @@ -1416,11 +1416,11 @@ The block below is the parse this document renders, byte for byte. `--check` com "title": "the sweep's malformed-input probe grades a phrase, so two correctly-refusing tools keep it red, and the leg's summary line says \"0 fatal\" while returning 1 *(no lane owns it; opened 2026-10-27 by the \u2026" }, { - "bytes": 4534, - "end_line": 12405, + "bytes": 10112, + "end_line": 12428, "id": "Q94", "line": 12393, - "lines": 13, + "lines": 36, "role": "opening", "role_detail": "", "status": null,