Round 7 reviewed the assertion added two commits ago and found it covered 1 of
the 5 nodes where the collision fires — the four it missed being the ones that
matter, since they are where the walk actually qualifies a name. I had already
widened it to walk the whole tree, but the reviewer's recommendation is better
than either version and is taken here: restructure so there is nothing to assert.
A nested inline object lives in its OWN namespace. The defect was qualifying its
members as `parent.child` BEFORE subtracting `required`, which merges the two
namespaces and lets a literal `a.b` member mask a nested `a` -> `b` one. Now
`_optional_of` subtracts inside each namespace and qualifies only the survivors,
so the sets are never mixed. No separator to defend, no assumption to assert, and
`NESTED_SEPARATOR` and its whole-tree checker are gone.
Verified at all five sites — top level of a named schema, one level down, inside
an `allOf` arm, under `items`, and inside an inline request body — every one now
surfaces (`a.b`, `outer.a.b`, `a.b`, `a.b`, `a.b`); four were masked before.
The reviewer flagged a trap in this fix and it is real: keeping a 2-tuple
compatibility shim (`return properties | nested, required`) re-creates the mask
at the merge boundary while the suite stays green. So `_resolve` returns three
sets and every caller changed. There is now a case that reddens exactly that
shim.
Residual, stated rather than hidden: a literal `a.b` and a nested `a` -> `b` that
are BOTH optional conflate into one reported string. That is a diagnostic
ambiguity, not a miss — the schema still enters the population and still forces a
disposition.
Also from round 7: `prefixItems` was the one keyword whose `required` handling no
case pinned (its only case carried none), so discarding it stayed green; it has a
case now. And the assumptions docstring is back to describing two assertions
rather than three, which the separator removal resolved on its own.
REBASED onto ad31a0285 (#825). That change edits the same guard-inventory summary
line this branch does, and the merged truth is neither side's number: 38 guards /
20 proof files / 16 with a mutation proof. Resolved by reading it off
`test_the_summary_counts_match_the_table` rather than by picking a side.
One local trap worth recording: a stale `scripts/**/__pycache__` made pytest
report this commit's new case failing while the source could not produce that
result — `_optional_of` returned the right value when called directly. Clearing
the cache resolved it. I nearly "fixed" correct code on the strength of a red.
946 passed 2 skipped on the new base, ruff check 0, ruff format 0, typecheck 0,
eslint 0, 1200/1200 web, build 0, catalog clean, doc-narrative 0 new.