# Migrating 4.0.2 to 4.0.3

5 September 2026.

Nothing to do. OBDS 4.0.3 is a public-surface release. It corrects the language annotation of the homepage, separates the search weight of the raw specification files from the pages written for readers, and adds one verified row to the comparison page. No normative OBDS contract, schema, runtime behaviour, conformance behaviour or interoperability contract changed. Every contract, hash and conformance result is identical to 4.0.2;
only the version stamp, the release-file names and the public-surface documents move. An
implementation that conforms to 4.0.2 conforms to 4.0.3 without changing a line.

## Open item: no dedicated German URL

English and German share one URL. The homepage carries both languages in the same document
and switches between them in the browser, so there is no `/de/` address to give a canonical
tag or an `hreflang` target of its own. `x-default` and the existing `en` and `de`
references are the best annotation available for a single-URL page, and they are in place.

What this costs while it stays open: a search engine cannot rank the German copy on its own
terms. There is one indexable document, its title and description are English, and the
German text is only present in the markup, so a German-language query has nothing addressed
to it to match. A reader arriving from a German search lands on an English page and has to
find the toggle. The German copy also cannot be linked to, quoted or shared as a distinct
page, and any measurement of German traffic is folded into the English figure. Closing it
means real `/de/` addresses, redirects and a sitemap rewrite, which is a URL structure
change rather than a documentation patch, so it gets its own pass.

---

# Migrating 4.0.1 to 4.0.2

5 September 2026.

Nothing to do. OBDS 4.0.2 is a documentation, discoverability and public-surface release. It improves current release consistency, examples, search discoverability and machine-readable navigation. No normative OBDS contract, schema, runtime behaviour, conformance behaviour or interoperability contract changed. Every contract, hash and conformance result is identical to 4.0.1;
only the version stamp, the release-file names and the public-surface documents move. An
implementation that conforms to 4.0.1 conforms to 4.0.2 without changing a line.

The release adds explainer pages, a comparison page and a research index to the website.
None of them is normative. Where they describe OBDS behaviour they restate the
specification; where the two ever disagree, the specification is authoritative.

---

# Migrating 4.0.0 to 4.0.1

5 September 2026.

Nothing to do. OBDS 4.0.1 is a documentation and public-surface consistency release. It corrects current release metadata and improves access to existing examples. No normative OBDS contract, schema, runtime behaviour, conformance behaviour or interoperability contract changed. Every contract, hash and conformance result is
identical to 4.0.0; only the version stamp, the release-file names and the
public-surface documents move. An implementation that conforms to 4.0.0 conforms
to 4.0.1 without changing a line.

If you read the examples: `examples/README.md` previously showed the pre-4.0
flat artefact name for the `foundation-minimal` build. The real 4.0 output is
`generations/<generation digest>/target-<identity digest>.context.json`. Read
`artifactRef` from the build report rather than constructing a path from
`targetId`. Section 13.4. The rendered walkthrough is at
<https://openbranddefinition.org/examples/>.

---

# Migrating 3.0.4 to 4.0.0

5 September 2026.

Rebuild production outputs with compiler 4.0.0. Read the returned generationId and artifactRef; do not construct filenames from targetId or load a latest/flat target file. Use run_generation_with_model(output_dir, generation_id, target_id=..., task_input=..., model=...) for production; select the old generation explicitly for rollback. Existing snapshots are not revoked. Regenerate Model Input Packages using the 4.0.0 projection contract and a registered verifier: old package hashes alone do not prove provenance. Update record consumers to schemas/4.0.0/runtime-decision-record.schema.json and handle model_failed as withheld output with a called adapter. Update build-report consumers to schemas/4.0.0/build-report.schema.json. Manifest, Build Plan and Compiled Context schema versions remain 1.0.0, 3.0.0 and 3.0.0 respectively. Correct manifests that only passed the old hand-written validator: owner/name types, approval identity and real timestamp validity now matter. Retrieval classification remains package metadata and cannot manufacture stateMap facts. No dependency, CI, performance or unrelated refactoring changes are part of this migration.

---

Previous release history follows unchanged.

# OBDS Migration Notes

Start at the section for the release you are on. Coming from 2.x, that is
"2.0.0 to 3.0.0"; coming from 3.0.x there is nothing to change in your files,
though 3.0.2 changes two governed outcomes — read that section before you rely
on a stored build report.

## 0.9.9 to 1.0.0

1. Set `schemaVersion` to `1.0.0`.
2. Use the single current specification document.
3. Remove dependencies on a separate `OBDS-CORE` document.
4. Keep `obds-foundation` in every manifest.
5. Treat optional profiles and runtime capabilities as parts of the same specification.
6. Rename references to `CORE Check Registry v1` to `Foundation Check Registry v1`.
7. Re-run the 1.0 schemas and every claimed capability suite.

## Brand State migration

OBDS 1.0 Brand States are:

- `defined`
- `unknown`
- `not_defined`
- `not_applicable`

A pre-1.0 `state: prohibited` element must become an explicit RULE with `obligation: prohibit`, exact scope, enforcement and validation mode. Do not silently map it to another knowledge state.

## Value Contract migration

Every defined FACT value declares `valueContractRef`. The referenced contract carries:

- `shapeHash`;
- `schemaRef`;
- `schemaHash`; and
- optional `validatorRef`.

Several contracts may exist for the same family and kind when approved shapes or contract versions differ. Recompute approval hashes after migration.

A pre-1.0 PATCH-style release that changed value shape or value contract must be reviewed as a compatibility event rather than carried forward automatically.

## Context Assembly migration

Compile targets before assembly. The Compiled Brand Context carries the target-scoped element records and Context Assembly policy. Normal assembly no longer scans the Brand Manifest. Manifest access is reserved for explicit `manifest_checked` no-hit resolution.

## Semantic Boundary migration

Qualitative prose may remain prose. Where a precise IS / IS NOT decision boundary materially improves review, use `family: stance`, `kind: semantic-boundary`, `nature: knowledge` and the standard semantic-boundary contract.

## Pre-publication hardening carried into 1.0.0

Before publishing 1.0:

1. add `asOf` to every Build Plan;
2. assign a shared element `subject` wherever multiple scoped elements are alternatives for the same decision; elements without an override relationship may omit it and default to their ID;
3. ensure scope values are strings;
4. validate governed JSON and YAML with duplicate-key rejection and YAML 1.2 boolean semantics;
5. replace rule obligation `allow` with `permit`;
6. ensure every defined RULES element resolves to a rule value contract;
7. confirm every declared Brand Profile is supported by the consuming implementation;
8. reclassify any PATCH containing value, subject, state, scope, validity, classification, addition or removal changes as MINOR or MAJOR as appropriate; and
9. regenerate approval, plan, compiled-context and derived-view hashes after migration.

## 3.0.0 to 3.0.1

Nothing to do.

3.0.1 is a packaging correction. No normative contract changed, so no manifest,
Build Plan, compiled artefact or stored hash moves. `schemaVersion` stays `3.0.0`
in Build Plans and compiled contexts, and the published 3.0.0 contracts are
byte-identical.

The one difference you can observe: the release archive now contains `tools/`, so
the two documented commands

```text
python reference/run_all.py
python reference/release-gate.py
```

run from an unpacked `OBDS-3.0.1-FINAL.zip` as well as from a clone. Under 3.0.0
they did not, because the surface registries named files the archive did not
carry.

## 3.0.1 to 3.0.2

Nothing to change in your files.

No normative contract changed. `schemaVersion` stays `3.0.0` in Build Plans and
compiled contexts, the published 3.0.0 contracts are byte-identical, and no
stored hash moves for any build that succeeded under 3.0.1.

Two governed outcomes do change, because the compiler and the runtime were
brought back to contracts 3.0 already publishes. Both changes are the same
shape: a decision that was wrong against the specification is now right.

**1. A conflict this target never reads no longer fails it.** Section 10.2a has
said since 1.1.3 that a hard conflict fails a target only when the conflicted
subject is decision-relevant to it. 3.0.0 replaced that test with "every
target-applicable conflict is decision-relevant", so a manifest defect on a
subject a target neither requires, nor references, nor carries into STATE_MAP or
STYLE_TEXTURE failed that target. If you have a build that failed under 3.0.1
with `OBDS-BUILD-SUBJECT-CONFLICT` and none of the five section 10.2a criteria
applies to it, that build now succeeds. The conflict is still reported in
`conflicts[]`, marked `decisionRelevant: false`, because it is still a manifest
defect worth repairing. A conflict the target *does* read still fails it, as it
always has.

**2. A runtime call naming a target the artefact is not for now fails closed.**
Section 26.2 requires exact target loading. The build side enforced it and the
runtime side did not: a caller passing `target_id` was answered from whatever
artefact it held, the model was called, and the Runtime Decision Record recorded
the target that was *asked for*. The requested identity is now bound to
`artefact.targetId` before any model call, on the canonical form of section
8.0a, and a mismatch produces `no_valid_artifact` with zero model calls. Passing
no `target_id` still accepts the artefact's own, and a canonically equivalent
spelling is the same target. If any of your code passes a `target_id` that does
not match the artefact it loads, that call is a bug this release surfaces.

## 3.0.2 to 3.0.3

Nothing to do.

3.0.3 is a documentation and public-surface neutrality patch. No normative
contract changed, so no manifest, Build Plan, compiled artefact or stored hash
moves. `schemaVersion` stays `3.0.0` in Build Plans and compiled contexts, and
the published 3.0.0 contracts are byte-identical.

The one difference you can observe is in the specification text: the Scope
example in section 9 now reads `productFamilies: [example-family]` instead of a
sector-specific value. `productFamilies` is unchanged; the example value was
never normative.

## 3.0.3 to 3.0.4

Nothing to do.

3.0.4 is a documentation and public-positioning patch. No normative contract
changed, so no manifest, Build Plan, compiled artefact or stored hash moves.
`schemaVersion` stays `3.0.0` in Build Plans and compiled contexts, and the
published 3.0.0 contracts are byte-identical.

The difference is public wording only: the project describes itself as an open
specification rather than an open standard. Nothing in the specification text
changed except its own version stamp and the release-file name in section 33.

## 2.0.0 to 3.0.0

3.0.0 is the Semantic Closure release. It changes five normative contracts, and
four of the five cannot invalidate anything you already have: they close paths
that produced a governed answer nobody intended. The one that touches your files
is the Build Plan.

Work through it in this order.

### 1. Build Plans: `schemaVersion` and two required target fields

**This is the change every 2.x project has to make.**

```yaml
schemaVersion: 3.0.0        # was 1.0.0

targets:
  - id: brand-assistant-de-at
    scope: { ... }
    maxTokens: 2400

    stateMap:                 # now required on every target
      mode: all_applicable    # none | kinds | all_applicable
      kinds: []               # required only when mode = kinds

    styleTexture:             # now required on every target
      mode: all               # all | selected | none
      elementIds: []          # required only when mode = selected
```

Both decide what reaches the compiled context, so an absent one was a governed
decision made by whichever implementation supplied the default. Write the values
your build already had; if you never declared them, the values above are the
defaults 2.x applied.

Nothing else in the Build Plan changes. `requiresDefined`, `contextAssembly` and
`releasePolicy` stay optional, because an absent one denies rather than grants.

Validate against `schemas/3.0.0/build-plan.schema.json`. Do not validate a 3.0
Build Plan against the frozen 1.0.0 contract.

### 2. Brand Manifests: check your RULE values

A RULE value may no longer carry `validatorRef`, and `validationMode:
deterministic` now requires at least one registered Foundation check.

```text
grep -n "validatorRef" your-manifest.yaml
```

Every hit inside an `elements[].value` of `family: rules` has to go. A hit inside
`valueContracts[]` is a value-contract `validatorRef` and stays: that is where
Foundation Validator Registry v1 has always applied.

If a rule was `validationMode: deterministic` with `checks: []`, it enforced
nothing. Decide which it is:

- it should enforce something mechanically: add a Foundation check;
- it is a judgement: change `validationMode` to `semantic`, `human` or
  `external`.

Every `checks[]` entry is now validated where it is written, not only where it is
compiled. An unregistered primitive, an unregistered `match` mode, an
unregistered `term_required` mode, or `phase: preflight` without
`appliesTo: task_input`, fails manifest validation instead of surfacing later as
a build error.

A `literal_required` check that defers its literal through `elementValueRef` no
longer has to carry a placeholder `literal` beside it. If you added one to get
past 2.x validation, you can remove it.

### 3. Identity strings: CR and LF

An identity string may no longer contain a carriage return or a line feed. NEL,
LINE SEPARATOR and PARAGRAPH SEPARATOR are unaffected.

```text
grep -nP "\r" your-manifest.yaml your-build-plan.yaml
```

In practice this finds a CRLF file ending, not an identity, and CRLF line endings
in the file itself are fine. What is refused is a line break inside an `id`, a
`version`, a `subject`, a `kind`, a `valueContractRef`, a scope value or a
reference. If you have one, it was already ambiguous: canonicalisation folds CR
and LF into the same bytes, so two such identities were one hash and two strings.

### 4. Governed input: an object root

A governed JSON or YAML document whose root is an array, a scalar or null is
refused, at every reader and every entry point. In 2.x one reader refused it and
four returned it.

If a tool of yours fed OBDS a sequence-rooted document, wrap it in an object. No
manifest, Build Plan or compiled artefact this project ships was ever
sequence-rooted.

### 5. If you consume Compiled Brand Contexts, Model Input Packages or Review Results

Nothing in your documents changes, but what a conforming consumer does with them
does.

- A 3.0 Compiled Brand Context declares `schemaVersion: 3.0.0` and is validated
  against `schemas/3.0.0/compiled-context.schema.json`.
- `elementRecords[]` items must carry `id`, `family`, `kind`, `nature`, `state`,
  `scope`, `validity`, `sourceRefs` and `annotations`. Records the reference
  compiler produced already do.
- `validFrom` and `validTo` must be `null` or an RFC 3339 date-time.
- A consumer validates the published contract, reproduces the required hashes and
  binds the required identities **before** reading any field. A Model Input
  Package or Review Result naming a different manifest `id`, `version` or
  `contentHash`, or a different `targetId`, is now refused however correctly its
  own hashes were recomputed.
- A compiled check must carry every parameter that changes its outcome. A runtime
  no longer supplies a missing one; it refuses the check.

If you built your own runtime against 2.x, the practical work is: run the
contract first, reproduce rather than compare, and stop filling in absent check
parameters.

### 6. Rebuild and re-hash

```text
python -m obds_ref.cli validate <manifest>
python -m obds_ref.cli build <manifest> <build-plan> --out <dir>
python reference/run_all.py
```

The Build Plan change moves `planHash`, which moves `artifactHash`. Regenerate
compiled contexts, derived views and any stored hashes downstream of them.
Approval hashes on the manifest move only if you changed the manifest.

### What does not change

No Brand State, no profile, no capability, no architecture. The frozen OBDS 1.0.0
contract surface and the 1.1.0 contract beside it are byte-identical to what 2.0.0
published. Manifest `schemaVersion` stays `1.0.0`. The Model Input Package and
Review Result contracts stay at their published 1.0.0 versions; 3.0 enforces them
rather than republishing them.

## 1.1.6 to 2.0.0

**This is the only OBDS release so far that can invalidate a manifest you already
have.** Almost certainly it does not. Run the check at the end of this section
and you will know in a second.

No schema changed. No Brand State was added. No capability, profile or field was
added or removed. `schemaVersion` stays `1.0.0` for manifests and `1.1.0` for
compiled contexts. If your documents pass the check below **and contain no plain
scalar written in exponent notation**, an OBDS 2.0 implementation reads them
exactly as 1.1.6 did, and every hash you have stays valid. Exponent notation is
the one change the check cannot report, because the document stays valid and
only its value moves; it has its own check below.

### What changed

Section 28 always made JSON the canonical interchange format and allowed YAML
where it produces an equivalent JSON document. It never said how a YAML plain
scalar becomes a JSON value. Section 28.1 now says, and that pins two things
that were previously left to the parser.

**One class of form changes meaning: exponent notation.** An unquoted scalar
written with an `e` or `E` exponent — `1e3`, `1E3`, `2E-2`, `1.5e3`, `-2e-2`,
`9e9` — was the *string* under a YAML 1.1 reader and is the *number* under 2.0,
which is what the same characters have always meant read as JSON. The class is
every plain scalar matching the JSON number grammar with an exponent, except the
spellings a YAML 1.1 reader already read as a number, which are those carrying
both a decimal point and a signed exponent (`1.0e+3`, `1.23e-4`); those are
unchanged. This is the only change in the release that is silent: the document
stays valid and the value moves under it, so a hash computed over it moves too.

**The rejected forms.** Each is a form some YAML version reads as a value the
JSON grammar does not produce, so leaving it accepted meant a document whose
meaning depended on its reader. **Was** is what the OBDS 1.1.6 reference reader
did with it, which is not always what YAML 1.2 does; the two disagreeing is the
whole reason the form is rejected. Write the **Write instead** cell exactly: a
form that was a string must stay a string, or your `approval.contentHash` moves.

| Written in your YAML | Was, under 1.1.6 | Is now | Write instead |
|---|---|---|---|
| `017` | the number 15 | rejected | `15`, or `'017'` for the string |
| `017.5` | the number 17.5 | rejected | `17.5`, or `'017.5'` for the string |
| `017e3` | the string `"017e3"` | rejected | `'017e3'` |
| `+42` | the number 42 | rejected | `42` |
| `1.` | the number 1.0 | rejected | `1.0` |
| `1.e3` | the string `"1.e3"` | rejected | `'1.e3'` |
| `.5` | the number 0.5 | rejected | `0.5` |
| `1_000` | the number 1000 | rejected | `1000` |
| `1_000.0`, `1_0.5`, `.5_0`, `0.0_` | the numbers 1000.0, 10.5, 0.5, 0.0 | rejected | `1000.0`, `10.5`, `0.5`, `0.0` |
| `12:30` | the number 750 | rejected | `'12:30'` |
| `1_0:30`, `40_:3`, `4_:1:2` | the numbers 630, 2403, 14462 | rejected | quote them, or write the number you meant |
| `2026-09-01` | already rejected: a date object, which governed JSON has no type for | rejected, with a message that names it | `'2026-09-01'` |
| `2026-09-01 00:00:00 Z` | already rejected: a datetime object | rejected, with a message that names it | `'2026-09-01T00:00:00Z'` |
| `0x1f`, `0b1010` | the numbers 31, 10 | rejected | `31`, `10` |
| `0o17` | the string `"0o17"` | rejected | `'0o17'` |
| `~` | null | rejected | `null` |
| an empty value | null | rejected | `null` |
| a document nesting more than 100 collections deep, counting the outermost | read, up to whatever the reader's stack allowed | rejected | flatten it; the deepest document this release ships nests 10 |
| any exponent form: `1e3`, `1E3`, `2E-2`, `-1.5e3` | the string | **the number** | `'1e3'` for the string; nothing for the number |

Every row above except the last is loud: the document is rejected and the
message names the value. The last row is the only silent one in this release.

**A form carrying an exponent needs one more look before you rewrite it.** The
1.1.6 reader used the YAML 1.1 float grammar, which required the exponent to
carry a sign, so within a single row the 1.1.6 value flips with that sign:
`1.e3` was the string `"1.e3"` but `1.e+3` was the number `1000.0`; `.5e3` was a
string but `.5e+3` was `500.0`; `+1.5e3` was a string but `+1.5e+3` was `1500.0`;
`017e3` was a string but `017.5e+3` was `17500.0`. Quote the unsigned spellings
and write the plain number for the signed ones. Getting this backwards is the
one way to follow this table and still move a hash.

A raw U+0085, U+2028 or U+2029 is also rejected, because YAML 1.1 counts them as
line breaks and YAML 1.2 does not. Write them as an escape in a double-quoted
scalar; section 14.3b already escapes two of them, so they remain ordinary
governed content.

Explicit tags such as `!!str` and `!!int` are rejected, as is the merge key
`<<`. Anchors and aliases keep working: an alias expands to the same node in
every YAML version, so it was never ambiguous.

Nothing here applies to JSON. If you author in JSON, 2.0 reads your documents
exactly as 1.1.6 did.

### What to do

Two checks, because the release has two kinds of change and one command cannot
report both.

**1. The rejected forms, which are loud.** Validate every governed YAML document
you have, not only manifests: a Build Plan carries no self-hash and its target is
hashed verbatim into `governedResultHash`, so an ambiguous scalar there moves a
hash with nothing to compare against.

```bash
python -m obds_ref.cli validate path/to/manifest.yaml
```

If it validates, no rejected form is present. If it reports an ambiguous plain
scalar, the message names the value and the reason, and the table above says what
to write instead.

**2. Exponent notation, which is silent.** Nothing rejects it, so list the
occurrences and read them:

```bash
grep -rnE '(^|[^0-9A-Za-z_.+-])[-+]?[0-9]+(\.[0-9]*)?[eE][-+]?[0-9]+([^0-9A-Za-z_.]|$)' \
  path/to/governed/*.yaml
```

Every scalar in exponent notation is on a line it prints. The reverse does not
hold: it also prints lines where the pattern appears inside a comment
(`# see 1e3 below`), a URL or another string, because it matches text and not
YAML structure. Read the lines; under 2.0 every unquoted occurrence is a
number. If you meant the number, nothing changes. If you meant
the string, quote it. The pattern deliberately over-reports: it prints quoted
occurrences too, and the two spellings that were already numbers (`1.0e+3`,
`1.23e-4`). It matches the value wherever it sits on the line, so flow mappings
(`{a: 1e3}`), inline sequences (`[1e3, 2e4]`) and a scalar with a trailing
comment are all reported. A list you read is safer than a verdict you trust.

Only after both checks pass does the promise at the top of this section hold.

If a value did change, quote it or rewrite it, then recompute
`approval.contentHash` and re-approve the manifest, exactly as for any other
change to governed content. A changed hash means the governed truth changed, and
that is a decision a named human makes, not a migration script.

The reference implementation ships 29 governed YAML documents. All 29 were
checked against both readings, and against the exponent pattern above, and none
of them changed.

## 1.1.5 to 1.1.6

No schema changed and no manifest change is required. Four observable changes,
each of which only affects a manifest that was already outside the
specification.

**One new normative rule, section 14.3c.** A governed string or object key must
consist only of code points assigned in Unicode 15.1.0, or Unicode
noncharacters, and must contain no surrogate. A document containing a code point
assigned only in a later Unicode version is now rejected. An implementation must
also run on a Unicode database at or after 15.1.0: the reference compiler needs
CPython 3.13 and the JavaScript oracle needs Node 21. This is what makes NFC, and therefore every
hash derived from it, identical on every conforming runtime. If you author in a
script added after Unicode 15.1.0, you cannot govern it under OBDS 1.1.6; raise
the pinned version in a later MINOR release rather than diverging locally. The
pinned assignment set ships with the release as
`reference/foundation/src/obds_ref/unicode-pin-15.1.0.json`.

**Element ids and semantic subjects are compared after NFC, section 8.0a.** Two
canonically equivalent ids are now one identity, so a manifest carrying both is
rejected as a duplicate, and an NFD and an NFC spelling of one subject now
resolve as one subject. `approval.contentHash` is unaffected, because it was
always computed over canonical, and therefore NFC, bytes. If your manifest ids
come from macOS paths or a DAM export they may be NFD; nothing needs to change,
because the comparison is now on the canonical form either way.

**`elementValueRef` resolves through the governed selection, section 11.5.** A
check that binds another element's value now fails the target when that element
has expired, is out of scope, lost its subject, sits in an unresolved conflict
or is not `defined`, with the section 13.1a cause that applies. Previously the
reference resolved it against the raw approved snapshot on `state` alone, so a
withdrawn value could still be compiled into an active blocking check. A target
that relied on that behaviour was enforcing text that was not governed truth.

**Conflict relevance counts more RULES, section 10.2a.** An unresolved subject
conflict is decision-relevant when one of the competing elements is a `defined`
RULE that would add requirements or compiled checks if it won, and when the
conflicted element is named in the target's `contextAssembly.eligibleGuidanceIds`,
not only when its enforcement is `block` or `require_approval`. A build that
succeeded while two competing non-blocking RULES cancelled a declared
dependency now fails, which is the outcome the same manifest already produced
once the conflict was resolved.

## 1.1.4 to 1.1.5

No schema changed and no manifest change is required.

The one observable change is that a manifest whose applicable RULE declares a
`requiresDefinedRefs` dependency that does not resolve to `defined` now fails
manifest validation or the target build. Such a manifest was already outside the
specification; the reference compiler simply did not say so.

## 1.1.3 to 1.1.4

No migration work. No schema changed and no manifest change is required.

Scope values are now normalised to Unicode NFC when they are compared, as section
9 already required. A consumer can observe a behavioural change only if a manifest
currently relies on an NFD scope value failing to match its canonically equivalent
NFC value. That manifest was already broken; 1.1.4 makes the required comparison.

## 1.0.4 to 1.1

No migration work for a manifest. Manifests stay at `schemaVersion: 1.0.0` and no
element contract changed.

For an implementation, four things:

1. **Emit `governedResultHash`** per section 14.3a and declare
   `schemaVersion: 1.1.0` on the Compiled Brand Context. Validate it against
   `schemas/1.1.0/compiled-context.schema.json`. The 1.0.0 contract is unchanged
   and 1.0 artefacts remain valid 1.0 artefacts.
2. **Check your precedence reading.** Section 10.2 now states the rule as strict
   subset inclusion on matched targets. If you read the old wording as
   "restricts more dimensions" you resolved some manifests as hard conflicts that
   1.1 resolves to a winner. Run `precedence-vectors` before assuming you agree.
3. **Check that required truth reaches your artefact.** If your context
   selection could drop an element named in `requiresDefined`, it was producing
   an incomplete context. Section 13.2 now says so explicitly.
4. **Adopt the four required-truth error codes** from section 13.1a if you
   report build failures.

`artifactHash` for an unchanged manifest and plan will move, because the artefact
gained a field and a schema version. That is expected across a version change.
Section 16.1 approvals bind the artefacts they were issued against; a rebuild
under 1.1 is a new artefact and needs its own approval.

## 1.0.3 to 1.0.4

No migration work. 1.0.4 changes release metadata and documentation only.

- No schema, no `$id`, no `schemaVersion` and no capability semantic changed.
- The public schema surface is byte-identical to 1.0.0, 1.0.1, 1.0.2 and 1.0.3.
- An implementation that conforms to 1.0.3 conforms to 1.0.4 with no work.

Two things are worth knowing.

First, if you publish your own conformance result, section 26 requires it to
identify the implementation by name and version, the suite by hash, the profile
and the counts, and to state that no required case was skipped or changed.
`OBDS-1.0.4-TEST-RESULT.json` is now a worked example of a result that meets
that rule, and `release-schemas/release-test-result.schema.json` is the shape
to validate against. A 1.0.3-era result that carried only counts should be
reissued.

Second, if your documentation repeated the OBDS pitch, check the same thing
1.0.4 corrected. `requiresDefined`, a failed target producing no Compiled Brand
Context and therefore no model call is Compiled Runtime, section 26.2. It is
not Foundation-only behaviour. Foundation, section 26.1, governs Brand Truth.
Nothing about the guarantee changed; only its label.

## 1.0.2 to 1.0.3

No migration work. 1.0.3 changes documentation, packaging and developer
experience only.

- No schema, no `$id`, no `schemaVersion` and no capability semantic changed.
- The public schema surface is byte-identical to 1.0.0, 1.0.1 and 1.0.2.
- An implementation that conforms to 1.0.2 conforms to 1.0.3 with no work.
- The only thing an implementer has to know is that documented commands have
  changed: `obds build` takes the manifest and the plan positionally, and the
  release gate now runs cleanly after the conformance suite.

## 1.0.1 to 1.0.2

No migration work. 1.0.2 changes licensing, packaging and documentation only.

- No manifest changes. No schema changes. No `$id` changes.
- `schemaVersion` stays `1.0.0`.
- An implementation that conforms to 1.0.1 conforms to 1.0.2 with no work.
- What changed for you is permission, not code: the specification and the
  documentation are now CC BY 4.0, and the schemas, the reference implementation,
  the conformance suite and the examples are now Apache License 2.0. Commercial
  implementation needs no separate permission.
- If you were waiting on a commercial licence before shipping, you are not
  waiting on anything any more.
