Skip to content

scripts/repo_shape.py reads ? a: b in a flow mapping as the literal key ? a, where the block reader refuses explicit keys, and refuses {a: } and {a:} as not a flow collection: '' instead of reading {'a': None} #252

Closed

Description

@brettheap

Lane: openreposhape-5 (openRepoShape-5)

Found by this lane's adversarial review of #213's fix as F3 and F4, two edges of the flow-mapping reader that the fix did not touch. Pre-existing on main 54aa69a, and the same on fix/yaml-flow-escaped-quote 86ffe66 (PR #244) and fix/adopt-plan-linebreak-names a02240a (PR #247). Latent: no writer in this repository emits a flow mapping with an explicit key or an empty value (see Reach).

What is wrong, two ways.

  1. An explicit key in a flow mapping is read as a key. The block reader refuses ? by name, and the reader's header lists explicit keys as REFUSED, "an invented meaning in a validator is a wrong answer with a confident tone". _refuse_unsupported (scripts/repo_shape.py:418) runs only on block lines (called at :533). _split_key_flow (:322) has no such check, so in a flow mapping ? a: b is the literal key ? a.
  2. An empty value in a flow mapping is refused as a flow collection. _flow_value (:299-301) is _flow(text) if text[:1] in "[{" else _scalar(text), and for the empty string ''[:1] in "[{" is True ('' in "[{" is True for any string), so _flow('') runs and raises not a flow collection: ''. The block twin a: reads {'a': None}, and so does YAML.
SHAPE=<openRepoShape checkout>
SHAPE=$SHAPE python3 - <<'PY'
import os, sys
sys.path.insert(0, os.environ["SHAPE"] + "/scripts")
from repo_shape import parse_yaml, YamlError
def read(text):
    try:
        return repr(parse_yaml(text))
    except YamlError as exc:
        return f"YamlError: {exc}"
for text in ("k: {? a: b}\n",            # F3: an explicit key in a flow mapping
             "k:\n  ? a\n  : b\n",       #     the same construct in a block: refused
             "k: {a: }\n",               # F4: an empty value in a flow mapping
             "k: {a:}\n",
             "k: {a: , b: 1}\n",
             "k:\n  a:\n  b: 1\n"):      #     the block twin of F4: a null value reads fine
    print(f"{text!r:24} ->", read(text))
PY

Observed on main 54aa69a (the same on 86ffe66 and on a02240a):

'k: {? a: b}\n'          -> {'k': {'? a': 'b'}}
'k:\n  ? a\n  : b\n'     -> YamlError: line 2: explicit keys are not supported
'k: {a: }\n'             -> YamlError: not a flow collection: ''
'k: {a:}\n'              -> YamlError: not a flow collection: ''
'k: {a: , b: 1}\n'       -> YamlError: not a flow collection: ''
'k:\n  a:\n  b: 1\n'     -> {'k': {'a': None, 'b': 1}}

PyYAML 6.0.3 reads k: {? a: b} as {'k': {'a': 'b'}} and each of the three empty-value rows with 'a': None ({'a': None, 'b': 1} for the third). Row 1 is a wrong answer with no refusal: the reader's own rule, applied in a block, would have raised. Rows 3 to 5 are refusals of valid YAML, with a message that names an empty string a person never wrote.

Expected. ? a: b in a flow mapping is refused as the block form is, explicit keys are not supported, since the reader declines to invent a meaning for it; the refusal sits where both forms meet, not only on block lines. {a: }, {a:} and {a: , b: 1} read {'a': None} and {'a': None, 'b': 1}: _flow_value treats an empty value as null (_scalar('') already returns None), and the [{ test asks whether the text OPENS a flow collection (text[:1] in ("[", "{"), or text.startswith(("[", "{"))), which an empty string does not. A test pins the rows above.

Reach. Nothing writes either form. The four flow writers emit sequences of bare words (adopt-project.py pins and allow_empty_legs, scripts/shape_materialize.py also_matches and referent_chain) and members: [] / paths: []; no writer emits a flow mapping with a key or value. So it shows only on a file a person edits or another tool writes, a flow mapping such as naming: {form: } left half-filled in project.yaml. None of the 14 tracked YAML files holds a flow mapping other than a template placeholder (git ls-files '*.yaml' '*.yml', searched for : {, - { and a line opening ? ).

Adjacent and not part of this issue, from the same review: empty flow ITEMS are dropped silently ([,] reads [], [a, , b] reads ['a', 'b'], where YAML refuses the second) and [k: v] reads as the one string k: v; both are older and untouched by the fix. They are listed so the reader's next differential check covers them.

Found by. handoffs/openRepoShape/attachments/lane-openRepoShape-5/review213-report.md in opensoft/brett-wip (F3 and F4). Re-run on main 54aa69a, 86ffe66 and a02240a for this issue.

Not claimed.

🤖 Generated with Claude Code

Activity

  1. brettheap commented on Oct 11, 2026

    @brettheap
    ContributorAuthor

    CLAIMED — lane openRepoShape-5, session e62c2fd7-176d-429d-89fb-0653dd08ad5f@Eagle, 2026-10-11T21:45:38Z, for #252

    Logged in lanes/log/openRepoShape-5.md at 7effddbcf0fe7250075f257f5a2457d28db8ca0e.

    The three reads (lane-collision-protocol Rule 1):

    1. existing `CLAIMED —` comments on opensoft/openRepoShape#252:
       none
    2. `gh pr list --repo opensoft/openRepoShape --state all --search "#252"`, kept where the row or its body names `#252`:
       none
    3. `git ls-remote --heads git@github.com:opensoft/openRepoShape` branches naming `252` as a whole token:
       none
    
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions