Skip to content

docs(ir): answer what a non-emitter consumer relies on - #1351

Open
OmarAlJarrah wants to merge 5 commits into
docs/emitters-verificationfrom
docs/ir-consumer-contract
Open

OmarAlJarrah wants to merge 5 commits into
docs/emitters-verificationfrom
docs/ir-consumer-contract

Conversation

@OmarAlJarrah

@OmarAlJarrah OmarAlJarrah commented Nov 2, 2026 •

Copy link
Copy Markdown
Member

Summary

The IR's design documents answer every question as an emitter would ask it. A catalog, a linter or a diff that takes the IR as its input format asks other ones: does an ID mean the same thing in the next revision, can I read this Unmodeled key next month, what do I pin, and why is the document this large. This PR answers them where such a consumer will look.

  • Identity across revisions (docs/ir/identity.md#across-revisions). An ID identifies an entity within one document, and across revisions only while its pointer holds still. Renaming a position gives new IDs. A reordered array is worse: it hands an old ID to a different entity, so a consumer matching by ID silently attaches one entity's state to another. On the golden spec, 9,957 of 41,697 types, 13,466 of 32,291 enum members, 5,871 of 49,434 properties and all 8 servers have an ID that passes through an array index. The section quotes the jq command that counts them.
  • Correlation keys (docs/ir/derived-facts.md#correlation-keys). This is the design that carries an entity across revisions: a key built from what the document names the entity by, never from an array position or a file location. It is derived on demand in irfacts, never stored. A key two entities share identifies neither, because a wrong match is the failure the key exists to prevent. A pass was the issue's draft; a derived fact is the better home, for the reasons open question 4 settled for usage: it needs nothing the document lacks, and a stored copy goes stale. Building it is pass: compute a key for matching entities across spec revisions #679, done in feat(irfacts): derive a correlation key for every entity #1354, the next PR in this stack.
  • What an Unmodeled key promises (docs/ir/unmodeled.md#what-a-key-promises, docs/ir/ir-version.md). A vendor extension's name is the source's. Every other part of a key (prefix, keyword, scope) is the compiler's spelling, so renaming one, moving an entry to another scope or reason, or changing its value's form moves IRVersion, as a renamed symbol does. A no_ir_home entry is promised to leave in the change that adds its field, and that generation's history entry names the key. No test holds the vocabulary yet: test: pin the Unmodeled keys a compiler spells to IRVersion #1350 is filed for a pin like the name pin.
  • A page for these consumers (docs/ir/consumers.md). What a reader finds and where, including what has no typed home yet (ir: decide whether a path item's summary and description get a typed home #1319, ir: response links have no home #669). Also what to pin, when a stored document must be recompiled, and the size decision.
  • Size. The IR is 7.0x the compact source and 4.0x under gzip. IDs take 23% of the type registry's bytes and provenance 20%. The compiler gets no provenance-trimmed mode. A probe showed that a document stripped of provenance still passes irverify, which would have to stop checking the ID-to-pointer agreement, so the stripped document proves less and looks the same. A slice of a document is the planned filter pass, whose output is a document like any other.
  • Open question 5 now records this answer and leaves only the structural third tier open. The emitter documents that cited State the cross-revision identity answer: pointer IDs are stable within a document only #419, Unmodeled key names are an unversioned contract that consumers are currently forced to read #429 and ir: an ID derived from an array index names a different entity after the array changes #678 now link the sections.

Closes #418
Closes #419
Closes #678
Closes #429
Closes #430

#432 is answered except its first question: no release is tagged, so the pin is a commit, resolved by Go as a pseudo-version. Cutting a tag is a release decision this PR does not take, so #432 stays open for it.

#1327 (open) renames openapi:pathItemSummary and openapi:pathItemDescription without moving IRVersion. Under the rule stated here, that rename takes a version bump.

The second commit, which corrects the correlation key table after the implementation in #1354 measured it against the golden spec, has a 67-character subject, 75 with this PR's number appended. The branch has two commits, so the PR lands under its title, which fits within 72.

Stack

Stacked on #1339, the last PR of the emitter-design stack, because it edits lines #1336 changes. Merge #1329 through #1339 in order, then this PR. GitHub then retargets it to main, and "Update branch" merges cleanly.

Test plan

  • Each count re-derived on the golden spec (spec @319dc756, morphic @b206dc99). The ID counts come from the jq command identity.md quotes. The sizes come from the compact and gzip-6 encodings of morphic compile -o output and of the source, and the byte shares from a walk of the type registry.
  • Probe: testdata/golden/openapi/petstore.yaml compiled, stripped of every provenance member and decoded again, gives no irverify finding either way. The size section states this.
  • Push checklist on the branch: subjects within 72 characters with the number, no artifact hits, no conflict with main or the base, and skills_lint clean.
  • CI: TestDocuments_LinksResolve holds the new anchors (#across-revisions, #correlation-keys, #what-a-key-promises, #what-to-pin, #size, #22-ir-passes-ir--ir).

A catalog, a linter or a diff reads the IR as an input format, and the
design documents answered its questions only for emitters. This states
the answers where such a consumer will look:

- identity across revisions: an ID holds only while its pointer does,
  and an array index can hand an old ID to another entity, so matching
  across revisions rests on a correlation key derived from what the
  document names an entity by (designed in derived-facts.md);
- what a key in Unmodeled promises: a vendor extension's name is the
  source's, every other part of a key is the compiler's spelling, and a
  change to it moves IRVersion as a renamed symbol does;
- what to pin, and when a stored document must be recompiled;
- why the compiler has no provenance-trimmed mode, and that a slice of
  a document is the planned filter pass.

Open question 5 now records the answer and leaves only the structural
tier open. The emitter documents cite the new sections in place of the
issues they close.
Building the correlation keys against the golden spec showed three rows
of the design wrong. Servers there share URL templates, so a server is
keyed by the scope naming it as well. A header carries a property ID of
its own, so it is an entity to key, not only a type. And two
declarations of one wire name in one composed model would leave both
unkeyed, though the flattening takes one, so the key follows the
flattening. A variant has no ID, so the table now keys its type rather
than the variant.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant