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 source via anchoring.code_tokens_by_line (normalized space-joined
  • Collect findings whose anchoring.enclosing_qualname(source, lineno) == descriptor.qualname
  • If occurrence is 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.