openvibers/openvibe.contracts Commits

Compat gate: recorded corrections (compatibility/corrections.json), accepted against their one base tag only; the 0.48.0 correction of network.grant.changed (the 0.47.0 shape nothing produced or consumed) is the first

OP OpenVibers committed 25 Sept 2026 · 14102333f1 · 3 files, +15 −1


Changes

ddiff --git a/CHANGELOG.md b/CHANGELOG.md
iindex 9e6531d9..ae085726 100644
---- a/CHANGELOG.md
++++ b/CHANGELOG.md
@@@ -6,7 +6,7 @@ the notes were in the tag and commit messages (`git tag -n1`).
## 0.48.0 — 2026-09-25
--**Correction to 0.47.0.** `network.grant.changed` was already emitted by Network's developer event relay for a developer app's grant, and OpenVibe.Events acts on it (it stops an app's subscriptions when its `events.app.subscribe` grant leaves approved). 0.47.0 gave that event a service-principal payload that does not match. Now `network.grant.changed` describes what is actually sent: `{ project_id, capability, audience, from, to }`, subject `app`. A first-party service principal's grant change is the new **`network.principal_grant.changed`** (the 0.47.0 payload under its own name). Nothing consumed the 0.47.0 shape.
++**Correction to 0.47.0.** `network.grant.changed` was already emitted by Network's developer event relay for a developer app's grant, and OpenVibe.Events acts on it (it stops an app's subscriptions when its `events.app.subscribe` grant leaves approved). 0.47.0 gave that event a service-principal payload that does not match. Now `network.grant.changed` describes what is actually sent: `{ project_id, capability, audience, from, to }`, subject `app`. A first-party service principal's grant change is the new **`network.principal_grant.changed`** (the 0.47.0 payload under its own name). Nothing consumed the 0.47.0 shape. The compat gate records this in `compatibility/corrections.json` (accepted against v0.47.0 only).
## 0.47.0 — 2026-09-25
ddiff --git a/compatibility/corrections.json b/compatibility/corrections.json
nnew file mode 100644
iindex 00000000..5ae2380c
---- /dev/null
++++ b/compatibility/corrections.json
@@@ -0,0 +1,11 @@
++{
++ "_about": "Recorded corrections: a contract released with a schema that nothing ever produced or consumed, fixed in the next release to describe what is actually sent. { id, base (the release tag the correction is compared with), fixedIn, reason }. The compat gate accepts the listed contract's changes against that one base only; every other comparison, and every later release, is checked as usual. Never for a contract anyone implemented.",
++ "corrections": [
++ {
++ "id": "network.grant.changed",
++ "base": "v0.47.0",
++ "fixedIn": "0.48.0",
++ "reason": "0.47.0 gave network.grant.changed a service-principal payload, but Network's developer relay already sent it for developer-app grants ({ project_id, capability, audience, from, to }) and OpenVibe.Events acts on that shape. Nothing produced or consumed the 0.47.0 shape; it moved to network.principal_grant.changed."
++ }
++ ]
++}
ddiff --git a/scripts/compat.js b/scripts/compat.js
iindex bdcdf93e..05e96a9a 100644
---- a/scripts/compat.js
++++ b/scripts/compat.js
@@@ -43,10 +43,13 @@ function compare(id, a, b, at) {
const oldCat = readAt(base, 'contracts/catalog.json');
const newCat = JSON.parse(fs.readFileSync(path.join(ROOT, 'contracts/catalog.json'), 'utf8'));
const deprecated = new Set(JSON.parse(fs.readFileSync(path.join(ROOT, 'compatibility/deprecations.json'), 'utf8')).deprecations.map(d => d.id));
++// A recorded correction (compatibility/corrections.json) is accepted against its one base tag only.
++const corrected = new Set(JSON.parse(fs.readFileSync(path.join(ROOT, 'compatibility/corrections.json'), 'utf8')).corrections.filter(c => c.base === base).map(c => c.id));
for (const entry of (oldCat ? oldCat.contracts : [])) {
const now = newCat.contracts.find(c => c.id === entry.id);
if (!now) { if (!deprecated.has(entry.id)) problems.push(`${entry.id}: removed from the catalog without a deprecation`); continue; }
if (now.schema !== entry.schema) continue; // new major file; old one must still be listed separately
++ if (corrected.has(entry.id)) { console.log(`compat vs ${base}: ${entry.id} is a recorded correction (compatibility/corrections.json)`); continue; }
compare(entry.id, readAt(base, `contracts/${entry.schema}`), JSON.parse(fs.readFileSync(path.join(ROOT, 'contracts', entry.schema), 'utf8')), '');
}
if (problems.length) { console.error(`compat vs ${base}: ${problems.length} breaking change(s)\n ${problems.join('\n ')}`); process.exit(1); }