Repository navigation
fix(placement-plan): check the exit inventory and refuse failed placements - #47
Conversation
…ments Close the verifier and generator gaps deferred from #45 (issue #46). - build_plan.py publishes exit_inventory, every exit object read from the content. verify_plan.py derives the expected exit record copies from it (one per group a crossing exit touches, the overworld's own exits in internal_exits) and requires exactly those copies, each equal to its inventory entry. It re-derives the summary and per-group crossing counts, requires plane endpoints on every exit whose ends are placed, and compares every other copy of an exit (blocked sites, relayering exits, wired and kept dangling exits, data problems, main connections) with its record or inventory entry. - Each stairwell replaces entry must equal its exit record: source tile, target map and tile, archetype, plane endpoints and direction. - A group whose placement fails stays unplaced: it leaves the placement order, is listed as unplaced and in the new summary.failed_placements, and is never retried. The verifier rejects a failed placement and any place or uncertain group without its placement, so the build does not publish such a plan. The view-radius sensitivity run lists its unplaced groups. - An overworld exit wired to another overworld map goes into the overworld's internal_exits; before, no list held a copy of it. - test_placement_plan.py: tampered copies of the committed plan for each check, and place_pool() with a failing group. No content checkout needed. Regenerated from content 3ad20150da76: placement-plan.json gains exit_inventory and summary.failed_placements, REPORT.md's check line and Reproducing section change, and the PNGs are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Dependency Review✅ No vulnerabilities or license issues found.Scanned FilesNone |
|
@codex review Please do a deep, thorough review of the whole diff and the code it touches and report every issue you find in this pass, including P2s, not only the most important few. |
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f7ad8debee
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Address the first Codex review round on #47. - Exit records and the exit inventory now share exit_source_fields(), and every content field of a record (tiles, target, archetype, form, direction, flags) must equal its inventory entry. A record may hold no field the verifier does not know. Wired exits no longer carry an authored_target of nulls. - A record has all of its placement fields exactly when both of its ends are placed, and none of them otherwise, so a partial endpoint pair is rejected. xy_distance and layer_delta must follow from the plane ends. The stairwell label must match becomes, and a portal's reason and slope_reachable are re-derived the way the generator decides them. - The exit data problems (kind and severity per exit) are re-derived from the inventory, which now also carries the reader's target problem and an explicit last_heal 0. - A blocked stairwell site may list only exits that need a stairwell, so the existing end check covers every listed exit. - A tentative placement needs an uncertain group, or an unreachable group with an alignment hint. - The alignment-hint retry runs only for groups no pool attempted, so a failed group cannot come back as both placed and failed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Round 1 (Codex review of f7ad8de): all six findings fixed in e36e51e.
Validation for e36e51e:
|
|
@codex review Please do a deep, thorough review of the whole diff and the code it touches and report every issue you find in this pass, including P2s, not only the most important few. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e36e51eaa5
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Address the second Codex review round on #47. A placement must name a main connection unless an alignment hint forced it, and never both. The connection's kind must be "in" or "out", it must be one of the group's exit records of that kind, and its other end must be the overworld or a group placed earlier. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Round 2 (Codex review of e36e51e): the one finding is fixed in 808ea4a.
Validation for 808ea4a: |
|
@codex review Please do a deep, thorough review of the whole diff and the code it touches and report every issue you find in this pass, including P2s, not only the most important few. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 808ea4acfb
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Address the third Codex review round on #47. - Every inventory entry's source map must belong to a group, including exits without a destination. - The inventory carries the remaining facts the exit data problems come from: the tile-link slot of a tiled exit without a link, a dynamic exit's authored path, and a target problem with its authored target. The verifier re-derives every data-problem entry field by field, details and notes included. - Two more exit copies are checked. A blocked site may list only exits between its own two owners (or between the top and bottom maps of a relayering site), and a kept decoration's plane_source must be its tile on the plane. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Round 3 (Codex review of 808ea4a): two findings fixed in a1f5c17, one declined.
Also in a1f5c17: a blocked site may list only exits between its own owners (or its top and bottom maps), and a kept decoration's Validation for a1f5c17:
|
|
@codex review Please do a deep, thorough review of the whole diff and the code it touches and report every issue you find in this pass, including P2s, not only the most important few. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a1f5c1706d
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Address the fourth Codex review round on #47. - Portal records: portal_reason_text is re-derived exactly, like the code and slope_reachable. - Stairwells: the shape follows from the ends (column or sloped, no other value), the kind is known and the ids run in one series per kind. max_bend, the owners (group, owner, other, connects: from the replaced exits), and the intermediate tiles' decoration (the exits' stair family) are re-derived. A stairwell must replace at least one exit. - Blocked sites: each must list an exit and have the shape of its ends, and a group's site belongs to the later placed end. The summary's stairwell and blocked-site counts are re-derived. - Groups: classifications and reasons must be known values, there is one overworld group, and the summary's group, map and classification counts are re-derived. - Placements: the offsets must equal the ideal spot plus the recorded shift and extra layers, and the ideal must follow from the main connection within the recorded vertical bounds. - The verifier's docstring now lists what it does not re-derive (the generator's explanations and evidence). The plan is unchanged; a rerun is byte-identical. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Round 4 (Codex review of a1f5c17): all six findings fixed in 0006dd7.
To stop this from becoming field-by-field patching, the verifier's docstring now spells out its contract, including what it deliberately does not re-derive: the generator's explanations and evidence, which the migration does not act on. Validation for 0006dd7:
|
|
@codex review Please do a deep, thorough review of the whole diff and the code it touches and report every issue you find in this pass, including P2s, not only the most important few. |
|
@codex security review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 0006dd7296
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
🛡️ Codex Security ReviewSecurity review completed. No security issues were found in this pull request. Reviewed commit: Only the user who started this review can view the report in Codex. ℹ️ About Codex security reviews in GitHubThis is an experimental Codex feature. Security reviews are triggered when:
Once complete, Codex will leave suggestions, or a comment if no findings are found. |
Address the fifth Codex review round on #47. - The overworld group's map count and per-layer counts are re-derived from its cells, like the other groups' counts from their member lists. - Every resolved target in the exit inventory must lie on a tile of its target map. - A column stairwell may leave its column by at most settings.stairwell_max_bend_tiles, and the blocked-site chain search uses that setting instead of a hard-coded 2. The plan is unchanged; a rerun is byte-identical. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Round 5 (Codex review of 0006dd7): all three findings fixed in 9bc81aa.
Validation for 9bc81aa:
The Codex security review for 0006dd7 started after the re-request and was still running at this push. |
|
@codex review Please do a deep, thorough review of the whole diff and the code it touches and report every issue you find in this pass, including P2s, not only the most important few. |
|
Codex Review: Didn't find any major issues. Breezy! Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
|
@codex security review |
🛡️ Codex Security ReviewSecurity review completed. No security issues were found in this pull request. Reviewed commit: Only the user who started this review can view the report in Codex. ℹ️ About Codex security reviews in GitHubThis is an experimental Codex feature. Security reviews are triggered when:
Once complete, Codex will leave suggestions, or a comment if no findings are found. |
|
@codex review Please do a deep, thorough review of the whole diff and the code it touches and report every issue you find in this pass, including P2s, not only the most important few. |
|
@codex review Please do a deep, thorough review of the whole diff and the code it touches and report every issue you find in this pass, including P2s, not only the most important few. |
63a5fd6 committed scripts/__pycache__/build_plan.cpython-314.pyc. It was written when verify_plan.py --content imported the generator, before any module disabled bytecode writing. That broke tools/test_writable_source.py in CI. Remove the file, and have verify_plan.py set sys.dont_write_bytecode at import, as build_plan.py and the tests already do. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
The committed .pyc is a generated CPython 3.14 cache rather than source: it embeds the originating checkout path and depends on interpreter version and source metadata. A normal import may either execute it when its header happens to validate or rewrite the tracked file when it does not, making behavior and worktree cleanliness depend on cache state. Remove it and ignore __pycache__/*.pyc.
AGENTS.md reference: AGENTS.md:L36-L39
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…s in CI Address the Codex review of 63a5fd6 on #47. - verify() turns any exception from a malformed plan (a missing key, a null where a list belongs) into a failed result rather than a traceback. verify_plan.py --content reports a checkout it cannot rebuild from the same way. - Placement offsets, ideal, shift and extra layers must be integers (booleans and floats are rejected). - The plan's schema is now atrinik-overworld-placement-plan/4, since exit_inventory and the failed-placement bookkeeping are required, and the verifier accepts only that schema. - tools/validate.sh runs test_placement_plan.py after the Cargo steps, like the other tools/test_*.py suites, so CI exercises the verifier's protections. The content-fidelity tests skip there. Regenerated from content 3ad20150da76: only the schema string changes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Round 13 (Codex review of 63a5fd6) and a CI failure: fixed in 791b2c0 and cc94be1. CI failure: 63a5fd6 committed
Validation for cc94be1:
|
|
@codex review Please do a deep, thorough review of the whole diff and the code it touches and report every issue you find in this pass, including P2s, not only the most important few. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: cc94be1550
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…risons Address the Codex review of cc94be1 on #47, fixing each class rather than each field. - One pass over the plan rejects floats anywhere, and booleans outside the known flag keys. Python's true == 1 can then no longer stand in for a distance, a layer or a count in any JSON-only comparison (layer_delta and xy_distance included). - The fidelity comparison checks JSON type for type in place, without serializing both plans. verify_plan.py refuses a plan file over 64 MiB before parsing it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Round 14 (Codex review of cc94be1): both findings fixed in 85ed583, as classes.
Validation for 85ed583:
|
|
@codex review Please do a deep, thorough review of the whole diff and the code it touches and report every issue you find in this pass, including P2s, not only the most important few. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 85ed583018
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Address the Codex review of 85ed583 on #47. Each of the suite's 134 verifications re-runs the full verifier on the 3 MB plan, about 1.2 s locally, so the serial suite took about 165 s here and about 10 min in Codex's environment. The tests are independent and CPU-bound, so the suite's entry point now runs one test per task in up to 8 forked worker processes, with --jobs 1 for serial. Each case still verifies its own tampered plan in isolation. Locally the suite takes 29 s with 8 workers and 48 s with 4, the size of CI's runners. Failures are reported with their unittest output and a nonzero exit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Round 15 (Codex review of 85ed583): the one finding fixed in 24e1329.
Validation for 24e1329:
|
|
@codex review Please do a deep, thorough review of the whole diff and the code it touches and report every issue you find in this pass, including P2s, not only the most important few. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 24e1329249
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…heck Address the Codex review of 24e1329 on #47. Each test verified its own tampered plan in full, and about 80% of a verification is the relayering re-derivation (242,824 overworld tiles), whose inputs almost no test touches. - relayering_rules() is now a pure function of explicit inputs: the overworld rows, the relayering record, the overworld's internal exits, the seam pieces' exits that have a record, and four settings. _relayering() memoizes the last result in the process, keyed by a SHA-256 of the canonical JSON of exactly those inputs, so the results are the same with or without the cache. A test asserts that each input is in the key, and that a changed relayering input is recomputed. - The occupancy pass finds '#' tiles with str.find and computes bounding boxes once per owner instead of per tile. The number-type pass checks first without building paths. - The test runner verifies the committed plan before forking, so every worker starts with the cache warm. verify_plan.py's output is byte-identical. A verification of an unchanged relayering takes 0.22 s instead of 1.15 s. The suite takes 39 s serially (was 166 s), 14 s with 4 workers and 10 s with 8. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Round 16 (Codex review of 24e1329): fixed in 3d04cb7.
Validation for 3d04cb7:
|
|
@codex review Please do a deep, thorough review of the whole diff and the code it touches and report every issue you find in this pass, including P2s, not only the most important few. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 3d04cb7f87
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Address the Codex review of 3d04cb7 on #47. - report.py lists every wired exit, including one wired between two overworld maps (internal_exits), which the Wired exits section used to omit. The crossing-stairs sentence counts only crossing wired exits that became stairs, not summary.wired_exits. A report test with such an exit fails on the old code. - verify() requires the plan to identify its input: a non-empty content_root, a 40-hex content_git_head, a boolean content_worktree_clean and a well-formed inputs digest. A plan that has lost its content_root no longer passes because --content ignores the value. The plan and REPORT.md are unchanged; --check passes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Round 17 (Codex review of 3d04cb7): both findings fixed in 7564152. Neither is another per-field fidelity case:
Validation for 7564152:
|
|
@codex review Please do a deep, thorough review of the whole diff and the code it touches and report every issue you find in this pass, including P2s, not only the most important few. |
|
Codex Review: Didn't find any major issues. Hooray! Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
|
@codex security review |
🛡️ Codex Security ReviewSecurity review completed. No security issues were found in this pull request. Reviewed commit: Only the user who started this review can view the report in Codex. ℹ️ About Codex security reviews in GitHubThis is an experimental Codex feature. Security reviews are triggered when:
Once complete, Codex will leave suggestions, or a comment if no findings are found. |
Summary
Closes #46. This fixes the three placement-plan verifier and generator gaps deferred from #45's final review. The verifier now checks the exit records against a complete source exit inventory, and each stairwell's
replacesentries against their exit records. A group whose placement fails stays unplaced and blocks publication. The committed plan is unchanged apart from the new inventory and bookkeeping fields: the same placements, stairwells and exits, and the same PNGs.Implementation / behavior
1. Complete crossing-exit inventory (#45 thread r4306554103)
build_plan.pypublishesexit_inventory: every exit object read from the content (1,200), keyed bysource_mapandline, with its legacy form and resolved target. The plan's schema becomesatrinik-overworld-placement-plan/4.verify_plan.pyderives the expected record copies from the inventory: one copy in each group a crossing exit touches, and overworld-to-overworld exits in the overworld'sinternal_exits. It requires exactly those copies (no missing, extra or misfiled copies), each equal to its inventory entry. So deleting either copy ofmine_entrance_a:641, or both, now fails.crossing_exits_after_placement, the exits into separate planes and the portal reasons. The inventory size must equalsummary.exit_objects.plane_source/plane_targetexactly when both of its ends are placed. Before, deleting them silently skipped the endpoint check.overworld_plane_source/_targetare now checked too.relayering.exits, wired and kept dangling exits,data_problems.exitsand each placed group'smain_connection.internal_exits. No current exit is affected.2. Stairwell
replacesvs. the exit record (#45 thread r4306554106)replacesentry must equal its indexed record in source tile, target map and tile, archetype, and plane source and target. Itsgoesmust match the record's direction.3. Failed groups stay unplaced (#45 thread r4306554110)
place_pool(). A group whoseplace_group()fails is kept out ofplacement_order, is never retried, and is listed inunplaced_place_groups/unplaced_uncertain_groupsand the newsummary.failed_placements. Groups reachable only through it stay unplaced as well. A failed tentative alignment (rockforge_c) is recorded the same way.status: failedplacement, and any place group without a proposed placement or uncertain group without a tentative one. It also re-derives the placement order, the unplaced lists andfailed_placementsfrom the records. The build's verify-before-publish therefore refuses such a plan.groups_unplaced; none today).Two checks.
verify_plan.pyre-derives the placement rules from the JSON alone; tests and CI run that. With--content DIRit also rebuilds the plan from the content atcontent_git_headwith the generator, and requires the published JSON to equal the rebuild as canonical JSON text, with onlycontent_root(the checkout path) aside. That fidelity check catches any edit that keeps the JSON consistent with itself.build_plan.py --checklikewise compares every artifact byte for byte.Review rounds extended the same idea to the rest of the exit and placement data. The records and the inventory share
exit_source_fields(), and every exit record field is either equal to the inventory or re-derived: placement fields present only when both ends are placed, distances, labels, portal reason and text,slope_reachable. Unknown fields are rejected. Exit data problems are re-derived entry by entry. Stairwell shape, ids, owners,max_bendand intermediate decoration are re-derived. Blocked sites must list qualifying exits between their own owners. Group classifications and summary counts are checked. Each placement's offsets must follow from its ideal spot, and the ideal from its main connection. The verifier's docstring lists what it deliberately does not re-derive: the generator's explanations and evidence.Generation is still deterministic, and the staging and verify-before-publish steps are unchanged.
Validation
3ad20150da76, clean underarch/andmaps/. At the base commit, a rerun reproduced all 34 committed artifacts byte-for-byte. With this change, two runs, one in place and one into a scratch directory, are byte-identical across all 34 artifacts. Onlyplacement-plan.jsonandREPORT.mdchange; the PNGs are identical.python3 -I scripts/verify_plan.py placement-plan.json:ok: true, with 1,007 record copies matched against 1,200 inventory exits.verify_plan.py placement-plan.json --content <checkout>: ok, from the input clone and from a fresh clone at another path.python3 -I scripts/test_placement_plan.py: 84 tests pass in about 40 s serially or 10 s with 8 workers (3 of them only whenPLACEMENT_PLAN_CONTENTnames a content checkout);tools/validate.shruns the suite. Each tampers with a copy of the committed plan, or drivesplace_pool()with a failing group. Run against the oldverify_plan.py, every tampering case is accepted or crashes the verifier instead of failing cleanly.MAX_SHIFT = MAX_EXTRA_LAYERS = 0to force failures into a scratch output directory holding sentinel files: 8 groups fail and 9 are unplaced (rockforge_bis reachable only through a failed group). The only verification errors are those failures, the build printsverification FAILED; nothing written, and the sentinels are untouched.build_plan.py --check: all 34 committed artifacts are byte-identical to a fresh run. A tampered copy thatverify_plan.pyaccepts (an internal exit dropped with its count) fails--check.tools/validate.sh: passes (exit 0), with the CI-pinnedsyft1.50.0 (SHA-256 verified) on PATH.git diff --check: clean.actionlintis not installed locally, and no workflow files changed.Limitations / follow-up
--content) and--checkneed a content checkout atcontent_git_head, so CI runs only the JSON-only check. TheContentFidelitytests skip there.data_problems.dangling_exits_wired,summary.wired_exitsand the records, but recomputing the wiring rule would duplicate the generator.tools/validate.shnow runstest_placement_plan.py(about 10-15 s in parallel workers), the one change outsidemigration/placement-plan/.🤖 Generated with Claude Code