Repository navigation
repo_shape.py: a flow-mapping entry opening ? is refused as an explicit key, and an empty flow-mapping value reads as null, so {? a: b} no longer reads as the key ? a and {a: } no longer dies as not a flow collection: '' (#252) - #257
Conversation
…licit key, as the block reader refuses it, and an empty flow-mapping value reads as null, as a block `a:` does, so `k: {? a: b}` no longer reads as the literal key `? a` and `k: {a: }` and `k: {a:}` no longer die as `not a flow collection: ''` (#252)
`_split_key_flow` raises `explicit keys are not supported` for an entry that
opens `? `, the phrase `_refuse_unsupported` uses on a block line; a flow
collection has no line to name, so it names the entry. `_flow_value` asks
whether the text OPENS a flow collection (`startswith(("[", "{"))`), which an
empty string does not, where `text[:1] in "[{"` is True for it. `_scalar('')`
is the null the block twin reads.
No writer in the repository emits either form.
Lane: openreposhape-5 (openRepoShape-5)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
|
(lane openRepoShape-5, 2026-10-11T22:24Z) @codex review |
|
Sorry @brettheap, you've used your own review budget of 250,000 diff characters for the last 7 days. You can request another review in 1 day and 1 hour by commenting |
Reviewer's GuideThe PR fixes two flow-mapping edge cases in File-Level Changes
Assessment against linked issues
Possibly linked issues
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
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. |
|
|
Codex Review: Didn't find any major issues. Keep it up! 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". |
…, #252 via #257, #195 via #226, #210 via #219, #211 via #220, #248 via #265, #212 via #223) into fix/adopt-record-submodule-placement Brings in 06b5204 (#214), 60d08a1 (#217), 2727b08 (#209), 4b2add8 (#252), aaa40eb (#195), d89fffa (#210), eb6df75 (#211), c3f4987 (#248) and 5751460 (#212) on top of 39ac59c. No rebase. 5751460 landed while the merge of c3f4987 was being checked, and it changes adopt-project.py, scripts/shape_materialize.py and tests/test_adopt_submodules.py, as this branch does, so this one merge takes it too. No conflicts. Both sides change adopt-project.py, scripts/repo_shape.py, scripts/shape_materialize.py, shape-doctor.py, tests/test_adopt_submodules.py and tests/test_shape_doctor.py. Every top-level definition only one side changed is that side's text in the merge, byte for byte. The one thing both changed is shape_materialize.py's imports, and both are kept: main's `codecs` and this branch's `path_spelling_fault` and `PATH_*` codes. Checked: - adopt-project.py: main calls `escape_what_stdout_cannot_encode()` first in `main` (#214). It makes a `.gitmodules` git's submodule reader cannot read a finding where the tip holds no submodule and the history does, or where `ignore`, `update` or `branch` has no value (#195). And every path a finding names is now `_spelled` (#212). This branch's `submodule_record`, `_resolution_text` and the record's preamble are unchanged by the merge, and so are `_answered_legs`, `_kept_in_a_leg`, `_commit_the_split`, `_refuse_what_check_finds`, `cmd_execute` and `Plan`. The record and #195's finding read the kept submodules the same way, `_kept_in_a_leg(_answered_legs(...))` over the tip's gitlinks. `execute` refuses #195's finding in `_refuse_what_check_finds`, before any leg exists and before `_commit_the_split` writes the record. So a source #195 refuses gets no record, and so does a source whose tip holds no submodule. - scripts/repo_shape.py: main refuses an explicit key in a flow mapping and reads an empty flow-mapping value as null (#252). A block scalar's record is now a (key, body) pair, and `_BLOCK_SENTINEL` is gone (#248); nothing on this branch names it. The record is a block sequence of double-quoted scalars, and it reads back through the merged reader with 0 mismatches (`submodule_record`, then `parse_yaml`, path and resolution): - 4,525 names, plus 35 more; - 240 aimed at #252 and #248 (`? a`, `{a: }`, `|`, `a: |`, a NUL, the old sentinel). - scripts/shape_materialize.py: main adds `escape_what_stdout_cannot_encode`, `run()` reading UTF-8 (#217), and `first_and_more(paths, spell)` (#212). This branch's `LEG_SEGMENT_REASONS` and `_spelling_reason` are untouched. - shape-doctor.py: main adds the `manifest kinds` finding for a `family.yaml` beside `project.yaml` (#211), and the stdout escaping. This branch's placement row and `recorded_submodules` are untouched. - tests: #212 respells the finding text that the earlier tests in test_adopt_submodules.py assert. None of this branch's tests quotes a finding. - No top-level name is bound twice in adopt-project.py, scaffold-project.py, scripts/repo_shape.py, scripts/shape_materialize.py, shape-doctor.py, validate-manifest.py or update-shape.py. Each merged module's names are the base's, plus what either side added, minus what either side removed. No side added a name the other side also added, in the tests either. - AGENTS.md is untouched by main: 472 lines, under the cap of 472. - The branch's diff against main is still #189 alone: 12 files, +1670 -156, file by file the same as against 39ac59c. Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…, #209, #252, #195, #210, #211, #248, #212, #251 and #206 merge with one hunk, the materializer import list, kept on both sides, and `_entry_findings` still hands `_leg_findings` the tree and ends with the root-symlink findings origin/main moved 39ac59c -> 60588d5: 06b5204 (#214 via #246), 60d08a1 (#217 via #230), 2727b08 (#209 via #218), 4b2add8 (#252 via #257), aaa40eb (#195 via #226), d89fffa (#210 via #219), eb6df75 (#211 via #220), c3f4987 (#248 via #265), 5751460 (#212 via #223), 23e16ae (#251 via #271) and 60588d5 (#206; its subject names no pull request). Conflict, resolved by keeping both sides: - adopt-project.py, the `shape_materialize` import list. This branch adds BOOTSTRAP, SYMLINK_HOPS and template_files; #214 adds escape_what_stdout_cannot_encode. The resolution is the union. Auto-merged: scripts/shape_materialize.py, where #214's escaping, #217's UTF-8 `run()` and #212's `first_and_more(paths, spell=None)` sit apart from this branch's #183 section, and adopt-project.py's other hunks. #195 adds `import os` at the line where this branch added it, and the merge keeps it once. Checked on the merged tree: - `_entry_findings` still calls `_leg_findings(plan, tree)` and ends with `_root_symlink_findings(plan, source, tree)`. `main` still calls `escape_what_stdout_cannot_encode()` first. - No top-level name is bound twice in adopt-project.py, scaffold-project.py, scripts/repo_shape.py or scripts/shape_materialize.py, and none is loaded but bound nowhere (AST). `first_and_more` gives its new `spell` a default, so this branch's call is unchanged. - Both sides are intact by +/- line sets: each side's change against the base reappears against the other side, apart from the import hunk and the shared `import os`. - #212 says this branch's `plan-root-symlink` names its paths raw, and that whichever pull request lands second should spell them. That was 04792d2. Since d11cf9f every path the finding names goes through `_spelled`. One clash the textual merge does not show is fixed in the next commit: #217's tests/test_git_output_decoding.py refuses a shipped text-mode `subprocess` call that names no codec, and this branch's `_checks_out_symlinks` probe names none. Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e-notice It merged with no conflict. Main brings nine commits since 39ac59c: - 06b5204, six tools escape a name their stdout cannot encode (#214 via #246), with tests/test_stdout_encoding.py; - 60d08a1, `shape_materialize.run()` and 26 other git and gh calls read the child's output as UTF-8 (#217 via #230), with tests/test_git_output_decoding.py; - 2727b08, update-shape.py's `gh pr create --base` (#209 via #218), with tests/test_update_shape_pr_base.py; - 4b2add8, scripts/repo_shape.py's flow mappings (#252 via #257), with tests/test_yaml_flow_mappings.py; - aaa40eb, adopt-project.py refuses a `.gitmodules` the history's gitlinks make unreadable (#195 via #226), with tests/test_adopt_history_gitlinks.py; - d89fffa, family.py `bump` to the current pin (#210 via #219); - eb6df75, shape-doctor.py's family.yaml beside a project.yaml (#211 via #220); - c3f4987, scripts/repo_shape.py's block-scalar records (#248 via #265), with tests/test_yaml_block_scalar_keys.py; - 5751460, adopt-project.py spells every path a FINDING or a refusal names (#212 via #223), with tests/test_adopt_finding_spelling.py. Three files changed on both sides: adopt-project.py, tests/test_adopt_ignored_shape_files.py and tests/test_adopt_verify.py. In each, `git diff -U0` of this merge against 5a8b649 equals main's diff against 39ac59c, and this merge against 5751460 equals the branch's diff against 39ac59c, apart from the hunk headers. Neither side's lines are changed. How main's changes meet this branch's failure handling: - Every definition the late failures go through is byte-identical to 5a8b649's: LATE_FAILURES, the notice (`_nothing_rolled_back`), `_failed_loudly`, `_report_late`, the three builders' handlers, `_verify` and `cmd_execute` among them. Three definitions differ, each by main's hunk alone. - `main()` gains `escape_what_stdout_cannot_encode()` as its first statement (06b5204). The branch writes every REFUSED block and notice to stderr, which Python opens with `backslashreplace` whatever the locale. - `run()` names its codec (60d08a1). Output that is not UTF-8 now reaches the branch's handlers as `CommandFailed`, not as `UnicodeDecodeError`. LATE_FAILURES keeps `UnicodeError`, because `git_out` in scripts/repo_shape.py still decodes strictly. - `Source.registered_submodules` reads the history (aaa40eb). Its one caller in `execute` is `_refuse_what_check_finds`, before `_create_leg_remotes`, so the new refusal comes before any leg repository exists and needs no notice. - 5751460 spells the paths of the verification's findings. The branch's footer and notice follow those findings unchanged. No top-level name is bound twice in adopt-project.py, scaffold-project.py, scripts/repo_shape.py or scripts/shape_materialize.py, and a `symtable` check finds no name they read that nothing binds. On the merged tree, ten test files give 439 passed and 1 skipped (Windows' own stdout default): the two shared ones, main's four new ones that read adopt-project.py (finding_spelling, history_gitlinks, stdout_encoding, git_output_decoding), and test_adopt_late_failure, test_adopt_filter_repo_failure, test_adopt_execute_refusals and test_adopt_plan_escapes. Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>



Lane: openreposhape-5 (openRepoShape-5)
Claim: #252 (comment)
What
Two edges of the reader's flow-mapping entry, found by the adversarial review of #213 (its F3 and F4) and
filed as #252. Both made a flow mapping read differently from its block twin.
?by name (_refuse_unsupported:explicit keys are not supported), and the reader's header lists explicit keys as REFUSED, because the meaning would have to beinvented. That check runs on block lines only, so
k: {? a: b}read as the literal key? a. A flowmapping entry opening
?is now refused with the same phrase. A flow collection has no line to name, so itnames the entry:
explicit keys are not supported: '? a: b'._flow_valueaskedtext[:1] in "[{", which is True for the empty string, so{a: },{a:}and{a: , b: 1}ran_flow('')and were refused asnot a flow collection: '', a text nobodywrote. The question is whether the text OPENS a flow collection (
startswith(("[", "{"))), which an emptyone does not. It reaches
_scalar(''), the null the blocka:reads. A quoted empty ({a: ""}) stays theempty string.
Functions touched, and nothing else:
_split_key_flow(the refusal, beforereturn _split_key(text)),_flow_value(the test), and one appended paragraph in the SUPPORTED/REFUSED header comment._split_key,_flow,_split_flow,_scalar,_strip_commentand_refuse_unsupportedare untouched.Why
"An invented meaning in a validator is a wrong answer with a confident tone." Row 1 was a wrong answer with no
refusal; rows 3 to 5 were refusals of valid YAML naming an empty string a person never wrote. Latent today: no
writer in the repository emits a flow mapping with a key or a value. It shows on a file a person edits, such as
naming: {form: }left half filled inproject.yaml.Evidence
tests/test_yaml_flow_mappings.py, 46 tests: 34 fail and 12 pass on 150c41d (the 12 are controls),46 pass on head. Pins the issue's rows, the places each can stand (after another entry, nested in a
flow mapping and in a flow sequence, as a block-sequence item, in a file read through
load_yaml), the blocktwin equal wherever the block reader reads one, the spelled nulls (
~,null,Null,NULL) equal to theempty value, quoted empties staying
'', and what must NOT be refused ("? a",'? a',?a,a?,b?).(20, 14, 1, 3, 5, 20, 13, 1, 2); the comment hunk and docstrings are behaviour-free and survive.
shipped code): flow == expected == block twin == PyYAML on every one on head; on 150c41d 64,387 of them read
wrong. 100,000 variants with one entry made an explicit key: all refused on head, none on 150c41d. Re-run of
scripts/repo_shape.py
_split_flowignores a backslash inside a double-quoted flow item, sopins: ["x\",y"]reads back as two itemsx\andy"where the same text as a block sequence item reads as the one itemx",y#213's flow/block fuzz (100,000 plus 60,000 with the code-point escapes): 0 mismatches.collection (
pins,allow_empty_legs,also_matches,referent_chain,members: [],paths: []) emitsequences or
[], never a mapping.test_yaml_flow_mappings,test_yaml_flow_quotes,test_load_yaml_refusals,test_adopt_plan_escapes,test_manifest_validator,test_pin_validator,test_neutral_pin_validator,test_repo_hygiene,test_cli_reference,test_adopt_plan: 321 passed, 1 skipped (SHAPE_MEDXEHR_CLONEunset).
test_shape_doctor.py: 181 passed.write files as bytes, so LF on every platform.
Composition
git merge-tree --write-tree HEAD <ref>: clean against origin/main 39ac59c, and clean againstfix/yaml-block-key-nul(9be6287), where the combined tree also passes both writers' new tests together (125).fix/yaml-quote-boundariesandfix/scaffold-referent-chain-linkshad no commit when this was written, sonothing was merged against them.
Left out
?([? a],[? a: b]) still reads as a string, where the block- ? aisrefused. Same class, different branch of
_flow; scripts/repo_shape.py reads? a: bin a flow mapping as the literal key? a, where the block reader refuses explicit keys, and refuses{a: }and{a:}asnot a flow collection: ''instead of reading{'a': None}#252 names the mapping.?followed by a tab, and a bare?block item, are not refused by either form (both look for"? ").{a}) is still refused aswithout a key.[k: v]notes in the issue are unchanged.Closes #252
🤖 Generated with Claude Code
Summary by Sourcery
Align flow-mapping edge cases with block-mapping behavior by refusing unsupported explicit keys and reading empty values as null.
Bug Fixes:
?refuse unsupported explicit keys instead of interpreting them as literal keys.Enhancements:
Tests: