fix(u6): fold the cold code-review panel — four-arm convergence on three surface clauses
/heid-code-review panel 01M35CK8YKEKMV7T15JXEF6A8N, verdict NOT drift-zero. Three findings arrived from all four arms independently, and they share a shape: a contract clause written as prose and never converted into an assertion. That is the lens working. - The panel dropped the added date the contract promised to show. - `bench ls` printed no ids, and the URL it printed was truncated to 52 columns so the line was not pasteable into `bench state|rm`. The test's docstring claimed it printed ids and asserted nothing of the kind. - `bench import` printed the description instead of the raw URL beside each normalized id, hiding the collapse the clause exists to expose. - An IPv6 literal lost its brackets: http://[::1]:8080/a normalized to http://::1:8080/a, a broken identity that no re-post can match. Bracketed literals are re-wrapped; an unbracketed one is refused rather than guessed. - A deeply-nested JSON RecursionError escaped read_benches' except pair. The byte cap does not help -- 200k open brackets is 200 KB. - An empty board hid the whole benches panel, registration form included. - The link refusal classified by captured-text emptiness, which bash can erase; it now answers with a B:/N sentinel so no name reads as "not a booth". INV-4's tie-break falsifier could not fail: _write_all serializes with sort_keys=True, so both insertion orders came back already id-sorted and removing the tie-break left the test green. It now calls order_benches directly. Same class as the five vacuous U4 falsifiers, found by a cold reader rather than by us. Also from the arms' per-invariant vacuity pass: INV-6 had no vector pinning a non-default port as part of the identity; INV-3 asserted only that links/ was absent; INV-8's hashed sequence omitted a read verb; INV-9's AST walk is defeated by a string import. All closed. Contract amended where the code was right: `updated` means last mutation, the id cap is write-only because the id is the locator controls post back, INV-8's file list includes the lock sidecar it always mandated. Every line number is out of the prose -- the panel found two already stale. 565 -> 593 tests. Nothing declined.
This commit is contained in:
+135
-18
@@ -97,6 +97,8 @@ def test_query_is_part_of_the_identity(tmp_path):
|
||||
("https://x.test:443/p", "https://x.test/p"),
|
||||
("http://x.test/p#frag", "http://x.test/p"), # fragment dropped
|
||||
(" http://x.test/p ", "http://x.test/p"), # whitespace
|
||||
("http://[::1]:80/a", "http://[::1]/a"), # default port, bracketed
|
||||
("http://[::1]/A", "http://[::1]/A"), # bracket round-trips
|
||||
])
|
||||
def test_these_pairs_are_one_bench(a, b):
|
||||
"""INV-6. Each pair is the SAME resource reached two ways."""
|
||||
@@ -108,6 +110,11 @@ def test_these_pairs_are_one_bench(a, b):
|
||||
("http://x.test/p", "http://x.test/P"), # path case
|
||||
("http://x.test/?a=1&b=2", "http://x.test/?b=2&a=1"), # query order is opaque
|
||||
("http://x.test:8092/", "https://x.test:8092/"), # scheme
|
||||
# A NON-DEFAULT PORT IS PART OF THE IDENTITY. Without this vector, "always
|
||||
# omit the port" passes every other row in this file — caught by the cold
|
||||
# panel's per-invariant "what would still pass" pass, not by us.
|
||||
("http://x.test:8092/p", "http://x.test/p"),
|
||||
("https://x.test:8443/p", "https://x.test/p"),
|
||||
])
|
||||
def test_these_pairs_are_two_benches(a, b):
|
||||
"""INV-6, the other direction. Each pair MAY be two different resources, and
|
||||
@@ -150,18 +157,32 @@ def test_the_stored_url_is_the_raw_string(tmp_path):
|
||||
# ---- INV-4: the rendered order is TOTAL and stated --------------------------
|
||||
|
||||
|
||||
def test_same_name_benches_do_not_swap(tmp_path):
|
||||
"""INV-4. Defeating change: dropping the `id` tie-break. Two benches with
|
||||
the SAME name, registered in both orders, must render identically — a test
|
||||
over distinct names passes with no tie-break at all."""
|
||||
def build(order):
|
||||
root = tmp_path / f"r{order}"
|
||||
root.mkdir()
|
||||
for u in (["http://a.test/", "http://b.test/"] if order else
|
||||
["http://b.test/", "http://a.test/"]):
|
||||
upsert_bench(root, u, "same name", "o")
|
||||
return [b.id for b in read_benches(root)[0]]
|
||||
assert build(0) == build(1)
|
||||
def test_same_name_benches_do_not_swap():
|
||||
"""INV-4. Defeating change: dropping the `id` tie-break.
|
||||
|
||||
THIS TEST USED TO GO THROUGH THE REGISTRY AND COULD NOT FAIL. `_write_all`
|
||||
serializes with `sort_keys=True`, so whatever order two benches were
|
||||
inserted in, they came back off disk already id-sorted — and removing the
|
||||
tie-break from `order_benches` left it green. A vacuous falsifier of
|
||||
exactly the shape persistent-memory.d/2026-09-22-vacuous-falsifiers.md
|
||||
describes: it asserted the outcome the author had in mind rather than the
|
||||
discriminator the invariant names. Caught by the cold panel (hulda, solo),
|
||||
not by us.
|
||||
|
||||
So it calls `order_benches` DIRECTLY, with records that tie on both prior
|
||||
keys, presented in both orders. Nothing upstream can pre-sort them.
|
||||
"""
|
||||
def recs(order):
|
||||
pair = [
|
||||
Bench(id="http://a.test/", url="http://a.test/", name="same name",
|
||||
owner="o", state="live", added="", updated=""),
|
||||
Bench(id="http://b.test/", url="http://b.test/", name="same name",
|
||||
owner="o", state="live", added="", updated=""),
|
||||
]
|
||||
return pair if order else list(reversed(pair))
|
||||
assert [b.id for b in order_benches(recs(0))] == \
|
||||
[b.id for b in order_benches(recs(1))]
|
||||
assert [b.id for b in order_benches(recs(1))] == ["http://a.test/", "http://b.test/"]
|
||||
|
||||
|
||||
def test_state_ranks_before_name(tmp_path):
|
||||
@@ -276,6 +297,14 @@ def test_benches_is_stdlib_only_and_imports_no_sibling():
|
||||
roots.add("booth" if node.level else (node.module or "").split(".")[0])
|
||||
outside = {r for r in roots if r and r not in sys.stdlib_module_names}
|
||||
assert not outside, f"booth/benches.py imports outside the stdlib (booth.* included): {sorted(outside)}"
|
||||
# A STRING IMPORT IS INVISIBLE TO THE WALK ABOVE. `__import__("booth.links")`
|
||||
# or `importlib.import_module(...)` inside a function defeats it entirely,
|
||||
# and that is the exact shape someone reaches for when a sibling import is
|
||||
# refused by review. Caught by the cold panel's per-invariant vacuity pass.
|
||||
called = {n.func.id for n in ast.walk(tree)
|
||||
if isinstance(n, ast.Call) and isinstance(n.func, ast.Name)}
|
||||
assert "__import__" not in called, "benches.py imports by string, defeating the AST walk"
|
||||
assert "importlib" not in roots, "benches.py can import anything at runtime via importlib"
|
||||
|
||||
|
||||
# ---- INV-2: ONE predicate decides what a booth URL is -----------------------
|
||||
@@ -485,6 +514,9 @@ def test_the_anchor_href_is_the_raw_url_not_the_id(tmp_path):
|
||||
("[]", "wrong top-level shape"),
|
||||
('{"http://a/": {"name": [], "owner": "o", "state": "live"}}', "wrong-typed field"),
|
||||
('{"http://a/": {"name": "n", "owner": "o", "state": "invented"}}', "unknown state"),
|
||||
("OVERSIZED", "over the size cap"),
|
||||
("UNREADABLE", "chmod 000"),
|
||||
("FIFO", "a named pipe"),
|
||||
])
|
||||
def test_a_damaged_registry_costs_its_panel_and_never_the_page(tmp_path, payload, label):
|
||||
"""INV-5, at the render. The v0.2.2 lesson: a poisoned sidecar returned 500
|
||||
@@ -492,12 +524,34 @@ def test_a_damaged_registry_costs_its_panel_and_never_the_page(tmp_path, payload
|
||||
the shape currently 500ing the gallery elsewhere in this service, so it is
|
||||
the one that matters most."""
|
||||
_board(tmp_path, ROW_REF)
|
||||
if payload is not None:
|
||||
(tmp_path / BENCHES_FILE).write_text(payload)
|
||||
c = _client(tmp_path)
|
||||
assert c.get("/b/links/").status_code == 200, label
|
||||
assert c.get("/").status_code == 200, label
|
||||
assert c.get("/healthz").status_code == 200, label
|
||||
reg = tmp_path / BENCHES_FILE
|
||||
if payload == "OVERSIZED":
|
||||
from booth.benches import BENCHES_MAX_BYTES
|
||||
reg.write_text('{"http://a/": {"name": "' + "x" * BENCHES_MAX_BYTES + '"}}')
|
||||
elif payload == "UNREADABLE":
|
||||
if os.geteuid() == 0:
|
||||
pytest.skip("root ignores the mode bit")
|
||||
reg.write_text("{}")
|
||||
reg.chmod(0o000)
|
||||
elif payload == "FIFO":
|
||||
os.mkfifo(reg)
|
||||
elif payload is not None:
|
||||
reg.write_text(payload)
|
||||
try:
|
||||
c = _client(tmp_path)
|
||||
body = c.get("/b/links/")
|
||||
assert body.status_code == 200, label
|
||||
assert c.get("/").status_code == 200, label
|
||||
assert c.get("/healthz").status_code == 200, label
|
||||
# AND THE ERROR IS VISIBLE. Asserting only 200 was the gap: a render
|
||||
# that swallowed the failure and drew an empty panel passed every case
|
||||
# here while telling the operator nothing needed fixing. Absent is the
|
||||
# one case that must NOT show an error.
|
||||
shown = "the bench registry could not be read" in body.text
|
||||
assert shown is (payload is not None), label
|
||||
finally:
|
||||
if payload == "UNREADABLE":
|
||||
reg.chmod(0o644)
|
||||
|
||||
|
||||
def test_damaged_and_absent_render_different_text(tmp_path):
|
||||
@@ -612,3 +666,66 @@ def test_a_failed_write_leaves_no_scratch_file(tmp_path, monkeypatch):
|
||||
assert not strays, strays
|
||||
# and the prior registry is intact — a failed write destroys nothing
|
||||
assert [b.name for b in read_benches(tmp_path)[0]] == ["one"]
|
||||
|
||||
|
||||
def test_an_ipv6_literal_keeps_its_brackets():
|
||||
"""`urlsplit().hostname` strips them, and a netloc rebuilt from it is not
|
||||
another spelling of the URL — it is a broken one, so a re-post never
|
||||
matches the row the operator means to update. Defeating change: rebuilding
|
||||
netloc from `hostname` with no re-wrap, which is what this shipped as."""
|
||||
assert normalize_bench_url("http://[::1]:8080/a") == "http://[::1]:8080/a"
|
||||
assert normalize_bench_url("http://[2001:DB8::1]/p") == "http://[2001:db8::1]/p"
|
||||
assert normalize_bench_url("HTTP://[::1]:80/p") == "http://[::1]/p"
|
||||
# An UNBRACKETED IPv6 netloc is refused with a reason, not repaired:
|
||||
# `urlsplit(...).port` raises on `::1:8080` because it cannot tell the
|
||||
# address from the port — which is precisely why the brackets exist. The
|
||||
# refusal is the honest answer; guessing where the address ends would be
|
||||
# inventing an identity out of an ambiguous string.
|
||||
with pytest.raises(ValueError):
|
||||
normalize_bench_url("http://::1:8080/a")
|
||||
|
||||
|
||||
def test_deeply_nested_json_does_not_escape_the_read(tmp_path):
|
||||
"""RecursionError is neither ValueError nor OSError, so it went straight
|
||||
past `read_benches`'s except pair and 500'd the page the function exists to
|
||||
protect. The byte cap does not help: 200k open brackets is 200 KB, well
|
||||
inside it. Defeating change: dropping the RecursionError arm."""
|
||||
(tmp_path / BENCHES_FILE).write_text("[" * 200_000)
|
||||
benches, err = read_benches(tmp_path)
|
||||
assert benches == [] and err
|
||||
|
||||
|
||||
def test_the_panel_renders_on_an_EMPTY_board(tmp_path):
|
||||
"""The panel is gated on PAGE IDENTITY, not page content. Gating on
|
||||
`board or benches` hid the panel and its registration form exactly when the
|
||||
board was empty and the registry absent — the state a new deployment starts
|
||||
in, and the one where "no benches registered yet" is most worth saying.
|
||||
Defeating change: any content-derived gate."""
|
||||
_board(tmp_path, "")
|
||||
body = _client(tmp_path).get("/b/links/").text
|
||||
assert "no benches registered yet" in body
|
||||
assert "bench-add" in body, "the registration form vanished with the panel"
|
||||
|
||||
|
||||
def test_the_panel_shows_when_a_bench_was_added(tmp_path):
|
||||
"""INV-N/What renders: the contract says the panel shows the date it was
|
||||
added; `b.added` appeared nowhere in the template and no test asked. All
|
||||
four cold arms found this independently."""
|
||||
_board(tmp_path, ROW_REF)
|
||||
upsert_bench(tmp_path, "https://talk.test/", "talk", "o")
|
||||
added = read_benches(tmp_path)[0][0].added[:10]
|
||||
assert added in _client(tmp_path).get("/b/links/").text
|
||||
|
||||
|
||||
@pytest.mark.parametrize("url,expected", BOOTH_URL_TABLE)
|
||||
def test_the_dead_marker_classifies_the_SAME_table(tmp_path, url, expected):
|
||||
"""INV-2 names RENDER-LEVEL agreement, and the marker tests never ran the
|
||||
table — three hand-written rows with no query between them, so a marker
|
||||
that stopped calling `booth_target` and treated `?q=1` as "not a booth"
|
||||
stayed green while disagreeing with the CLI. Caught by the cold panel.
|
||||
|
||||
Every row whose target does not exist on disk must be marked dead; every
|
||||
non-booth row must not be."""
|
||||
_board(tmp_path, f"- [r]({url}) <sub>· x · 2026-09-01 00:00</sub>\n")
|
||||
marked = bool(_dead_rows(_client(tmp_path).get("/b/links/").text))
|
||||
assert marked is (expected is not None), (url, expected)
|
||||
|
||||
Reference in New Issue
Block a user