diff --git a/docs/reference/recogniser-capabilities.md b/docs/reference/recogniser-capabilities.md index ffb48ac4..d1aa9408 100644 --- a/docs/reference/recogniser-capabilities.md +++ b/docs/reference/recogniser-capabilities.md @@ -64,3 +64,34 @@ pass. `bosses` is the fully consumed reference family. `repeating-radial-profiles` is the opposite reference: it remains geometry-only critique evidence for a separately authored gear declaration, with no inferred gear feature added to fill the table. + +## Passage compatibility boundary + +The installed `b123d-recognisers==0.3.1` release already contains the `passages` family introduced +in 0.2.6. Draftwright declares its schema-v1 `Passage` output exhaustively but deliberately keeps +the family `unsupported`, with the drafting decision tracked by issue #1245. This is a truthful +consumer disposition: the geometry remains visible in the aggregate inventory, while Draftwright +does not invent an IR feature, DSL declaration, generated code, drawing annotation, or completeness +requirement for it. + +Draftwright's exact dependency pin must remain on a reviewed released version in the interval +`>=0.2.6,<0.4.0` until the separately reviewed F4b transition is ready. The lower boundary ensures +the installed manifest really contains `passages`; the upper boundary prevents accidental adoption +of the planned 0.4 compatibility change. + +The reviewed future 0.4 model is a distinct migration, not latent behavior in this declaration: + +- `SectionPassage` will be the authoritative physical output and aggregate census source; +- legacy `Passage` values will be an accepted-only compatibility projection; +- `recognise_passages(..., ledger=...)` will be a fail-loud unavailable compatibility operation; +- the writer-free `recognise_passages` name will remain public but non-authoritative; and +- rich split-junction passages can supersede a currently rendered Slot claim, moving ownership to + the Passage family through `SLOT_SUPERSEDED_BY_PASSAGE`; and +- the capability contract must represent those API and per-output roles exhaustively before the + dependency pin can cross 0.4. + +That migration must update the package release, Draftwright adapter and declaration model, +validator, tests, exact pin, and lockfile together. The current validator remains unchanged and +fail closed; there is no forward declaration or stale-manifest exception. Crossing 0.4 also +requires #1245 to decide what those newly Passage-owned occurrences draw, or an explicit reviewed +decision to accept the resulting loss of the existing Slot callout. diff --git a/tests/test_recogniser_capabilities.py b/tests/test_recogniser_capabilities.py index f3ae9939..99196296 100644 --- a/tests/test_recogniser_capabilities.py +++ b/tests/test_recogniser_capabilities.py @@ -125,6 +125,46 @@ def test_installed_package_contract_validates_without_a_sibling_checkout() -> No assert len(package["families"]) == len(declaration["families"]) == 25 +def test_existing_passage_contract_is_truthfully_declared_before_f4b() -> None: + """Pin the current-family declaration separately from the future 0.4 migration.""" + + assert INSTALLED_PACKAGE_VERSION == "0.3.1" + installed = tuple(int(component) for component in INSTALLED_PACKAGE_VERSION.split(".")) + # b123d-recognisers#184 and Draftwright #1337/#1245 must be accepted before this + # upper bound moves: 0.4 changes both the capability model and rendered ownership. + assert (0, 2, 6) <= installed < (0, 4, 0) + package = _families(recognition.capability_manifest()) + declaration = _families(consumer_capability_declaration()) + passage_package = package["passages"] + passage_consumer = declaration["passages"] + + assert passage_package["introduced_in"] == "0.2.6" + assert len(passage_package["records"]) == 1 + passage_record = passage_package["records"][0] + assert passage_record["name"] == "Passage" + assert passage_record["role"] == "output" + assert passage_record["schema_version"] == 1 + assert passage_record["aggregate_membership"] == ["RecognitionResult.passages"] + assert passage_consumer["record_schemas"] == {"Passage": [1]} + assert passage_consumer["disposition"] == "unsupported" + assert passage_consumer["tracking"] == "https://github.com/pzfreo/draftwright/issues/1245" + assert { + passage_consumer[name]["state"] + for name in ( + "ir_adapter", + "dsl_declaration", + "generated_code", + "drawing_consumer", + ) + } == {"unsupported"} + assert passage_consumer["completeness"]["state"] == "deferred" + assert passage_consumer["documentation"] == { + "state": "supported", + "evidence": ["docs/reference/recogniser-capabilities.md"], + } + assert "passages" not in pending_family_declarations() + + def test_runtime_adapter_inventory_is_derived_independently_and_exhaustive() -> None: runtime = _runtime_emitted_records() tiers = [