Repository navigation
irverify: an operation, service or group ID that lost its separator verifies clean #802
Description
Problem
An ID is <kind>/<namespace>/<path>, and the first two separators are all that say where the namespace ends. A producer that glues the path onto the namespace emits an ID that is well-formed in a namespace named for the concatenation:
op/openapi/paths/~1pets/get what the producer means
op/openapipaths/~1pets/get separator lost: namespace "openapipaths", path "~1pets/get"
irverify holds IDs to the grammar in two ways, and neither reaches this for every class:
- Shape (
ir/id-malformed) cannot see it, because the result is well-formed. - Agreement of the path with the pointer the node records (
ir/id-provenance-disagreement) does see it, but only where the node records the pointer its ID was derived from: types, security schemes and properties.- An operation's provenance records where its body is declared and its ID where it is mounted, and the two differ for an operation reached through a
$ref'd path item (component-reuse.yamlin the corpus). - A service records no pointer.
- A group has no provenance, and is minted by a grouping rule rather than found at a coordinate.
- An operation's provenance records where its body is declared and its ID where it is mounted, and the two differ for an operation reached through a
So for operations, services and groups a lost separator verifies clean, and no check on the finished ID could see it.
Inside this repository a compiler cannot produce one. The framework supplies the separator when the namespace and the path arrive as separate arguments, and an architecture test holds compilers to building a namespace only from a string literal (added with operation-group IDs, #801). What is left is any document whose producer is not one of this repository's compilers: a decoded JSON document, a pass, an out-of-tree compiler. The IR is the ABI, so those are in scope.
Proposal
Let a document declare the namespaces its IDs use, and have irverify check membership.
Documentgains a member holding the set of namespaces. A producer states it the way it already stampsIRVersion.irverifyreports an ID whose namespace (ir.IDSpace) is not in the set, once per ID, at the node. The shared primitive namespace is owned byir(IDSpacePrim) and needs no declaration.- The check applies to every class that has a kind prefix, so it also covers types and security schemes, and it needs no format knowledge and no recorded pointer.
- Each compiler builds the set from the namespace constants it already has. A test per compiler holds the declared set equal to the set its corpus actually uses, so it cannot drift in either direction: an undeclared real namespace would be a false violation, and an unused declared one a loophole.
IRVersionmoves, perir-design.md§2.1, since every document gains a key.
Open points
- A flat set or one per kind. A flat set is the smaller contract and is enough for a lost separator, which creates a namespace that exists nowhere. A set per kind would also catch an ID using another kind's namespace.
- Whether a declared namespace that no ID uses is reported. Reporting it closes the loophole, at the price of a rule every producer must keep in step.
Alternatives considered
- Record on
Provenancethe pointer an ID was derived from when it differs from where the node is declared. Format-specific, widens everyProvenance, and does nothing for services or groups, which derive from no pointer. - An unambiguous delimiter between the namespace and the path. Makes the defect structural, but rewrites every ID in every document, golden and test. Far larger than the problem.
- Flag a namespace that extends another in use (
anonaddrbesideanon). A heuristic with false positives, because legitimate namespaces can share a prefix. - Do nothing. The protection then stays at the source, which is sufficient while every producer is in this repository.
When to do it
It protects only against producers outside this repository, and there are none yet. It becomes worth doing when the first out-of-tree compiler, or an external producer of IR JSON, appears.
Acceptance
- A document with an operation, service, group, property, type or security-scheme ID in an undeclared namespace is reported by
irverify, one violation per ID, at the node. - The primitive namespace needs no declaration.
- The OpenAPI compiler declares its namespaces, and a test fails when the declared set and the set its conformance corpus uses differ in either direction.
ir-design.mdrecords the member, the check and the version-history entry, and the sentence in §3.1 sayingirverifycannot see a lost separator is removed.- Regenerating the goldens changes only the version line and the new member.
Activity
docs: spell optional group references as pointers and cite #802
OmarAlJarrah commentedon Oct 9, 2026
Implemented in #801, which closes this on merge. The two open points were settled as follows.
Decided in the same change: the declaration is required, like
irVersion, and a document declaring nothing while carrying IDs is reported once and not per ID. It rides the 0.7.0 bump that addingOperationGroup.IDalready makes, so it costs no further generation.