Probed the new guard against a partially-broken derivation rather than only the
total-failure case the floors were written for: deleting the transitive step from
`_request_reachable`, so only schemas named directly on a request body resolve.
Both floors stay satisfied and `test_the_derivation_reached_a_real_population`
stays GREEN. What reddens is the planted-member test — its plant is reached
through a nested `$ref` exactly so that it can — and the disposition test, since
the nested rows vanish and report as PHANTOM.
So the floors are the crude backstop against a parse that reached nothing at all,
and the planted-member test is what actually holds the walk honest. Recorded in
the docstring, because a floor described as anti-vacuity reads as coverage it does
not have, and the next reader deciding which of these to keep working should know
which one is load-bearing.
Self-review of the new guard, before round 3 reported: `_request_reachable`
seeded its walk from POST/PUT/PATCH request bodies only. That reads as obviously
right and already had a counterexample in the document — `DELETE
/api/v1/media-items` carries a request body.
A DELETE body probably cannot cause a full-replace drop. "Probably" is what
produced both of this issue's earlier misses, and a verb allow-list is another
hand-drawn edge on a population that has now been drawn wrongly twice. Every
operation carrying a request body now seeds the walk, so anything it surfaces
must acquire a stated disposition rather than being argued away in advance.
Effect measured: reachable schemas 141 -> 142, droppable set unchanged at 8, set
equality against the registry still holds. So this buys no new rows today; it
removes a place to be wrong later.
Also states in the docstring why the walk is transitive and why `_schema_refs`
recurses over every dict value and list element rather than naming composition
keywords — `oneOf` appears 23 times in the document today, and naming keywords
is the same frozen-filter mistake one level down.
879 passed, 2 skipped.
Round 2 returned BLOCKED. The enumeration added in the previous commit was
itself incomplete: it omitted `ArtworkContentTypeModel`, which carries three
members outside its `required` array and is reachable from the full-replace
`PUT /channels/{id}` via `UpdateChannelRequest.logo`. It was sorted out on its
name — `...Model` reads as a response model — which is the same failure the
enumeration was written to eliminate one level up, and it closed with a false
universal negative ("the remaining schemas ... are never request bodies") sixty
lines below this document's own warning against exactly that.
Two misses from one mechanism, so the mechanism goes rather than the list
getting a third patch.
`scripts/tests/test_optional_request_members.py` derives the population every
run from `ErsatzTV/wwwroot/openapi/v1.json`: every schema with a property
outside `required` that is TRANSITIVELY reachable from a POST/PUT/PATCH request
body, asserted set-equal in both directions against a registry of per-schema
dispositions. Transitivity is load-bearing — `MultiCollectionItemRequest` and
`ArtworkContentTypeModel` are both nested, so a top-level-only check would have
reproduced both misses. Population derived, dispositions hand-written as the
reviewed SCOPE, per testing.guard-derives-population-from-source.
Witnessed red: removing the `ArtworkContentTypeModel` disposition reports it as
MISSING with its three members. That mutation is DECLARED in
`mutation_manifest.py` and re-applied every suite, so the row grades MUTATION
rather than resting on this message. Renaming the key rather than deleting the
entry keeps the module importable, so the red is the MISSING direction and not
an ImportError reddening for the wrong reason.
ArtworkContentTypeModel's real disposition is COMPUTED, not COVERED: the three
members are get-only properties derived from `Path` and are never deserialized,
so omitting them drops nothing — and `Complete<T>` must NOT be applied there,
because it would force a caller to fabricate server-computed values in an
outbound request. That caveat is now in spa-conventions §4b, where a reader
following "annotate each construction site" would otherwise have produced a
wrong payload.
Also reconciles the contradiction round 2 found between the wrapper-boundary
rationale and the snapshot: 13 of ~33 full-replace PUT wrappers carry
`Complete<>` today. The rest are stated as still contingent — the difference
from before is that the contingency is now MONITORED by the derived test rather
than assumed.
L5 the phantom denominator is ten sites, not nine (five accepted, three of those
without a spread or a local). N6 fixture field placed alphabetically. N7 the
record's three-act framing trimmed to the current statement plus the rejected
one, so a cold reader does not have to work out which is live.
Verification: 879 passed 2 skipped in scripts/tests (up from 874), typecheck 0,
eslint 0, 1200/1200 web tests, web build 0, catalog check clean, doc-narrative 0.
Decisions-Edit: yes