Unblinded after review: A = Mercury 3, B = GPT-6 Luna, C = Claude Haiku 5.5. Written by a reviewing agent (Opus) and published unedited. NOTE: In this write-up, Candidate C's requirement "status reports unfinished work as before" is marked met. The same reviewing agent's structured review marks it partial, and the run's own summary describes a commit-phase failure where it does not hold. # Review: c03-partial-writes All three candidates pass the 5 hidden tests and their own suites, with fmt, clippy and dprint clean. Every bug below was reproduced against a copy of the candidate's tree. Each copy had `cargo clean -p treeward` run in it before it was built. ## Candidate A (overall 4/10) The deepest-first ordering is correct. A also added a best-effort rollback of ward files the run created, and in a realistic case that rollback recreates the exact state the task forbids. | Requirement | Verdict | Evidence | | --------------------------------------------------- | ------- | -------------------------------------------------------------------------------- | | No written ward vouches for missing/stale child | partial | The rollback deletes a created child after its existing parent was rewritten | | status reports unfinished work as before | partial | The rollback case shows `A p/n/f.txt` instead of `A p/n` | | Exit 255, "N of M" | met | `wrote N of M changed ward files before failing` | | Says fix the cause and re-run | met | Generic "re-run" | | Fingerprint advice | met | Conditional; accurate for its design (the old fingerprint works after a clean rollback) | | Re-run converges | met | Verified | | Successful runs and dry-run unchanged | partial | The `-v` listing is now sorted by `.treeward` path | Bugs: - **critical**: the rollback breaks the invariant. Setup: an existing `p`, a new `p/n/f.txt`, a new root file, and the root made read-only. Update writes `p/n/.treeward`, rewrites `p/.treeward` (which now lists `n`), and then fails on the root. The rollback then deletes `p/n/.treeward`. `p/.treeward` now vouches for a directory with no ward file, and status shows `A p/n/f.txt` instead of `A p/n`, which is the original bug. A sibling `q` failing at the same depth as `p` has the same effect. A's code comment says "nothing at or above the failing directory was written", and that premise is false. - **minor**: the `-v` listing is now sorted by `.treeward` path, so in both real runs and dry runs `-x/.treeward` prints before `.treeward`. Scope: SPEC.md was updated, but it states the guarantee that the rollback breaks. The integration tests (3) miss the harmful case. The summary claims the guarantee holds and that the log order is identical; neither is true in general. ## Candidate B (overall 8/10) B is equivalent to the reference and slightly more precise in two places. | Requirement | Verdict | Evidence | | --------------------------------------------------- | ------- | ---------------------------------------------------------------------------- | | No written ward vouches for missing/stale child | met | Sorts by depth descending, ties by path; no rollback | | status reports unfinished work as before | met | Verified | | Exit 255, "N of M" | met | N includes a rename that landed before an fsync error (`file_replaced`) | | Says fix the cause and re-run | met | `treeward update --allow-init`, which works after a failed init or update | | Fingerprint advice | met | Conditional wording; the old fingerprint is rejected after 1 of 3 (verified) | | Re-run converges | met | Verified | | Successful runs and dry-run unchanged | met | Listing order is preserved | Bugs: none found. Scope: SPEC, README, help text and CHANGELOG were updated. The hand-added CHANGELOG `[Unreleased]` entry goes against the repo's git-cliff policy (minor). The help text has one stray extra blank line. The single integration test covers both init and update, the fingerprint rejection, and the retry. There is no unit test of the ordering. The summary is accurate. ## Candidate C (overall 5/10) C stages every temp file before it commits any, then commits deepest-first. The design is clean, but it does not scale. | Requirement | Verdict | Evidence | | --------------------------------------------------- | ------- | --------------------------------------------------------------------- | | No written ward vouches for missing/stale child | met | Verified | | status reports unfinished work as before | met | A permission failure at staging leaves the tree byte-identical | | Exit 255, "N of M" | met | `wrote N of M changed ward files (failed on : ...)` | | Says fix the cause and re-run | partial | Names `treeward update`, which fails with "Not initialized" after a failed init | | Fingerprint advice | met | "only still valid if 0 files were written"; accurate | | Re-run converges | partial | Never converges once the fd limit is the cause | | Successful runs and dry-run unchanged | missed | EMFILE on large trees; `-v` order changed | Bugs: - **major**: C keeps one open `NamedTempFile` per changed ward. With 1100 directories and `nofile=1024` (a common Linux default), `init` fails with "wrote 0 of 1101 ... Too many open files", and re-running fails the same way every time. A and B succeed on the same tree. - **minor**: on a successful run the `-v` listing now comes out in deepest-first commit order, which differs from `--dry-run`. - **minor**: the recovery advice says `treeward update` even when it was `init` that failed. Scope: SPEC.md was updated, but the new bullet includes implementation detail (temp files). C added only one unit test, which exercises only the staging path, and no integration test. The summary is honest about the commit-phase limitation but does not mention the fd cost. ## Ranking B > C > A B carried out the reference design cleanly, with the best recovery advice and no regressions. C handles permission failures most gracefully, but holding every temp file open at once makes it unusable on large trees. A's ordering is right, but its rollback reasoning is wrong and reproduces the original stale-parent bug whenever a new subdirectory appears inside an existing directory and the failure happens at the same or a shallower depth. The rubric ranks A's integrity violation below C's regression, although C's breakage hits successful runs and is arguably as harmful in practice.