Skip to content

feat(ir): enforce irVersion and record the compatibility policy - #329

Merged
OmarAlJarrah merged 2 commits into
mainfrom
feat/ir-version-enforcement
Aug 9, 2026
Merged

feat(ir): enforce irVersion and record the compatibility policy#329
OmarAlJarrah merged 2 commits into
mainfrom
feat/ir-version-enforcement

Conversation

@OmarAlJarrah

Copy link
Copy Markdown
Member

Summary

Document.IRVersion was written by exactly one line in the OpenAPI compiler and read by
nothing. A document with an absent, empty, stale or nonsense irVersion passed irverify
and pass.Validate with zero violations and zero diagnostics, so a compiler that forgot the
stamp — or a document written by an older generation of the schema — was indistinguishable
from a current one. This adds the read side and writes the policy down.

  • ir.CompatibleVersion(v) bool is the compatibility predicate: exact equality with the
    ir.IRVersion the consumer was built against. Every bump this constant has taken changed
    the JSON shape (0.2.0 split Preserved/RawConfig, 0.3.0 renamed it to Unmodeled), so
    there is no looser relation to admit — a differing patch, a prerelease suffix and a value
    that is not a version at all are equally unreadable.
  • irverify gained a version rule (ir/irverify/version.go), reporting
    ir/ir-version-absent for a document carrying no stamp and ir/ir-version-incompatible
    for one this build cannot read. Two codes because the failures name different writers:
    absence is a producer that forgot, and it is the failure omitempty hides best, since a
    document without the key is byte-identical to one that never had it.
  • docs/ir-design.md §2.1 records the policy: what the version names, what moves it,
    what a bump obliges compilers and emitters to do, what a consumer does on mismatch, and
    the fact that every committed golden embeds irVersion so a bump rewrites the whole
    snapshot corpus.

Why a Violation and not an ir.Diagnostic. The two channels are for different authors:
ir.Diagnostic reports a problem in someone's source spec, irverify.Violation reports our
own bug. An unstamped document is a producer defect and an incompatible one is a fault in the
toolchain pairing; neither is a spec problem. A Diagnostic also lives inside the document,
which is the wrong place to record that the document cannot be read.

Where enforcement lives. In irverify.Verify, which is the gate every consumer runs before
trusting a document, whether it was just compiled in memory or decoded from JSON. Nothing in
the tree decodes a persisted Document today, so there is no loader to attach a separate check
to; ir.CompatibleVersion is the predicate one will call when it is written (#76 is the first
consumer that will need it), and §2.1 says so.

Out of scope, deliberately: no migration machinery. There is no converter between schema
generations and none is planned — a document written by another generation is re-compiled from
its source spec. That is stated in §2.1 rather than left implicit.

The hand-built fixtures that reach irverify.Verify now carry the stamp, for the same reason
they already carry IDs and names: a fixture built to exercise one rule must contribute no
violations of its own. The five test documents that hard-coded a long-stale "0.1.0" now use
ir.IRVersion.

Test plan

  • ir/irverify/version_test.go — absent, and four incompatible spellings (older generation,
    newer generation, non-version, whitespace-padded), each asserting the single expected code
    and the doc.IRVersion path; a clean control; and a round-trip case proving invariant 7 is
    intact, marshalling a valid document, decoding it, verifying it clean, and re-encoding to the
    same JSON.
  • ir/document_test.goTestCompatibleVersion pins the exact-match policy across eight
    spellings including a differing patch and a prerelease suffix.
  • Both halves proven to bite. Dropping the rule's registration in Verify reddens the version
    tests. Separately, deleting the compiler's stamp line reddens TestVerify_Corpus and the
    harness corpus sweep — those were green on main with the stamp deleted, which is the
    acceptance criterion the check exists for.
  • Full gate green: gofmt, go vet, golangci-lint, go build, coverage at exactly 100%.

Closes #70

The version rule holds every hand-built document to carrying a stamp, and this
branch stamped the fixtures that existed when it was written. #319 landed
afterwards with new ones, so nine tests in ir/irverify fail once the two meet:
bigval_test.go and kinds_test.go build documents with no irVersion, and the rule
reports ir/ir-version-absent on each.

Nothing warned. The merge is textually clean and the tree compiles; only running
the suite shows it.

Resolved by stamping those eight fixtures, for the reason this branch already
gives for the ones it stamped: a fixture built to exercise one rule must
contribute no violations of its own. The package's other unstamped documents are
left alone — their tests assert that a particular violation is present rather
than an exact set, so the stamp would change nothing and adding it would be
churn.
@OmarAlJarrah
OmarAlJarrah force-pushed the feat/ir-version-enforcement branch from d9ac3a8 to d915bca Compare August 9, 2026 10:43
@OmarAlJarrah
OmarAlJarrah merged commit 42cb518 into main Aug 9, 2026
1 check passed
@OmarAlJarrah
OmarAlJarrah deleted the feat/ir-version-enforcement branch August 9, 2026 10:48
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.

ir: enforce IRVersion and define a compatibility policy

1 participant