Contracts
descriptor-resolver.md
Contract — Content-Descriptor Resolver (IC-DESCRIPTOR)
The shared helper every WS1 gate imports. Location: tests/architectural/_ratchet_keys.py (or a sibling test-support module). Reuses contracts/anchoring.
Types
ContentDescriptor = tuple[rel_path: str, qualname: str, token_substring: str,
occurrence: int | None, rationale: str]
CompositeKey = tuple[str, str] # (qualname, token_line) — from anchoring.composite_key
resolve_descriptor(source: str, descriptor: ContentDescriptor) -> CompositeKey
tokens; f-string interiors/strings/comments dropped).
AND whose normalized token line contains descriptor.token_substring.
count is 0 or (with no occurrence) >1. Never silently pick the first.
- Tokenize
sourceviaanchoring.code_tokens_by_line(normalized space-joined - Collect findings whose
anchoring.enclosing_qualname(source, lineno) == descriptor.qualname - If
occurrenceis set, select that ordinal (0-based) among matches in file order. - MUST yield exactly one finding. RAISE / FAIL (RED) if the match
- Return
anchoring.composite_key(source, finding.lineno).
descriptor_still_live(source, descriptor, seeded_key: CompositeKey) -> bool
(exactly-one resolution AND key equality). Any deviation (0 matches, >1, or a different key) → False → the twin-guard reds → the entry must be deleted.
- Returns True iff
resolve_descriptor(source, descriptor) == seeded_key - Forbidden: "≥1 finding matches" semantics (D-1 bite hole).
Authoring rule
token_substring is written in the normalized token space (coord_branch or _current_branch, parent . parent, get_current_branch (), NOT raw source (feature_dir.parent.parent). A raw-source substring matches nothing → vacuous green → caught by the FR-013 non-vacuity self-test.
positional-anchor-ban.md
Contract — Standing Positional-Anchor Ban (IC-METAGUARD, FR-004)
New test tests/architectural/test_ratchet_positional_anchor_ban.py. Generalizes DIR-041 anchoring.is_file_line_anchor / FORBIDDEN_POSITIONAL_FIELDS from the Contract Registry to every ratchet allow-list. Delivers #2077's recurrence guard.
What it scans
(module-level tuples/frozensets/dicts feeding a set-membership or comparison).
inline_meta_read_allowlist.yaml.
- All Python under
tests/architectural/: allow-list seed constants - The two YAML allow-lists:
resolution_gate_allowlist.yaml,
Rule
a seed/key later read for set-membership, key-equality, or staleness comparison.
1. A non-authoritative line: locator field explicitly documented as "navigation only; no comparison/membership/count reads it" (the existing convention in the two YAMLs). 2. Count-floor baselines (an integer that is a count, not a position). 3. An occurrence ordinal inside a ContentDescriptor (a scan index, not a line).
- BAN: an integer used as a line anchor in an authoritative comparand —
- PERMIT:
Authoritative-vs-diagnostic detection
Classify a tuple/field as authoritative iff its value flows into a set-membership/==/<= comparison against a live finding. A field only read for error messages / navigation is diagnostic. When in doubt, the seed carries an explicit marker (e.g. a # diagnostic-locator comment or a typed wrapper) so the guard's classification is not a heuristic.
Enumeration duty (FR-014)
The guard's report enumerates the two deferred path::qualname census allow-lists (test_org_activation_seam._BUILTIN_ONLY_ALLOWLIST, test_coord_read_residuals_closeout._IDENTITY_CALLSHAPE_KNOWN_RESIDUALS) as known-relocation-anchored-but-out-of-scope, with the follow-up reference.
Sequencing
Written red-first; goes green only once every in-scope WS1 line seed is migrated. It must NOT be merged before the WS1 migrations (else it reds them and blows NFR-004). Non-vacuity self-test: plant an int line anchor in a scratch authoritative seed → guard reds.