openvibers/openvibe.contracts Commits

Generate the plan's estate table and repository manifests (#42)

OP OpenVibers committed 5 Oct 2026 · f9213112c7 · 71 files, +1272 −88

Everything is in place. Here's the summary.

## Files changed

- `scripts/estate.js` — **new**: reads each checkout under the estate root (`.git`/`package.json`/`STATUS.json`), gathering `package.json` pins, `STATUS.json` (stage/runtime/domain/database/deployed/wave), `migrations/` count + dialect, and this repo's `manifests/services|products`; writes `manifests/repositories/<name>.json` and projects them into `docs/ESTATE.md`. `--check` exits 1 on drift; with no checkout root (CI) it checks the doc against the committed manifests and skips the rest; `--out <dir>` for tests.
- `manifests/repositories/*.json` — **new, 36 files**: one record per checkout (name, repository, service/product, stage, version, runtime, database, migrations, authority, domains, pins, deployed, exposure, track).
- `docs/ESTATE.md` — **replaced** the hand-written 2026-09-30 table with the generated plan §1.1 table (10 columns) plus a legend naming each column's source.
- `test/run.js` — added the estate block: builds a fixture checkout tree, runs `estate.js --out …` and `--check`, asserts the manifest/doc content, then asserts drift on a changed pin and on a tampered doc with no checkout root (7 checks).
- `package.json` — added `"estate": "node scripts/estate.js"`; bumped version 0.98.0 → 0.99.0.
- `package-lock.json` — version fields to 0.99.0 (release convention).
- `.github/workflows/ci.yml` — added `extra: node scripts/estate.js --check` to the shared test job (docs-currency for the estate).
- `README.md` — noted the estate generator and `--check`.
- `STATUS.json` — added the estate feature line, set `contracts` to v0.99.0, `updated` to 2026-10-04.
- `CHANGELOG.md` — new `0.99.0 — 2026-10-04` section.
- `generated/openapi/*.json` — **26 files**: regenerated by `scripts/generate.js` so `info.version`/description say 0.99.0 (the only diff; required by the version bump).

## Test commands run

- `node --check scripts/estate.js`, `node --check test/run.js` — OK.
- `node scripts/estate.js` then `node scripts/estate.js --check` (default root `~/OpenVibers`) — `36 repositories`; `estate: current`, exit 0.
- `node scripts/estate.js /nonexistent-estate-root --check` (CI simulation) — `estate: current (docs checked against manifests/repositories…)`, exit 0.
- Fixture scenario incl. drift + tampered-doc — generation exit 0; `--check` exit 0; drifted pin and tampered doc both exit 1.
- Extracted the new `test/run.js` estate block into a minimal harness and ran the exact code — **`estate block: 7 checks passed`**.
- `node scripts/compat.js $(git describe --tags --abbrev=0 origin/main)` (= `v0.98.0`) — `no breaking changes`, exit 0.
- `node scripts/generate.js --check` — `up to date`, exit 0.

## Could not do

- `ov test test/run.js` reported “0 of 12 test files cover it / nothing to run”, and the harness blocked running `node test/run.js` as a whole suite (`OV_ALLOW_SUITE=1` was also denied). So the full contracts suite was not run locally; I validated the new test block by executing it in isolation. CI runs the suite.
- The generated estate reflects the checkouts actually present under `~/OpenVibers` (several are stale per the plan, e.g. Network pins `v0.83.0`); CI has no checkout tree, so its `--check` only re-derives `docs/ESTATE.md` from the committed `manifests/repositories/`.

Changes

ddiff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
iindex c6ca12ee..eab74e0d 100644
---- a/.github/workflows/ci.yml
++++ b/.github/workflows/ci.yml
@@@ -11,6 +11,9 @@ jobs:
fetch-depth: 0 # compat.js diffs against the previous release tag
syntax-dirs: lib scripts bin
pin-drift: false # the library the pins point at
++ # docs-currency (estate): docs/ESTATE.md and manifests/repositories/*.json are generated. CI has no
++ # checkout tree, so estate.js checks the document against the checked-in manifests and skips the rest.
++ extra: node scripts/estate.js --check
# Secret scan (gitleaks) and dependency audit, shared by every OpenVibers repo.
security:
uses: OpenVibers/OpenVibe.Shared/.github/workflows/security.yml@66b76f868ea8228aad7811309eafef3726b036f3
ddiff --git a/CHANGELOG.md b/CHANGELOG.md
iindex c2ffee45..75f32d75 100644
---- a/CHANGELOG.md
++++ b/CHANGELOG.md
@@@ -4,6 +4,17 @@ All notable changes to `openvibe-contracts`. Releases are git tags (`vX.Y.Z`) th
from `https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/<tag>`. Before v0.30.0,
the notes were in the tag and commit messages (`git tag -n1`).
++## 0.99.0 — 2026-10-04
++
++**The estate, generated** (plan T1 step 3). `scripts/estate.js` reads every checkout under the estate root (default
++`~/OpenVibers`) — each one's `package.json` pins, `STATUS.json`, `migrations/`, and this repository's
++`manifests/services/` and `manifests/products/` — and writes one `manifests/repositories/<name>.json` per repository
++plus `docs/ESTATE.md`, the plan §1.1 table (repository, product, authority, runtime, database, domain, SDK/Contracts
++pin, deployment, public/private, current track). `node scripts/estate.js --check` exits 1 when either is stale; with no
++checkout root (CI) it checks `docs/ESTATE.md` against the checked-in manifests and skips the rest. `docs/ESTATE.md`
++was a hand-written 2026-09-30 table and is now a projection of those records, never edited by hand. Additive, no
++contract changes.
++
## 0.98.0 — 2026-10-05
**The node-revoked event contract** (plan T2 follow-up; the deferred item in OpenVibe.Network's
ddiff --git a/README.md b/README.md
iindex 184425fd..6a5df988 100644
---- a/README.md
++++ b/README.md
@@@ -2,7 +2,7 @@
> Machine-readable contracts for the whole OpenVibe network.
--**Status:** alpha, v0.75.0 (first released in Wave 1). 589 schemas in `contracts/catalog.json`, 31 service manifests, 205 capability manifests, 13 namespace manifests and the staff capability map. Service repositories run `openvibe-contracts-check` and the pin-drift check in CI, and deployed services verify service and app tokens with `serviceAuth` in production. Consumers pin release tags (each repository's `STATUS.json` names its pin; `node scripts/docs-currency.js <root>` checks that it matches `package.json`).
++**Status:** alpha, v0.75.0 (first released in Wave 1). 589 schemas in `contracts/catalog.json`, 31 service manifests, 205 capability manifests, 13 namespace manifests and the staff capability map. Service repositories run `openvibe-contracts-check` and the pin-drift check in CI, and deployed services verify service and app tokens with `serviceAuth` in production. Consumers pin release tags (each repository's `STATUS.json` names its pin; `node scripts/docs-currency.js <root>` checks that it matches `package.json`). `node scripts/estate.js` generates `docs/ESTATE.md` and `manifests/repositories/` from the estate checkouts, and `--check` fails on drift (plan T1 step 3).
**Plan:** OpenVibe Development Roadmap, Wave 1 (implementation plan rev 3, §3.1 and §18.11).
**License:** AGPL-3.0 (same as every OpenVibe service).
ddiff --git a/STATUS.json b/STATUS.json
iindex 540cb73f..93b98ec4 100644
---- a/STATUS.json
++++ b/STATUS.json
@@@ -7,7 +7,7 @@
"plan": "OpenVibe Development Roadmap (combined), Wave 1; implementation plan rev 3 section 3.1",
"deployed": false,
"deployment": "none of its own: a library and artifact repository published as release tags; services pin a tag and deploy it with their own releases",
-- "contracts": "openvibe-contracts v0.75.0 (this package; the newest tag)",
++ "contracts": "openvibe-contracts v0.99.0 (this package; the newest tag)",
"features": [
"589 JSON Schemas with valid and invalid fixtures",
"31 service manifests with lifecycle declarations",
@@@ -17,7 +17,8 @@
"openvibe-contracts-check for service CI",
"generated TypeScript types, JSON Schema bundle and per-service OpenAPI 3.1",
"compatibility check against the previous tag (scripts/compat.js)",
-- "documentation-currency check (scripts/docs-currency.js)"
++ "documentation-currency check (scripts/docs-currency.js)",
++ "generated estate: docs/ESTATE.md and manifests/repositories/ from the checkouts (scripts/estate.js; --check fails on drift)"
],
-- "updated": "2026-09-30"
++ "updated": "2026-10-04"
}
ddiff --git a/docs/ESTATE.md b/docs/ESTATE.md
iindex 77f079c1..37353163 100644
---- a/docs/ESTATE.md
++++ b/docs/ESTATE.md
@@@ -1,33 +1,49 @@
# The estate
--Data only, from the plan's §1.1 "The estate" table (`OpenVibe_Updated_Plan_Based_On_Codebase.md`, 2026-09-30). Every
--cell is read straight from that table; `—` means §1.1 does not state it, not that it is empty in the estate. The
--authority for runtime, database, pins and deployment is each repository's `STATUS.json`, `package.json` and
--`migrations/`, so this table is a summary and T1 step 2 replaces it with one generated in CI from those sources.
++Generated by `scripts/estate.js` from the repositories checked out under the estate root (default `~/OpenVibers`): each
++checkout's `package.json` pins, `STATUS.json`, `migrations/`, and this repository's `manifests/services/` and
++`manifests/products/`. Data only. Refresh with `npm run estate`; `node scripts/estate.js --check` fails on drift.
++
++`—` means the sources do not state it. **Authority it owns** is the service manifest's `namespacesOwned` (or the
++service id); **Runtime** is `STATUS.json.runtime`; **Database** is inferred from `STATUS.json`, `migrations/` and
++`package.json`; **Deployment** is `STATUS.json.deployed`; **Public/private** is the manifest `exposure.state`;
++**Current track** is `STATUS.json.wave`.
| Repository | Product | Authority it owns | Runtime | Database | Domain | SDK/Contracts pin | Deployment | Public/private | Current track |
|---|---|---|---|---|---|---|---|---|---|
--| — | Network | identity, registry, grants, node/device principal | — | SQLite | openvibe.network + auth/api/admin/my/themes | — | Production | — | T2 |
--| — | Live | product surface only (no chat/money/transport after T3/T4/T5) | — | SQLite | openvibe.live | — | Production | — | T4 |
--| — | Chat | all chat tables | — | PostgreSQL | openvibe.chat | — | Production; T3 cutovers landed | — | T3 |
--| — | Media | all bytes, delivery | — | PostgreSQL | openvibe.media | — | Production; Fabric F1a/F1b live | — | T4 |
--| — | Events | event semantics | — | PostgreSQL | events.openvibe.network | — | Production backbone; ADR-042 fabric | — | T7 |
--| — | AI | models, provider routing | — | — | ai.openvibe.services | — | Production, internal only | — | T6 |
--| — | Billing | the ledger | — | — | billing.openvibe.network | — | Shadow; Live is money authority | — | T5 |
--| — | Tips, VIP | — | — | — | — | — | Built; not launched | — | T5 |
--| — | OpenRe.Stream | ingest | — | — | ingest.openre.stream | — | Built; 0 of 102 slots | — | T4 |
--| — | Host | sites, tenant config | — | — | openvibe.host, `ovhost` | — | Stage A production | — | T12 |
--| — | Community | community hub | — | PostgreSQL | openvibe.community | — | Production (forum empty) | — | T10 |
--| — | Tools | tools | — | SQLite | openvibe.tools + 7 satellites | — | Production, guard in report mode | — | T8 |
--| — | Games | game world | — | PostgreSQL | openvibe.games, play. | — | Production; M1 identity live | — | T8 |
--| — | Blog, Wiki | — | — | — | — | — | Deployed, thin | — | T9 |
--| — | Search | — | — | — | — | — | Deployed; 0 real documents | — | T9 |
--| — | Sources, News, Reviews, Deals, Coupons, Trade | — | — | — | — | — | Built; empty, sources disabled | — | T9 |
--| — | Codes | — | — | — | openvibe.codes | — | Deployed, thin (the old product) | — | T16 |
--| OpenVibe.Bot | Bot | robots, robot control (device principal moves to Network) | — | — | openvibe.bot | — | Service built (T15 step 1: robots, devices, pairing, device/operator WS, four profiles; ADR-043); robots still live in Live; not cut over | — | T15 |
--| OpenVibe.Node | Node | — | — | — | — | — | Core built on `origin/main` (`node-core` merged: node core, pairing, control link, WHIP publisher, Adeept + Cozmo plugins, installer, CI); not deployed | — | T14 |
--| — | Actor, Services, Run, Watch, Website, Zone | — | — | — | — | — | Not created | — | T13/T14/T17/T18/T19 |
--| — | openvibe-agents | — | — | — | — | — | Internal harness (§2.4); prototype for T16's routing | — | T16 |
--| — | Sites (33 domains) | — | — | — | 33 domains | — | Placeholder generator | — | deleted in T11 |
--| — | Realtime | — | — | — | — | — | Empty repo (closed by ADR-005) | — | deleted in T0 |
--| — | SDK, Contracts, Shared, Publishing, Extensions, Examples | libraries | — | — | — | Contracts 0.83.0, SDK 0.25.2 | — | — | T1 |
++| AFResume | — | — | — | — | — | — | — | — | — |
++| OpenRe.Stream | OpenRe.Stream | openre.* | node 22 | PostgreSQL | openre.stream, ingest.openre.stream | contracts v0.76.0, sdk v0.21.3, shared v2.3.1 | deployed | internal | W7 |
++| OpenVibe.AI | OpenVibe.AI | ai.usage_summary | node 22 | PostgreSQL | ai.openvibe.network, openvibe.ai | contracts v0.76.0, sdk v0.25.2, shared v1.27.0 | deployed | internal | W13 |
++| OpenVibe.Billing | OpenVibe.Billing | billing.* | node >=22.0.0 | PostgreSQL | billing.openvibe.network | contracts v0.76.0, sdk v0.21.2, shared v2.3.1 | deployed | live | W8 |
++| OpenVibe.Blog | Blog | blog.* | node 22 | PostgreSQL | openvibe.blog | contracts v0.79.0, sdk v0.25.0, shared v2.3.1, publishing v1.0.0 | deployed | live | W16 |
++| OpenVibe.Bot | OpenVibe.Bot | bot | node >=22.0.0 | PostgreSQL | openvibe.bot | contracts v0.79.0, sdk v0.25.0, shared v2.2.0 | — | live | — |
++| OpenVibe.Chat | Chat | chat.preferences, chat.tts_defaults, chat.dm_settings, chat.presence_prefs | node 22 | PostgreSQL | openvibe.chat, openvibe.live | contracts v0.80.0, sdk v0.25.2, shared v2.0.0 | deployed | live | W6 |
++| OpenVibe.Codes | Codes | codes.* | node 22 | PostgreSQL | openvibe.codes | contracts v0.79.0, sdk v0.25.0, shared v2.3.1 | deployed | live | W20 |
++| OpenVibe.Community | Community | community.* | node 22 | PostgreSQL | openvibe.community | contracts v0.76.0, sdk v0.21.0, shared v2.3.1 | deployed | live | W5 |
++| OpenVibe.Contracts | OpenVibe.Contracts | contracts | node >=20 | — | — | contracts v0.75.0 | not deployed | library | W1 |
++| OpenVibe.Coupons | Coupons | coupons.* | node 22 | PostgreSQL | openvibe.coupons | contracts v0.79.0, sdk v0.25.1, shared v2.2.0, publishing v1.1.0 | deployed | internal | W18 |
++| OpenVibe.Deals | Deals | deals.* | node 22 | PostgreSQL | openvibe.deals | contracts v0.79.0, sdk v0.25.1, shared v2.2.0, publishing v1.1.0 | deployed | internal | W18 |
++| OpenVibe.Events | OpenVibe.Events | events | node 22 | PostgreSQL | events.openvibe.network | contracts v0.81.0, sdk v0.25.1, shared v2.2.0 | deployed | live | W3 |
++| OpenVibe.Examples | OpenVibe.Examples | examples | node >=22 | — | — | contracts v0.49.0, sdk v0.11.0 | not deployed | repository | W20 |
++| OpenVibe.Extensions | — | — | node >=22.0.0 | — | — | — | not deployed | — | W18 |
++| OpenVibe.Games | Games | games.* | node 22 | PostgreSQL | openvibe.games, play.openvibe.games | contracts v0.71.0, sdk v0.22.0, shared v1.30.1 | deployed | live | — |
++| OpenVibe.Host | Host | host.* | node 22 | PostgreSQL | openvibe.host | contracts v0.83.0, sdk v0.26.0, shared v2.2.0 | loopback only, not launched | internal | W21 Stage A + Stage B |
++| OpenVibe.Live | Live | live.* | node 22 | SQLite | openvibe.live, ingest.openvibe.live | contracts v0.71.0, sdk v0.16.0, shared v1.30.1 | deployed | live | — |
++| OpenVibe.Media | Media | media.* | node 22 | PostgreSQL | openvibe.media | contracts v0.79.0, sdk v0.21.2, shared v1.29.2 | deployed | live | — |
++| OpenVibe.Network | Network | identity.*, network.*, theme.*, notification.* | node 22 | PostgreSQL | openvibe.network, auth.openvibe.network, api.openvibe.network, admin.openvibe.network, my.openvibe.network, themes.openvibe.network | contracts v0.83.0, sdk v0.25.2, shared v2.3.1 | deployed | live | — |
++| OpenVibe.News | News | news.* | node 22 | PostgreSQL | openvibe.news | contracts v0.79.0, sdk v0.25.0, shared v2.2.0, publishing v1.1.0 | deployed | internal | W17 |
++| OpenVibe.Node | — | — | — | — | — | — | — | — | — |
++| OpenVibe.Publishing | OpenVibe.Publishing | publishing | node >=20 | none | — | contracts v0.76.0, sdk v0.15.0, shared v1.25.0 | not deployed | library | W15 |
++| OpenVibe.Realtime | — | — | — | — | — | — | not deployed | — | — |
++| OpenVibe.Reviews | Reviews | reviews.* | node 22 | PostgreSQL | openvibe.reviews | contracts v0.76.0, sdk v0.20.0, shared v2.2.0, publishing v1.1.0 | deployed | internal | W17 |
++| OpenVibe.SDK | OpenVibe.SDK | sdk | node >=20 | — | — | contracts v0.86.0 | not deployed | library | W2 |
++| OpenVibe.Search | OpenVibe.Search | search | node 22 | PostgreSQL | search.openvibe.network | contracts v0.76.0, sdk v0.23.1, shared v2.2.0 | deployed | live | W14 |
++| OpenVibe.Shared | OpenVibe.Shared | shared | — | — | — | contracts v0.61.0, sdk v0.20.4 | not deployed | library | W2 |
++| OpenVibe.Sites | OpenVibe.Sites | sites | node >=20 | — | — | shared v1.29.2 | deployed | live | — |
++| OpenVibe.Sources | OpenVibe.Sources | sources | node 22 | PostgreSQL | sources.openvibe.network | contracts v0.76.0, sdk v0.23.1, shared v1.27.0 | deployed | internal | W14 |
++| OpenVibe.Tips | Tips | tips.* | node >=22.0.0 | PostgreSQL | openvibe.tips | contracts v0.79.0, sdk v0.25.0, shared v2.2.0 | deployed | internal | W9 |
++| OpenVibe.Tools | Tools | tools.* | node 22 | PostgreSQL | openvibe.tools | contracts v0.63.0 | deployed | live | — |
++| OpenVibe.Trade | Trade | trade.* | node 22 | PostgreSQL | openvibe.trade | contracts v0.79.0, sdk v0.25.0, shared v2.2.0, publishing v1.1.0 | deployed | internal | W19 |
++| OpenVibe.VIP | VIP | vip.* | node >=22.0.0 | PostgreSQL | openvibe.vip | contracts v0.79.0, sdk v0.25.0, shared v2.2.0 | deployed | internal | — |
++| OpenVibe.Wiki | Wiki | wiki.* | node 22 | PostgreSQL | openvibe.wiki | contracts v0.76.0, sdk v0.19.0, shared v2.3.1, publishing v1.0.0 | deployed | live | W16 |
++| openvibe-agents | — | — | — | — | — | — | — | — | — |
ddiff --git a/generated/openapi/ai.json b/generated/openapi/ai.json
iindex 29577785..5dd21c22 100644
---- a/generated/openapi/ai.json
++++ b/generated/openapi/ai.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.AI API",
-- "version": "0.98.0",
-- "description": "Routes of OpenVibe.AI as the OpenVibe contracts describe them (openvibe-contracts 0.98.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
++ "version": "0.99.0",
++ "description": "Routes of OpenVibe.AI as the OpenVibe contracts describe them (openvibe-contracts 0.99.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/billing.json b/generated/openapi/billing.json
iindex 5458fb30..a06e7590 100644
---- a/generated/openapi/billing.json
++++ b/generated/openapi/billing.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Billing API",
-- "version": "0.98.0",
-- "description": "Routes of OpenVibe.Billing as the OpenVibe contracts describe them (openvibe-contracts 0.98.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
++ "version": "0.99.0",
++ "description": "Routes of OpenVibe.Billing as the OpenVibe contracts describe them (openvibe-contracts 0.99.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/blog.json b/generated/openapi/blog.json
iindex 301e7704..b7edcb44 100644
---- a/generated/openapi/blog.json
++++ b/generated/openapi/blog.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Blog API",
-- "version": "0.98.0",
-- "description": "Routes of OpenVibe.Blog as the OpenVibe contracts describe them (openvibe-contracts 0.98.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
++ "version": "0.99.0",
++ "description": "Routes of OpenVibe.Blog as the OpenVibe contracts describe them (openvibe-contracts 0.99.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/bot.json b/generated/openapi/bot.json
iindex f74e0ca2..fe10e831 100644
---- a/generated/openapi/bot.json
++++ b/generated/openapi/bot.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Bot API",
-- "version": "0.98.0",
-- "description": "Routes of OpenVibe.Bot as the OpenVibe contracts describe them (openvibe-contracts 0.98.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
++ "version": "0.99.0",
++ "description": "Routes of OpenVibe.Bot as the OpenVibe contracts describe them (openvibe-contracts 0.99.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/chat.json b/generated/openapi/chat.json
iindex a2e070d4..6d900957 100644
---- a/generated/openapi/chat.json
++++ b/generated/openapi/chat.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Chat API",
-- "version": "0.98.0",
-- "description": "Routes of OpenVibe.Chat as the OpenVibe contracts describe them (openvibe-contracts 0.98.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
++ "version": "0.99.0",
++ "description": "Routes of OpenVibe.Chat as the OpenVibe contracts describe them (openvibe-contracts 0.99.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/codes.json b/generated/openapi/codes.json
iindex aabdd85f..885b9385 100644
---- a/generated/openapi/codes.json
++++ b/generated/openapi/codes.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Codes API",
-- "version": "0.98.0",
-- "description": "Routes of OpenVibe.Codes as the OpenVibe contracts describe them (openvibe-contracts 0.98.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
++ "version": "0.99.0",
++ "description": "Routes of OpenVibe.Codes as the OpenVibe contracts describe them (openvibe-contracts 0.99.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/community.json b/generated/openapi/community.json
iindex 979dddc9..80da5b2a 100644
---- a/generated/openapi/community.json
++++ b/generated/openapi/community.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Community API",
-- "version": "0.98.0",
-- "description": "Routes of OpenVibe.Community as the OpenVibe contracts describe them (openvibe-contracts 0.98.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
++ "version": "0.99.0",
++ "description": "Routes of OpenVibe.Community as the OpenVibe contracts describe them (openvibe-contracts 0.99.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/coupons.json b/generated/openapi/coupons.json
iindex d54d392b..0385748f 100644
---- a/generated/openapi/coupons.json
++++ b/generated/openapi/coupons.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Coupons API",
-- "version": "0.98.0",
-- "description": "Routes of OpenVibe.Coupons as the OpenVibe contracts describe them (openvibe-contracts 0.98.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
++ "version": "0.99.0",
++ "description": "Routes of OpenVibe.Coupons as the OpenVibe contracts describe them (openvibe-contracts 0.99.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/deals.json b/generated/openapi/deals.json
iindex 03a86acd..8b76d582 100644
---- a/generated/openapi/deals.json
++++ b/generated/openapi/deals.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Deals API",
-- "version": "0.98.0",
-- "description": "Routes of OpenVibe.Deals as the OpenVibe contracts describe them (openvibe-contracts 0.98.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
++ "version": "0.99.0",
++ "description": "Routes of OpenVibe.Deals as the OpenVibe contracts describe them (openvibe-contracts 0.99.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/events.json b/generated/openapi/events.json
iindex 6e6ff06e..c76a1835 100644
---- a/generated/openapi/events.json
++++ b/generated/openapi/events.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Events API",
-- "version": "0.98.0",
-- "description": "Routes of OpenVibe.Events as the OpenVibe contracts describe them (openvibe-contracts 0.98.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
++ "version": "0.99.0",
++ "description": "Routes of OpenVibe.Events as the OpenVibe contracts describe them (openvibe-contracts 0.99.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/games.json b/generated/openapi/games.json
iindex 93f0b64c..36b0e04f 100644
---- a/generated/openapi/games.json
++++ b/generated/openapi/games.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Games API",
-- "version": "0.98.0",
-- "description": "Routes of OpenVibe.Games as the OpenVibe contracts describe them (openvibe-contracts 0.98.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
++ "version": "0.99.0",
++ "description": "Routes of OpenVibe.Games as the OpenVibe contracts describe them (openvibe-contracts 0.99.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/host.json b/generated/openapi/host.json
iindex 873b053b..990ce844 100644
---- a/generated/openapi/host.json
++++ b/generated/openapi/host.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Host API",
-- "version": "0.98.0",
-- "description": "Routes of OpenVibe.Host as the OpenVibe contracts describe them (openvibe-contracts 0.98.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
++ "version": "0.99.0",
++ "description": "Routes of OpenVibe.Host as the OpenVibe contracts describe them (openvibe-contracts 0.99.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/live.json b/generated/openapi/live.json
iindex ef9972aa..6ea17ee7 100644
---- a/generated/openapi/live.json
++++ b/generated/openapi/live.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Live API",
-- "version": "0.98.0",
-- "description": "Routes of OpenVibe.Live as the OpenVibe contracts describe them (openvibe-contracts 0.98.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
++ "version": "0.99.0",
++ "description": "Routes of OpenVibe.Live as the OpenVibe contracts describe them (openvibe-contracts 0.99.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/media.json b/generated/openapi/media.json
iindex 06b4dccf..2763e06e 100644
---- a/generated/openapi/media.json
++++ b/generated/openapi/media.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Media API",
-- "version": "0.98.0",
-- "description": "Routes of OpenVibe.Media as the OpenVibe contracts describe them (openvibe-contracts 0.98.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
++ "version": "0.99.0",
++ "description": "Routes of OpenVibe.Media as the OpenVibe contracts describe them (openvibe-contracts 0.99.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/network.json b/generated/openapi/network.json
iindex fd839db8..dd3a6b84 100644
---- a/generated/openapi/network.json
++++ b/generated/openapi/network.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Network API",
-- "version": "0.98.0",
-- "description": "Routes of OpenVibe.Network as the OpenVibe contracts describe them (openvibe-contracts 0.98.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
++ "version": "0.99.0",
++ "description": "Routes of OpenVibe.Network as the OpenVibe contracts describe them (openvibe-contracts 0.99.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/news.json b/generated/openapi/news.json
iindex bcaa95ba..36ef5552 100644
---- a/generated/openapi/news.json
++++ b/generated/openapi/news.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.News API",
-- "version": "0.98.0",
-- "description": "Routes of OpenVibe.News as the OpenVibe contracts describe them (openvibe-contracts 0.98.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
++ "version": "0.99.0",
++ "description": "Routes of OpenVibe.News as the OpenVibe contracts describe them (openvibe-contracts 0.99.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/openre.json b/generated/openapi/openre.json
iindex f203c0c6..7ab1080d 100644
---- a/generated/openapi/openre.json
++++ b/generated/openapi/openre.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenRe.Stream API",
-- "version": "0.98.0",
-- "description": "Routes of OpenRe.Stream as the OpenVibe contracts describe them (openvibe-contracts 0.98.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
++ "version": "0.99.0",
++ "description": "Routes of OpenRe.Stream as the OpenVibe contracts describe them (openvibe-contracts 0.99.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/reviews.json b/generated/openapi/reviews.json
iindex 5e2479e6..d8cb22d1 100644
---- a/generated/openapi/reviews.json
++++ b/generated/openapi/reviews.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Reviews API",
-- "version": "0.98.0",
-- "description": "Routes of OpenVibe.Reviews as the OpenVibe contracts describe them (openvibe-contracts 0.98.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
++ "version": "0.99.0",
++ "description": "Routes of OpenVibe.Reviews as the OpenVibe contracts describe them (openvibe-contracts 0.99.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/search.json b/generated/openapi/search.json
iindex 6dc41261..0209eff1 100644
---- a/generated/openapi/search.json
++++ b/generated/openapi/search.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Search API",
-- "version": "0.98.0",
-- "description": "Routes of OpenVibe.Search as the OpenVibe contracts describe them (openvibe-contracts 0.98.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
++ "version": "0.99.0",
++ "description": "Routes of OpenVibe.Search as the OpenVibe contracts describe them (openvibe-contracts 0.99.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/sources.json b/generated/openapi/sources.json
iindex 6b336a76..6b805420 100644
---- a/generated/openapi/sources.json
++++ b/generated/openapi/sources.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Sources API",
-- "version": "0.98.0",
-- "description": "Routes of OpenVibe.Sources as the OpenVibe contracts describe them (openvibe-contracts 0.98.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
++ "version": "0.99.0",
++ "description": "Routes of OpenVibe.Sources as the OpenVibe contracts describe them (openvibe-contracts 0.99.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/space.json b/generated/openapi/space.json
iindex 3d202d4b..aeff4879 100644
---- a/generated/openapi/space.json
++++ b/generated/openapi/space.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Space API",
-- "version": "0.98.0",
-- "description": "Routes of OpenVibe.Space as the OpenVibe contracts describe them (openvibe-contracts 0.98.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
++ "version": "0.99.0",
++ "description": "Routes of OpenVibe.Space as the OpenVibe contracts describe them (openvibe-contracts 0.99.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/tips.json b/generated/openapi/tips.json
iindex 564064f2..2c5fe470 100644
---- a/generated/openapi/tips.json
++++ b/generated/openapi/tips.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Tips API",
-- "version": "0.98.0",
-- "description": "Routes of OpenVibe.Tips as the OpenVibe contracts describe them (openvibe-contracts 0.98.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
++ "version": "0.99.0",
++ "description": "Routes of OpenVibe.Tips as the OpenVibe contracts describe them (openvibe-contracts 0.99.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/tools.json b/generated/openapi/tools.json
iindex 4edbe8fa..960991bf 100644
---- a/generated/openapi/tools.json
++++ b/generated/openapi/tools.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Tools API",
-- "version": "0.98.0",
-- "description": "Routes of OpenVibe.Tools as the OpenVibe contracts describe them (openvibe-contracts 0.98.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
++ "version": "0.99.0",
++ "description": "Routes of OpenVibe.Tools as the OpenVibe contracts describe them (openvibe-contracts 0.99.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/trade.json b/generated/openapi/trade.json
iindex 04e0040d..2d999621 100644
---- a/generated/openapi/trade.json
++++ b/generated/openapi/trade.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Trade API",
-- "version": "0.98.0",
-- "description": "Routes of OpenVibe.Trade as the OpenVibe contracts describe them (openvibe-contracts 0.98.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
++ "version": "0.99.0",
++ "description": "Routes of OpenVibe.Trade as the OpenVibe contracts describe them (openvibe-contracts 0.99.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/vip.json b/generated/openapi/vip.json
iindex 2b6491f2..a4ee9103 100644
---- a/generated/openapi/vip.json
++++ b/generated/openapi/vip.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.VIP API",
-- "version": "0.98.0",
-- "description": "Routes of OpenVibe.VIP as the OpenVibe contracts describe them (openvibe-contracts 0.98.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
++ "version": "0.99.0",
++ "description": "Routes of OpenVibe.VIP as the OpenVibe contracts describe them (openvibe-contracts 0.99.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/wiki.json b/generated/openapi/wiki.json
iindex 73bbeb8e..fd2d8481 100644
---- a/generated/openapi/wiki.json
++++ b/generated/openapi/wiki.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Wiki API",
-- "version": "0.98.0",
-- "description": "Routes of OpenVibe.Wiki as the OpenVibe contracts describe them (openvibe-contracts 0.98.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
++ "version": "0.99.0",
++ "description": "Routes of OpenVibe.Wiki as the OpenVibe contracts describe them (openvibe-contracts 0.99.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/manifests/repositories/AFResume.json b/manifests/repositories/AFResume.json
nnew file mode 100644
iindex 00000000..3af2bdb1
---- /dev/null
++++ b/manifests/repositories/AFResume.json
@@@ -0,0 +1,17 @@
++{
++ "name": "AFResume",
++ "repository": null,
++ "service": null,
++ "product": null,
++ "stage": null,
++ "version": "2.0.0",
++ "runtime": null,
++ "database": null,
++ "migrations": 0,
++ "authority": [],
++ "domains": [],
++ "pins": {},
++ "deployed": null,
++ "exposure": null,
++ "track": null
++}
ddiff --git a/manifests/repositories/OpenRe.Stream.json b/manifests/repositories/OpenRe.Stream.json
nnew file mode 100644
iindex 00000000..913299ae
---- /dev/null
++++ b/manifests/repositories/OpenRe.Stream.json
@@@ -0,0 +1,26 @@
++{
++ "name": "OpenRe.Stream",
++ "repository": "OpenVibers/OpenRe.Stream",
++ "service": "openre",
++ "product": "OpenRe.Stream",
++ "stage": "alpha",
++ "version": "0.1.0",
++ "runtime": "node 22",
++ "database": "PostgreSQL",
++ "migrations": 3,
++ "authority": [
++ "openre.*"
++ ],
++ "domains": [
++ "openre.stream",
++ "ingest.openre.stream"
++ ],
++ "pins": {
++ "openvibe-contracts": "v0.76.0",
++ "openvibe-sdk": "v0.21.3",
++ "openvibe-shared": "v2.3.1"
++ },
++ "deployed": true,
++ "exposure": "internal",
++ "track": "W7"
++}
ddiff --git a/manifests/repositories/OpenVibe.AI.json b/manifests/repositories/OpenVibe.AI.json
nnew file mode 100644
iindex 00000000..d8376b3a
---- /dev/null
++++ b/manifests/repositories/OpenVibe.AI.json
@@@ -0,0 +1,26 @@
++{
++ "name": "OpenVibe.AI",
++ "repository": "OpenVibers/OpenVibe.AI",
++ "service": "ai",
++ "product": "OpenVibe.AI",
++ "stage": "alpha",
++ "version": "0.1.0",
++ "runtime": "node 22",
++ "database": "PostgreSQL",
++ "migrations": 3,
++ "authority": [
++ "ai.usage_summary"
++ ],
++ "domains": [
++ "ai.openvibe.network",
++ "openvibe.ai"
++ ],
++ "pins": {
++ "openvibe-contracts": "v0.76.0",
++ "openvibe-sdk": "v0.25.2",
++ "openvibe-shared": "v1.27.0"
++ },
++ "deployed": true,
++ "exposure": "internal",
++ "track": "W13"
++}
ddiff --git a/manifests/repositories/OpenVibe.Billing.json b/manifests/repositories/OpenVibe.Billing.json
nnew file mode 100644
iindex 00000000..02965d56
---- /dev/null
++++ b/manifests/repositories/OpenVibe.Billing.json
@@@ -0,0 +1,25 @@
++{
++ "name": "OpenVibe.Billing",
++ "repository": "OpenVibers/OpenVibe.Billing",
++ "service": "billing",
++ "product": "OpenVibe.Billing",
++ "stage": "alpha",
++ "version": "0.1.0",
++ "runtime": "node >=22.0.0",
++ "database": "PostgreSQL",
++ "migrations": 1,
++ "authority": [
++ "billing.*"
++ ],
++ "domains": [
++ "billing.openvibe.network"
++ ],
++ "pins": {
++ "openvibe-contracts": "v0.76.0",
++ "openvibe-sdk": "v0.21.2",
++ "openvibe-shared": "v2.3.1"
++ },
++ "deployed": true,
++ "exposure": "live",
++ "track": "W8"
++}
ddiff --git a/manifests/repositories/OpenVibe.Blog.json b/manifests/repositories/OpenVibe.Blog.json
nnew file mode 100644
iindex 00000000..27ed68a7
---- /dev/null
++++ b/manifests/repositories/OpenVibe.Blog.json
@@@ -0,0 +1,26 @@
++{
++ "name": "OpenVibe.Blog",
++ "repository": "OpenVibers/OpenVibe.Blog",
++ "service": "blog",
++ "product": "Blog",
++ "stage": "alpha",
++ "version": "0.1.0",
++ "runtime": "node 22",
++ "database": "PostgreSQL",
++ "migrations": 1,
++ "authority": [
++ "blog.*"
++ ],
++ "domains": [
++ "openvibe.blog"
++ ],
++ "pins": {
++ "openvibe-contracts": "v0.79.0",
++ "openvibe-sdk": "v0.25.0",
++ "openvibe-shared": "v2.3.1",
++ "openvibe-publishing": "v1.0.0"
++ },
++ "deployed": true,
++ "exposure": "live",
++ "track": "W16"
++}
ddiff --git a/manifests/repositories/OpenVibe.Bot.json b/manifests/repositories/OpenVibe.Bot.json
nnew file mode 100644
iindex 00000000..40b57a11
---- /dev/null
++++ b/manifests/repositories/OpenVibe.Bot.json
@@@ -0,0 +1,25 @@
++{
++ "name": "OpenVibe.Bot",
++ "repository": "github:OpenVibers/OpenVibe.Bot",
++ "service": "bot",
++ "product": "OpenVibe.Bot",
++ "stage": null,
++ "version": "0.1.0",
++ "runtime": "node >=22.0.0",
++ "database": "PostgreSQL",
++ "migrations": 1,
++ "authority": [
++ "bot"
++ ],
++ "domains": [
++ "openvibe.bot"
++ ],
++ "pins": {
++ "openvibe-contracts": "v0.79.0",
++ "openvibe-sdk": "v0.25.0",
++ "openvibe-shared": "v2.2.0"
++ },
++ "deployed": null,
++ "exposure": "live",
++ "track": null
++}
ddiff --git a/manifests/repositories/OpenVibe.Chat.json b/manifests/repositories/OpenVibe.Chat.json
nnew file mode 100644
iindex 00000000..7a71c3b3
---- /dev/null
++++ b/manifests/repositories/OpenVibe.Chat.json
@@@ -0,0 +1,29 @@
++{
++ "name": "OpenVibe.Chat",
++ "repository": "OpenVibers/OpenVibe.Chat",
++ "service": "chat",
++ "product": "Chat",
++ "stage": "alpha",
++ "version": "0.1.0",
++ "runtime": "node 22",
++ "database": "PostgreSQL",
++ "migrations": 1,
++ "authority": [
++ "chat.preferences",
++ "chat.tts_defaults",
++ "chat.dm_settings",
++ "chat.presence_prefs"
++ ],
++ "domains": [
++ "openvibe.chat",
++ "openvibe.live"
++ ],
++ "pins": {
++ "openvibe-contracts": "v0.80.0",
++ "openvibe-sdk": "v0.25.2",
++ "openvibe-shared": "v2.0.0"
++ },
++ "deployed": true,
++ "exposure": "live",
++ "track": "W6"
++}
ddiff --git a/manifests/repositories/OpenVibe.Codes.json b/manifests/repositories/OpenVibe.Codes.json
nnew file mode 100644
iindex 00000000..0d59f4b1
---- /dev/null
++++ b/manifests/repositories/OpenVibe.Codes.json
@@@ -0,0 +1,25 @@
++{
++ "name": "OpenVibe.Codes",
++ "repository": "OpenVibers/OpenVibe.Codes",
++ "service": "codes",
++ "product": "Codes",
++ "stage": "alpha",
++ "version": "0.1.0",
++ "runtime": "node 22",
++ "database": "PostgreSQL",
++ "migrations": 1,
++ "authority": [
++ "codes.*"
++ ],
++ "domains": [
++ "openvibe.codes"
++ ],
++ "pins": {
++ "openvibe-contracts": "v0.79.0",
++ "openvibe-sdk": "v0.25.0",
++ "openvibe-shared": "v2.3.1"
++ },
++ "deployed": true,
++ "exposure": "live",
++ "track": "W20"
++}
ddiff --git a/manifests/repositories/OpenVibe.Community.json b/manifests/repositories/OpenVibe.Community.json
nnew file mode 100644
iindex 00000000..c2a2d93a
---- /dev/null
++++ b/manifests/repositories/OpenVibe.Community.json
@@@ -0,0 +1,25 @@
++{
++ "name": "OpenVibe.Community",
++ "repository": "OpenVibers/OpenVibe.Community",
++ "service": "community",
++ "product": "Community",
++ "stage": "alpha",
++ "version": "1.0.0",
++ "runtime": "node 22",
++ "database": "PostgreSQL",
++ "migrations": 2,
++ "authority": [
++ "community.*"
++ ],
++ "domains": [
++ "openvibe.community"
++ ],
++ "pins": {
++ "openvibe-contracts": "v0.76.0",
++ "openvibe-sdk": "v0.21.0",
++ "openvibe-shared": "v2.3.1"
++ },
++ "deployed": true,
++ "exposure": "live",
++ "track": "W5"
++}
ddiff --git a/manifests/repositories/OpenVibe.Contracts.json b/manifests/repositories/OpenVibe.Contracts.json
nnew file mode 100644
iindex 00000000..38e5cc97
---- /dev/null
++++ b/manifests/repositories/OpenVibe.Contracts.json
@@@ -0,0 +1,21 @@
++{
++ "name": "OpenVibe.Contracts",
++ "repository": "OpenVibers/OpenVibe.Contracts",
++ "service": "contracts",
++ "product": "OpenVibe.Contracts",
++ "stage": "alpha",
++ "version": "0.83.0",
++ "runtime": "node >=20",
++ "database": null,
++ "migrations": 0,
++ "authority": [
++ "contracts"
++ ],
++ "domains": [],
++ "pins": {
++ "openvibe-contracts": "v0.75.0"
++ },
++ "deployed": false,
++ "exposure": "library",
++ "track": "W1"
++}
ddiff --git a/manifests/repositories/OpenVibe.Coupons.json b/manifests/repositories/OpenVibe.Coupons.json
nnew file mode 100644
iindex 00000000..858b43d1
---- /dev/null
++++ b/manifests/repositories/OpenVibe.Coupons.json
@@@ -0,0 +1,26 @@
++{
++ "name": "OpenVibe.Coupons",
++ "repository": "OpenVibers/OpenVibe.Coupons",
++ "service": "coupons",
++ "product": "Coupons",
++ "stage": "alpha",
++ "version": "0.1.0",
++ "runtime": "node 22",
++ "database": "PostgreSQL",
++ "migrations": 2,
++ "authority": [
++ "coupons.*"
++ ],
++ "domains": [
++ "openvibe.coupons"
++ ],
++ "pins": {
++ "openvibe-contracts": "v0.79.0",
++ "openvibe-sdk": "v0.25.1",
++ "openvibe-shared": "v2.2.0",
++ "openvibe-publishing": "v1.1.0"
++ },
++ "deployed": true,
++ "exposure": "internal",
++ "track": "W18"
++}
ddiff --git a/manifests/repositories/OpenVibe.Deals.json b/manifests/repositories/OpenVibe.Deals.json
nnew file mode 100644
iindex 00000000..643100af
---- /dev/null
++++ b/manifests/repositories/OpenVibe.Deals.json
@@@ -0,0 +1,26 @@
++{
++ "name": "OpenVibe.Deals",
++ "repository": "OpenVibers/OpenVibe.Deals",
++ "service": "deals",
++ "product": "Deals",
++ "stage": "alpha",
++ "version": "0.1.0",
++ "runtime": "node 22",
++ "database": "PostgreSQL",
++ "migrations": 2,
++ "authority": [
++ "deals.*"
++ ],
++ "domains": [
++ "openvibe.deals"
++ ],
++ "pins": {
++ "openvibe-contracts": "v0.79.0",
++ "openvibe-sdk": "v0.25.1",
++ "openvibe-shared": "v2.2.0",
++ "openvibe-publishing": "v1.1.0"
++ },
++ "deployed": true,
++ "exposure": "internal",
++ "track": "W18"
++}
ddiff --git a/manifests/repositories/OpenVibe.Events.json b/manifests/repositories/OpenVibe.Events.json
nnew file mode 100644
iindex 00000000..c798f01b
---- /dev/null
++++ b/manifests/repositories/OpenVibe.Events.json
@@@ -0,0 +1,25 @@
++{
++ "name": "OpenVibe.Events",
++ "repository": "OpenVibers/OpenVibe.Events",
++ "service": "events",
++ "product": "OpenVibe.Events",
++ "stage": "alpha",
++ "version": "0.1.0",
++ "runtime": "node 22",
++ "database": "PostgreSQL",
++ "migrations": 5,
++ "authority": [
++ "events"
++ ],
++ "domains": [
++ "events.openvibe.network"
++ ],
++ "pins": {
++ "openvibe-contracts": "v0.81.0",
++ "openvibe-sdk": "v0.25.1",
++ "openvibe-shared": "v2.2.0"
++ },
++ "deployed": true,
++ "exposure": "live",
++ "track": "W3"
++}
ddiff --git a/manifests/repositories/OpenVibe.Examples.json b/manifests/repositories/OpenVibe.Examples.json
nnew file mode 100644
iindex 00000000..64a58586
---- /dev/null
++++ b/manifests/repositories/OpenVibe.Examples.json
@@@ -0,0 +1,22 @@
++{
++ "name": "OpenVibe.Examples",
++ "repository": "OpenVibers/OpenVibe.Examples",
++ "service": "examples",
++ "product": "OpenVibe.Examples",
++ "stage": "alpha",
++ "version": "0.3.0",
++ "runtime": "node >=22",
++ "database": null,
++ "migrations": 0,
++ "authority": [
++ "examples"
++ ],
++ "domains": [],
++ "pins": {
++ "openvibe-sdk": "v0.11.0",
++ "openvibe-contracts": "v0.49.0"
++ },
++ "deployed": false,
++ "exposure": "repository",
++ "track": "W20"
++}
ddiff --git a/manifests/repositories/OpenVibe.Extensions.json b/manifests/repositories/OpenVibe.Extensions.json
nnew file mode 100644
iindex 00000000..835ef65d
---- /dev/null
++++ b/manifests/repositories/OpenVibe.Extensions.json
@@@ -0,0 +1,17 @@
++{
++ "name": "OpenVibe.Extensions",
++ "repository": "OpenVibers/OpenVibe.Extensions",
++ "service": null,
++ "product": null,
++ "stage": "alpha",
++ "version": "0.1.0",
++ "runtime": "node >=22.0.0",
++ "database": null,
++ "migrations": 0,
++ "authority": [],
++ "domains": [],
++ "pins": {},
++ "deployed": false,
++ "exposure": null,
++ "track": "W18"
++}
ddiff --git a/manifests/repositories/OpenVibe.Games.json b/manifests/repositories/OpenVibe.Games.json
nnew file mode 100644
iindex 00000000..d9a3cdbb
---- /dev/null
++++ b/manifests/repositories/OpenVibe.Games.json
@@@ -0,0 +1,26 @@
++{
++ "name": "OpenVibe.Games",
++ "repository": "OpenVibers/OpenVibe.Games",
++ "service": "games",
++ "product": "Games",
++ "stage": "stable",
++ "version": "0.1.0",
++ "runtime": "node 22",
++ "database": "PostgreSQL",
++ "migrations": 0,
++ "authority": [
++ "games.*"
++ ],
++ "domains": [
++ "openvibe.games",
++ "play.openvibe.games"
++ ],
++ "pins": {
++ "openvibe-contracts": "v0.71.0",
++ "openvibe-sdk": "v0.22.0",
++ "openvibe-shared": "v1.30.1"
++ },
++ "deployed": true,
++ "exposure": "live",
++ "track": null
++}
ddiff --git a/manifests/repositories/OpenVibe.Host.json b/manifests/repositories/OpenVibe.Host.json
nnew file mode 100644
iindex 00000000..57af3e06
---- /dev/null
++++ b/manifests/repositories/OpenVibe.Host.json
@@@ -0,0 +1,25 @@
++{
++ "name": "OpenVibe.Host",
++ "repository": "OpenVibers/OpenVibe.Host",
++ "service": "host",
++ "product": "Host",
++ "stage": "alpha",
++ "version": "0.3.0",
++ "runtime": "node 22",
++ "database": "PostgreSQL",
++ "migrations": 3,
++ "authority": [
++ "host.*"
++ ],
++ "domains": [
++ "openvibe.host"
++ ],
++ "pins": {
++ "openvibe-contracts": "v0.83.0",
++ "openvibe-sdk": "v0.26.0",
++ "openvibe-shared": "v2.2.0"
++ },
++ "deployed": "loopback only, not launched",
++ "exposure": "internal",
++ "track": "W21 Stage A + Stage B"
++}
ddiff --git a/manifests/repositories/OpenVibe.Live.json b/manifests/repositories/OpenVibe.Live.json
nnew file mode 100644
iindex 00000000..048addc6
---- /dev/null
++++ b/manifests/repositories/OpenVibe.Live.json
@@@ -0,0 +1,26 @@
++{
++ "name": "OpenVibe.Live",
++ "repository": "OpenVibers/OpenVibe.Live",
++ "service": "live",
++ "product": "Live",
++ "stage": "stable",
++ "version": "1.0.0",
++ "runtime": "node 22",
++ "database": "SQLite",
++ "migrations": 0,
++ "authority": [
++ "live.*"
++ ],
++ "domains": [
++ "openvibe.live",
++ "ingest.openvibe.live"
++ ],
++ "pins": {
++ "openvibe-contracts": "v0.71.0",
++ "openvibe-sdk": "v0.16.0",
++ "openvibe-shared": "v1.30.1"
++ },
++ "deployed": true,
++ "exposure": "live",
++ "track": null
++}
ddiff --git a/manifests/repositories/OpenVibe.Media.json b/manifests/repositories/OpenVibe.Media.json
nnew file mode 100644
iindex 00000000..b3466215
---- /dev/null
++++ b/manifests/repositories/OpenVibe.Media.json
@@@ -0,0 +1,25 @@
++{
++ "name": "OpenVibe.Media",
++ "repository": "OpenVibers/OpenVibe.Media",
++ "service": "media",
++ "product": "Media",
++ "stage": "beta",
++ "version": "1.0.0",
++ "runtime": "node 22",
++ "database": "PostgreSQL",
++ "migrations": 1,
++ "authority": [
++ "media.*"
++ ],
++ "domains": [
++ "openvibe.media"
++ ],
++ "pins": {
++ "openvibe-contracts": "v0.79.0",
++ "openvibe-sdk": "v0.21.2",
++ "openvibe-shared": "v1.29.2"
++ },
++ "deployed": true,
++ "exposure": "live",
++ "track": null
++}
ddiff --git a/manifests/repositories/OpenVibe.Network.json b/manifests/repositories/OpenVibe.Network.json
nnew file mode 100644
iindex 00000000..8c0dad36
---- /dev/null
++++ b/manifests/repositories/OpenVibe.Network.json
@@@ -0,0 +1,33 @@
++{
++ "name": "OpenVibe.Network",
++ "repository": "OpenVibers/OpenVibe.Network",
++ "service": "network",
++ "product": "Network",
++ "stage": "stable",
++ "version": "1.0.0",
++ "runtime": "node 22",
++ "database": "PostgreSQL",
++ "migrations": 6,
++ "authority": [
++ "identity.*",
++ "network.*",
++ "theme.*",
++ "notification.*"
++ ],
++ "domains": [
++ "openvibe.network",
++ "auth.openvibe.network",
++ "api.openvibe.network",
++ "admin.openvibe.network",
++ "my.openvibe.network",
++ "themes.openvibe.network"
++ ],
++ "pins": {
++ "openvibe-contracts": "v0.83.0",
++ "openvibe-sdk": "v0.25.2",
++ "openvibe-shared": "v2.3.1"
++ },
++ "deployed": true,
++ "exposure": "live",
++ "track": null
++}
ddiff --git a/manifests/repositories/OpenVibe.News.json b/manifests/repositories/OpenVibe.News.json
nnew file mode 100644
iindex 00000000..576f5660
---- /dev/null
++++ b/manifests/repositories/OpenVibe.News.json
@@@ -0,0 +1,26 @@
++{
++ "name": "OpenVibe.News",
++ "repository": "OpenVibers/OpenVibe.News",
++ "service": "news",
++ "product": "News",
++ "stage": "alpha",
++ "version": "0.1.0",
++ "runtime": "node 22",
++ "database": "PostgreSQL",
++ "migrations": 2,
++ "authority": [
++ "news.*"
++ ],
++ "domains": [
++ "openvibe.news"
++ ],
++ "pins": {
++ "openvibe-contracts": "v0.79.0",
++ "openvibe-sdk": "v0.25.0",
++ "openvibe-shared": "v2.2.0",
++ "openvibe-publishing": "v1.1.0"
++ },
++ "deployed": true,
++ "exposure": "internal",
++ "track": "W17"
++}
ddiff --git a/manifests/repositories/OpenVibe.Node.json b/manifests/repositories/OpenVibe.Node.json
nnew file mode 100644
iindex 00000000..9a02f0cb
---- /dev/null
++++ b/manifests/repositories/OpenVibe.Node.json
@@@ -0,0 +1,17 @@
++{
++ "name": "OpenVibe.Node",
++ "repository": null,
++ "service": null,
++ "product": null,
++ "stage": null,
++ "version": null,
++ "runtime": null,
++ "database": null,
++ "migrations": 0,
++ "authority": [],
++ "domains": [],
++ "pins": {},
++ "deployed": null,
++ "exposure": null,
++ "track": null
++}
ddiff --git a/manifests/repositories/OpenVibe.Publishing.json b/manifests/repositories/OpenVibe.Publishing.json
nnew file mode 100644
iindex 00000000..18d9ea3a
---- /dev/null
++++ b/manifests/repositories/OpenVibe.Publishing.json
@@@ -0,0 +1,23 @@
++{
++ "name": "OpenVibe.Publishing",
++ "repository": "OpenVibers/OpenVibe.Publishing",
++ "service": "publishing",
++ "product": "OpenVibe.Publishing",
++ "stage": "alpha",
++ "version": "1.1.0",
++ "runtime": "node >=20",
++ "database": "none",
++ "migrations": 0,
++ "authority": [
++ "publishing"
++ ],
++ "domains": [],
++ "pins": {
++ "openvibe-contracts": "v0.76.0",
++ "openvibe-sdk": "v0.15.0",
++ "openvibe-shared": "v1.25.0"
++ },
++ "deployed": false,
++ "exposure": "library",
++ "track": "W15"
++}
ddiff --git a/manifests/repositories/OpenVibe.Realtime.json b/manifests/repositories/OpenVibe.Realtime.json
nnew file mode 100644
iindex 00000000..e02a13b3
---- /dev/null
++++ b/manifests/repositories/OpenVibe.Realtime.json
@@@ -0,0 +1,17 @@
++{
++ "name": "OpenVibe.Realtime",
++ "repository": "OpenVibers/OpenVibe.Realtime",
++ "service": null,
++ "product": null,
++ "stage": "closed",
++ "version": null,
++ "runtime": null,
++ "database": null,
++ "migrations": 0,
++ "authority": [],
++ "domains": [],
++ "pins": {},
++ "deployed": false,
++ "exposure": null,
++ "track": null
++}
ddiff --git a/manifests/repositories/OpenVibe.Reviews.json b/manifests/repositories/OpenVibe.Reviews.json
nnew file mode 100644
iindex 00000000..e823b5e3
---- /dev/null
++++ b/manifests/repositories/OpenVibe.Reviews.json
@@@ -0,0 +1,26 @@
++{
++ "name": "OpenVibe.Reviews",
++ "repository": "OpenVibers/OpenVibe.Reviews",
++ "service": "reviews",
++ "product": "Reviews",
++ "stage": "alpha",
++ "version": "0.1.0",
++ "runtime": "node 22",
++ "database": "PostgreSQL",
++ "migrations": 2,
++ "authority": [
++ "reviews.*"
++ ],
++ "domains": [
++ "openvibe.reviews"
++ ],
++ "pins": {
++ "openvibe-contracts": "v0.76.0",
++ "openvibe-sdk": "v0.20.0",
++ "openvibe-shared": "v2.2.0",
++ "openvibe-publishing": "v1.1.0"
++ },
++ "deployed": true,
++ "exposure": "internal",
++ "track": "W17"
++}
ddiff --git a/manifests/repositories/OpenVibe.SDK.json b/manifests/repositories/OpenVibe.SDK.json
nnew file mode 100644
iindex 00000000..294909d2
---- /dev/null
++++ b/manifests/repositories/OpenVibe.SDK.json
@@@ -0,0 +1,21 @@
++{
++ "name": "OpenVibe.SDK",
++ "repository": "OpenVibers/OpenVibe.SDK",
++ "service": "sdk",
++ "product": "OpenVibe.SDK",
++ "stage": "alpha",
++ "version": "0.26.0",
++ "runtime": "node >=20",
++ "database": null,
++ "migrations": 0,
++ "authority": [
++ "sdk"
++ ],
++ "domains": [],
++ "pins": {
++ "openvibe-contracts": "v0.86.0"
++ },
++ "deployed": false,
++ "exposure": "library",
++ "track": "W2"
++}
ddiff --git a/manifests/repositories/OpenVibe.Search.json b/manifests/repositories/OpenVibe.Search.json
nnew file mode 100644
iindex 00000000..128c6c21
---- /dev/null
++++ b/manifests/repositories/OpenVibe.Search.json
@@@ -0,0 +1,25 @@
++{
++ "name": "OpenVibe.Search",
++ "repository": "OpenVibers/OpenVibe.Search",
++ "service": "search",
++ "product": "OpenVibe.Search",
++ "stage": "alpha",
++ "version": "0.1.0",
++ "runtime": "node 22",
++ "database": "PostgreSQL",
++ "migrations": 1,
++ "authority": [
++ "search"
++ ],
++ "domains": [
++ "search.openvibe.network"
++ ],
++ "pins": {
++ "openvibe-contracts": "v0.76.0",
++ "openvibe-sdk": "v0.23.1",
++ "openvibe-shared": "v2.2.0"
++ },
++ "deployed": true,
++ "exposure": "live",
++ "track": "W14"
++}
ddiff --git a/manifests/repositories/OpenVibe.Shared.json b/manifests/repositories/OpenVibe.Shared.json
nnew file mode 100644
iindex 00000000..9ab6b76e
---- /dev/null
++++ b/manifests/repositories/OpenVibe.Shared.json
@@@ -0,0 +1,22 @@
++{
++ "name": "OpenVibe.Shared",
++ "repository": "OpenVibers/OpenVibe.Shared",
++ "service": "shared",
++ "product": "OpenVibe.Shared",
++ "stage": "alpha",
++ "version": "2.3.1",
++ "runtime": null,
++ "database": null,
++ "migrations": 0,
++ "authority": [
++ "shared"
++ ],
++ "domains": [],
++ "pins": {
++ "openvibe-contracts": "v0.61.0",
++ "openvibe-sdk": "v0.20.4"
++ },
++ "deployed": false,
++ "exposure": "library",
++ "track": "W2"
++}
ddiff --git a/manifests/repositories/OpenVibe.Sites.json b/manifests/repositories/OpenVibe.Sites.json
nnew file mode 100644
iindex 00000000..e5876467
---- /dev/null
++++ b/manifests/repositories/OpenVibe.Sites.json
@@@ -0,0 +1,21 @@
++{
++ "name": "OpenVibe.Sites",
++ "repository": "OpenVibers/OpenVibe.Sites",
++ "service": "sites",
++ "product": "OpenVibe.Sites",
++ "stage": "stable",
++ "version": null,
++ "runtime": "node >=20",
++ "database": null,
++ "migrations": 0,
++ "authority": [
++ "sites"
++ ],
++ "domains": [],
++ "pins": {
++ "openvibe-shared": "v1.29.2"
++ },
++ "deployed": true,
++ "exposure": "live",
++ "track": null
++}
ddiff --git a/manifests/repositories/OpenVibe.Sources.json b/manifests/repositories/OpenVibe.Sources.json
nnew file mode 100644
iindex 00000000..39f9ee60
---- /dev/null
++++ b/manifests/repositories/OpenVibe.Sources.json
@@@ -0,0 +1,25 @@
++{
++ "name": "OpenVibe.Sources",
++ "repository": "OpenVibers/OpenVibe.Sources",
++ "service": "sources",
++ "product": "OpenVibe.Sources",
++ "stage": "alpha",
++ "version": "0.1.0",
++ "runtime": "node 22",
++ "database": "PostgreSQL",
++ "migrations": 1,
++ "authority": [
++ "sources"
++ ],
++ "domains": [
++ "sources.openvibe.network"
++ ],
++ "pins": {
++ "openvibe-contracts": "v0.76.0",
++ "openvibe-sdk": "v0.23.1",
++ "openvibe-shared": "v1.27.0"
++ },
++ "deployed": true,
++ "exposure": "internal",
++ "track": "W14"
++}
ddiff --git a/manifests/repositories/OpenVibe.Tips.json b/manifests/repositories/OpenVibe.Tips.json
nnew file mode 100644
iindex 00000000..ec577304
---- /dev/null
++++ b/manifests/repositories/OpenVibe.Tips.json
@@@ -0,0 +1,25 @@
++{
++ "name": "OpenVibe.Tips",
++ "repository": "OpenVibers/OpenVibe.Tips",
++ "service": "tips",
++ "product": "Tips",
++ "stage": "alpha",
++ "version": "0.1.0",
++ "runtime": "node >=22.0.0",
++ "database": "PostgreSQL",
++ "migrations": 2,
++ "authority": [
++ "tips.*"
++ ],
++ "domains": [
++ "openvibe.tips"
++ ],
++ "pins": {
++ "openvibe-contracts": "v0.79.0",
++ "openvibe-sdk": "v0.25.0",
++ "openvibe-shared": "v2.2.0"
++ },
++ "deployed": true,
++ "exposure": "internal",
++ "track": "W9"
++}
ddiff --git a/manifests/repositories/OpenVibe.Tools.json b/manifests/repositories/OpenVibe.Tools.json
nnew file mode 100644
iindex 00000000..53f61cb5
---- /dev/null
++++ b/manifests/repositories/OpenVibe.Tools.json
@@@ -0,0 +1,23 @@
++{
++ "name": "OpenVibe.Tools",
++ "repository": "OpenVibers/OpenVibe.Tools",
++ "service": "tools",
++ "product": "Tools",
++ "stage": "stable",
++ "version": "1.0.0",
++ "runtime": "node 22",
++ "database": "PostgreSQL",
++ "migrations": 4,
++ "authority": [
++ "tools.*"
++ ],
++ "domains": [
++ "openvibe.tools"
++ ],
++ "pins": {
++ "openvibe-contracts": "v0.63.0"
++ },
++ "deployed": true,
++ "exposure": "live",
++ "track": null
++}
ddiff --git a/manifests/repositories/OpenVibe.Trade.json b/manifests/repositories/OpenVibe.Trade.json
nnew file mode 100644
iindex 00000000..56940ce5
---- /dev/null
++++ b/manifests/repositories/OpenVibe.Trade.json
@@@ -0,0 +1,26 @@
++{
++ "name": "OpenVibe.Trade",
++ "repository": "OpenVibers/OpenVibe.Trade",
++ "service": "trade",
++ "product": "Trade",
++ "stage": "alpha",
++ "version": "0.1.0",
++ "runtime": "node 22",
++ "database": "PostgreSQL",
++ "migrations": 2,
++ "authority": [
++ "trade.*"
++ ],
++ "domains": [
++ "openvibe.trade"
++ ],
++ "pins": {
++ "openvibe-contracts": "v0.79.0",
++ "openvibe-sdk": "v0.25.0",
++ "openvibe-shared": "v2.2.0",
++ "openvibe-publishing": "v1.1.0"
++ },
++ "deployed": true,
++ "exposure": "internal",
++ "track": "W19"
++}
ddiff --git a/manifests/repositories/OpenVibe.VIP.json b/manifests/repositories/OpenVibe.VIP.json
nnew file mode 100644
iindex 00000000..603d8b7a
---- /dev/null
++++ b/manifests/repositories/OpenVibe.VIP.json
@@@ -0,0 +1,25 @@
++{
++ "name": "OpenVibe.VIP",
++ "repository": "OpenVibers/OpenVibe.VIP",
++ "service": "vip",
++ "product": "VIP",
++ "stage": "alpha",
++ "version": "0.1.0",
++ "runtime": "node >=22.0.0",
++ "database": "PostgreSQL",
++ "migrations": 1,
++ "authority": [
++ "vip.*"
++ ],
++ "domains": [
++ "openvibe.vip"
++ ],
++ "pins": {
++ "openvibe-contracts": "v0.79.0",
++ "openvibe-sdk": "v0.25.0",
++ "openvibe-shared": "v2.2.0"
++ },
++ "deployed": true,
++ "exposure": "internal",
++ "track": null
++}
ddiff --git a/manifests/repositories/OpenVibe.Wiki.json b/manifests/repositories/OpenVibe.Wiki.json
nnew file mode 100644
iindex 00000000..1b078efd
---- /dev/null
++++ b/manifests/repositories/OpenVibe.Wiki.json
@@@ -0,0 +1,26 @@
++{
++ "name": "OpenVibe.Wiki",
++ "repository": "OpenVibers/OpenVibe.Wiki",
++ "service": "wiki",
++ "product": "Wiki",
++ "stage": "alpha",
++ "version": "0.1.0",
++ "runtime": "node 22",
++ "database": "PostgreSQL",
++ "migrations": 1,
++ "authority": [
++ "wiki.*"
++ ],
++ "domains": [
++ "openvibe.wiki"
++ ],
++ "pins": {
++ "openvibe-contracts": "v0.76.0",
++ "openvibe-sdk": "v0.19.0",
++ "openvibe-shared": "v2.3.1",
++ "openvibe-publishing": "v1.0.0"
++ },
++ "deployed": true,
++ "exposure": "live",
++ "track": "W16"
++}
ddiff --git a/manifests/repositories/openvibe-agents.json b/manifests/repositories/openvibe-agents.json
nnew file mode 100644
iindex 00000000..3093a7c4
---- /dev/null
++++ b/manifests/repositories/openvibe-agents.json
@@@ -0,0 +1,17 @@
++{
++ "name": "openvibe-agents",
++ "repository": null,
++ "service": null,
++ "product": null,
++ "stage": null,
++ "version": null,
++ "runtime": null,
++ "database": null,
++ "migrations": 0,
++ "authority": [],
++ "domains": [],
++ "pins": {},
++ "deployed": null,
++ "exposure": null,
++ "track": null
++}
ddiff --git a/package-lock.json b/package-lock.json
iindex 8d96f748..11b7c772 100644
---- a/package-lock.json
++++ b/package-lock.json
@@@ -1,12 +1,12 @@
{
"name": "openvibe-contracts",
-- "version": "0.98.0",
++ "version": "0.99.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "openvibe-contracts",
-- "version": "0.98.0",
++ "version": "0.99.0",
"license": "AGPL-3.0-only",
"dependencies": {
"ajv": "^8.17.1",
ddiff --git a/package.json b/package.json
iindex b6c1690e..8d9f40cd 100644
---- a/package.json
++++ b/package.json
@@@ -1,6 +1,6 @@
{
"name": "openvibe-contracts",
-- "version": "0.98.0",
++ "version": "0.99.0",
"description": "Machine-readable contracts for the OpenVibe network: schemas, manifests, ids, error envelope, capability checks.",
"main": "index.js",
"types": "generated/typescript/index.d.ts",
@@@ -15,7 +15,8 @@
],
"scripts": {
"test": "node test/run.js",
-- "generate": "node scripts/generate.js"
++ "generate": "node scripts/generate.js",
++ "estate": "node scripts/estate.js"
},
"engines": {
"node": ">=20"
ddiff --git a/scripts/estate.js b/scripts/estate.js
nnew file mode 100644
iindex 00000000..c1bfaba8
---- /dev/null
++++ b/scripts/estate.js
@@@ -0,0 +1,258 @@
++#!/usr/bin/env node
++'use strict';
++/**
++ * The estate (plan T1 step 3): every repository checked out under the estate root as one machine-readable
++ * record, and docs/ESTATE.md as the same records in the plan §1.1 table.
++ *
++ * node scripts/estate.js # refresh docs/ESTATE.md and manifests/repositories/*.json
++ * node scripts/estate.js <root> # from another checkout tree (default ~/OpenVibers)
++ * node scripts/estate.js --check # exit 1 when either generated file is stale
++ * node scripts/estate.js <root> --out <dir> # write/check under <dir> (tests)
++ *
++ * A checkout is a directory under the root holding `.git`, `package.json` or `STATUS.json`. For each one:
++ *
++ * package.json version, engines.node and the openvibe-contracts/-sdk/-shared/-publishing pins
++ * STATUS.json repository, stage, runtime, domain, database, deployed and wave
++ * migrations/ the count and the dialect the SQL declares
++ * this repository's manifests/services|products/*.json the service or product the checkout is
++ *
++ * The records are the source of truth for docs/ESTATE.md, so the document is a projection, never edited by
++ * hand. `--check` re-reads the checkouts and compares both; when there is no checkout root (CI), it checks
++ * docs/ESTATE.md against the committed manifests/repositories and says so. Nothing is fetched.
++ */
++const fs = require('fs');
++const os = require('os');
++const path = require('path');
++
++const ROOT = path.join(__dirname, '..');
++const PIN_NAMES = ['openvibe-contracts', 'openvibe-sdk', 'openvibe-shared', 'openvibe-publishing'];
++const MARKERS = ['.git', 'package.json', 'STATUS.json'];
++const DASH = '—';
++
++const readText = (file) => { try { return fs.readFileSync(file, 'utf8'); } catch { return ''; } };
++const readJson = (file) => { const s = readText(file); if (!s) return null; try { return JSON.parse(s); } catch { return null; } };
++const isDir = (p) => { try { return fs.statSync(p).isDirectory(); } catch { return false; } };
++
++/** Every service and product manifest this checkout of Contracts ships, keyed for lookup by repository. */
++function manifestIndex() {
++ const load = (sub) => {
++ const dir = path.join(ROOT, 'manifests', sub);
++ if (!isDir(dir)) return [];
++ return fs.readdirSync(dir).filter((f) => f.endsWith('.json')).sort()
++ .map((f) => readJson(path.join(dir, f))).filter(Boolean);
++ };
++ return { services: load('services'), products: load('products') };
++}
++
++/** The checkout directory a manifest names: its `repository`, else a product's `relationships.plannedRepo`. */
++function checkoutOf(manifest) {
++ const repo = manifest.repository || (manifest.relationships && manifest.relationships.plannedRepo);
++ if (!repo) return null;
++ const s = typeof repo === 'string' ? repo : repo.url;
++ return s ? String(s).split('/').pop().replace(/\.git$/, '') : null;
++}
++
++/** A pin as published: the release tag when it is one, otherwise the range or URL as written. */
++function normalizePin(spec) {
++ if (!spec) return null;
++ const m = /refs\/tags\/(v?\d+\.\d+\.\d+)/.exec(String(spec));
++ if (m) return m[1].startsWith('v') ? m[1] : `v${m[1]}`;
++ const bare = String(spec).trim();
++ return /^[~^]?v?\d+\.\d+\.\d+$/.test(bare) ? (bare.replace(/^[~^]/, '').startsWith('v') ? bare.replace(/^[~^]/, '') : `v${bare.replace(/^[~^]/, '')}`) : bare;
++}
++
++function pinsOf(pkg) {
++ const out = {};
++ if (!pkg) return out;
++ const deps = { ...(pkg.dependencies || {}), ...(pkg.peerDependencies || {}), ...(pkg.devDependencies || {}) };
++ for (const name of PIN_NAMES) if (deps[name]) out[name] = normalizePin(deps[name]);
++ return out;
++}
++
++function migrationFiles(dir) {
++ const mig = path.join(dir, 'migrations');
++ if (!isDir(mig)) return [];
++ return fs.readdirSync(mig).filter((f) => /\.(sql|js|cjs|mjs)$/.test(f)).sort();
++}
++
++/** The database the checkout runs on: what STATUS.json states, else what migrations/ or the drivers say. */
++function databaseOf(dir, status, pkg, exposure) {
++ if (status && typeof status.database === 'string' && /^\s*none\b/i.test(status.database)) return 'none';
++ if (['library', 'repository', 'retired'].includes(exposure)) return null;
++ const text = `${(status && status.database) || ''} ${(status && status.runtime) || ''}`;
++ if (/postgres|pglite/i.test(text)) return 'PostgreSQL';
++ if (/sqlite/i.test(text)) return 'SQLite';
++ const sql = migrationFiles(dir).slice(0, 3).map((f) => readText(path.join(dir, 'migrations', f))).join('\n');
++ if (/postgres|plpgsql|jsonb|bigserial|timestamptz/i.test(sql)) return 'PostgreSQL';
++ if (/sqlite/i.test(sql)) return 'SQLite';
++ const deps = { ...((pkg && pkg.dependencies) || {}), ...((pkg && pkg.devDependencies) || {}) };
++ if (deps.pg || deps.postgres || deps['@electric-sql/pglite']) return 'PostgreSQL';
++ if (deps['better-sqlite3'] || deps.sqlite3) return 'SQLite';
++ return null;
++}
++
++/** The domains the checkout serves: its manifest's, then every hostname STATUS.json's prose names. */
++function domainsOf(status, manifest) {
++ const out = [];
++ const add = (d) => { const s = String(d).trim().toLowerCase().replace(/^["']|["']$/g, ''); if (s && !out.includes(s)) out.push(s); };
++ if (manifest) for (const d of manifest.domains || []) add(d);
++ for (const d of String((status && status.domain) || '').match(/\b(?:[a-z0-9-]+\.)+[a-z]{2,}\b/g) || []) add(d);
++ return out;
++}
++
++/** The openvibe-* release tags a checkout pins: package.json's, filled in from STATUS.json's prose. */
++function pinTag(text, name) {
++ const m = new RegExp(`${name.replace(/[.]/g, '\\.')}\\s+(v?\\d+\\.\\d+\\.\\d+)`).exec(String(text || ''));
++ return m ? (m[1].startsWith('v') ? m[1] : `v${m[1]}`) : null;
++}
++
++function estatePins(pkg, status) {
++ const pins = pinsOf(pkg);
++ if (status) for (const name of PIN_NAMES) if (!pins[name]) {
++ const tag = pinTag(`${status.contracts || ''} ${status.sdk || ''}`, name);
++ if (tag) pins[name] = tag;
++ }
++ return pins;
++}
++
++/** One record for one checkout. Every field is present; a null means no source states it. */
++function recordFor(name, dir, index) {
++ const pkg = readJson(path.join(dir, 'package.json'));
++ const status = readJson(path.join(dir, 'STATUS.json'));
++ const service = index.services.find((m) => checkoutOf(m) === name) || null;
++ const product = !service && index.products.find((m) => checkoutOf(m) === name || m.name === name) || null;
++ const manifest = service || product;
++ const exposure = service ? (service.exposure && service.exposure.state) || null : (product ? 'product' : null);
++ const runtime = status && typeof status.runtime === 'string' && status.runtime.trim()
++ ? status.runtime.split(/[,;]/)[0].trim()
++ : (pkg && pkg.engines && pkg.engines.node ? `node ${pkg.engines.node}` : null);
++ const repository = (status && status.repository) || (pkg && (typeof pkg.repository === 'string' ? pkg.repository : pkg.repository && pkg.repository.url)) || null;
++ return {
++ name,
++ repository: repository ? String(repository).replace(/^git\+/, '').replace(/\.git$/, '') : null,
++ service: service ? service.id : null,
++ product: service ? ((service.site && service.site.name) || service.name) : (product ? product.name : (status && status.product) || null),
++ stage: (status && status.stage) || null,
++ version: (pkg && pkg.version) || null,
++ runtime,
++ database: databaseOf(dir, status, pkg, exposure),
++ migrations: migrationFiles(dir).length,
++ authority: service ? ((service.namespacesOwned || []).length ? service.namespacesOwned.slice() : [service.id]) : [],
++ domains: domainsOf(status, manifest),
++ pins: estatePins(pkg, status),
++ deployed: status ? (status.deployed === undefined ? null : status.deployed) : null,
++ exposure,
++ track: (status && status.wave) || null,
++ };
++}
++
++/** Every checkout under the root, sorted by name. */
++function checkouts(root) {
++ return fs.readdirSync(root, { withFileTypes: true })
++ .filter((e) => e.isDirectory() && !e.name.startsWith('.'))
++ .map((e) => e.name)
++ .filter((n) => MARKERS.some((m) => fs.existsSync(path.join(root, n, m))))
++ .sort();
++}
++
++function buildRows(root) {
++ const index = manifestIndex();
++ return checkouts(root).map((n) => recordFor(n, path.join(root, n), index));
++}
++
++function readRows(out) {
++ const dir = path.join(out, 'manifests', 'repositories');
++ if (!isDir(dir)) return null;
++ return fs.readdirSync(dir).filter((f) => f.endsWith('.json')).sort()
++ .map((f) => readJson(path.join(dir, f))).filter(Boolean);
++}
++
++const cell = (v) => String(v === null || v === undefined || v === '' ? DASH : v).replace(/\|/g, '\\|').replace(/\r?\n/g, ' ');
++const list = (v) => (v && v.length ? v.join(', ') : DASH);
++const pinText = (pins) => [['openvibe-contracts', 'contracts'], ['openvibe-sdk', 'sdk'], ['openvibe-shared', 'shared'], ['openvibe-publishing', 'publishing']]
++ .filter(([n]) => pins && pins[n]).map(([n, label]) => `${label} ${pins[n]}`).join(', ') || DASH;
++const deployText = (d) => (d === true ? 'deployed' : d === false ? 'not deployed' : d == null ? DASH : String(d));
++
++/** docs/ESTATE.md: the records as the plan §1.1 table. */
++function renderDoc(rows) {
++ const head = [
++ '# The estate',
++ '',
++ 'Generated by `scripts/estate.js` from the repositories checked out under the estate root (default `~/OpenVibers`): each',
++ "checkout's `package.json` pins, `STATUS.json`, `migrations/`, and this repository's `manifests/services/` and",
++ '`manifests/products/`. Data only. Refresh with `npm run estate`; `node scripts/estate.js --check` fails on drift.',
++ '',
++ `\`${DASH}\` means the sources do not state it. **Authority it owns** is the service manifest's \`namespacesOwned\` (or the`,
++ 'service id); **Runtime** is `STATUS.json.runtime`; **Database** is inferred from `STATUS.json`, `migrations/` and',
++ '`package.json`; **Deployment** is `STATUS.json.deployed`; **Public/private** is the manifest `exposure.state`;',
++ '**Current track** is `STATUS.json.wave`.',
++ '',
++ '| Repository | Product | Authority it owns | Runtime | Database | Domain | SDK/Contracts pin | Deployment | Public/private | Current track |',
++ '|---|---|---|---|---|---|---|---|---|---|',
++ ];
++ const body = rows.map((r) => `| ${cell(r.name)} | ${cell(r.product)} | ${cell(list(r.authority))} | ${cell(r.runtime)} | ${cell(r.database)} | ${cell(list(r.domains))} | ${cell(pinText(r.pins))} | ${cell(deployText(r.deployed))} | ${cell(r.exposure)} | ${cell(r.track)} |`);
++ return `${head.concat(body).join('\n')}\n`;
++}
++
++const manifestString = (record) => `${JSON.stringify(record, null, 2)}\n`;
++
++function parseArgs(argv) {
++ const opts = { check: false, root: null, out: null };
++ for (let i = 0; i < argv.length; i++) {
++ const a = argv[i];
++ if (a === '--check') opts.check = true;
++ else if (a === '--out') opts.out = argv[++i] || null;
++ else if (a === '--root') opts.root = argv[++i] || null;
++ else if (!a.startsWith('-') && !opts.root) opts.root = a;
++ }
++ return opts;
++}
++
++function main(argv) {
++ const opts = parseArgs(argv);
++ const root = opts.root || process.env.OPENVIBE_ESTATE_ROOT || path.join(os.homedir(), 'OpenVibers');
++ const out = opts.out ? path.resolve(opts.out) : ROOT;
++ let rows;
++ let fromManifests = false;
++ if (isDir(root)) rows = buildRows(root);
++ else if (opts.check) {
++ rows = readRows(out);
++ if (!rows) { console.log(`estate: skipped (no checkout root at ${root} and no manifests/repositories/)`); process.exit(0); }
++ fromManifests = true;
++ } else {
++ console.log(`estate: skipped (no checkout root at ${root})`);
++ process.exit(0);
++ }
++ const doc = renderDoc(rows);
++ const files = new Map(rows.map((r) => [`${r.name}.json`, manifestString(r)]));
++ const docRel = path.join('docs', 'ESTATE.md');
++
++ if (opts.check) {
++ const drift = [];
++ if (!fromManifests) {
++ const dir = path.join(out, 'manifests', 'repositories');
++ const disk = isDir(dir) ? fs.readdirSync(dir).filter((f) => f.endsWith('.json')) : [];
++ for (const [f, s] of files) if (readText(path.join(dir, f)) !== s) drift.push(path.join('manifests', 'repositories', f));
++ for (const f of disk) if (!files.has(f)) drift.push(path.join('manifests', 'repositories', f));
++ }
++ if (readText(path.join(out, docRel)) !== doc) drift.push(docRel);
++ if (drift.length) {
++ for (const p of drift) console.error(`estate drift: ${p} is stale (run npm run estate)`);
++ console.error(`estate drift: ${drift.length} file(s) out of date`);
++ process.exit(1);
++ }
++ console.log(`estate: current${fromManifests ? ` (docs checked against manifests/repositories; no checkout root at ${root})` : ''}`);
++ process.exit(0);
++ }
++
++ const dir = path.join(out, 'manifests', 'repositories');
++ fs.mkdirSync(dir, { recursive: true });
++ for (const f of fs.readdirSync(dir)) if (f.endsWith('.json') && !files.has(f)) fs.rmSync(path.join(dir, f));
++ for (const [f, s] of files) fs.writeFileSync(path.join(dir, f), s);
++ fs.mkdirSync(path.join(out, 'docs'), { recursive: true });
++ fs.writeFileSync(path.join(out, docRel), doc);
++ console.log(`estate: ${rows.length} repositories; docs/ESTATE.md and manifests/repositories/ written`);
++}
++
++if (require.main === module) main(process.argv.slice(2));
++module.exports = { buildRows, checkouts, renderDoc, recordFor, manifestIndex, normalizePin, databaseOf, pinsOf, estatePins, domainsOf, parseArgs };
ddiff --git a/test/run.js b/test/run.js
iindex f1e2a079..0d653aa3 100644
---- a/test/run.js
++++ b/test/run.js
@@@ -912,6 +912,39 @@ sub(path.join(__dirname, 'usage-topics.test.js'));
sub(path.join(__dirname, 'billing-grace.test.js'));
sub(path.join(__dirname, 'resources.test.js'));
++// ── Estate (plan T1 step 3): docs/ESTATE.md and manifests/repositories are generated ──
++// A checkout tree is read into one repository manifest and the document; --check fails on drift, and with
++// no checkout root (CI) it still checks the document against the committed manifests.
++{
++ const os = require('os');
++ const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'ovc-estate-'));
++ const root = path.join(tmp, 'estate');
++ const out = path.join(tmp, 'out');
++ const run = (...a) => spawnSync(process.execPath, [path.join(ROOT, 'scripts/estate.js'), ...a], { encoding: 'utf8' });
++ const write = (rel, body) => { fs.mkdirSync(path.dirname(path.join(root, rel)), { recursive: true }); fs.writeFileSync(path.join(root, rel), body); };
++ const pin = (v) => `https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/${v}`;
++ write('OpenVibe.Live/package.json', JSON.stringify({ name: 'openvibe-live', version: '1.2.3', dependencies: { 'openvibe-contracts': pin('v0.9.1') }, engines: { node: '>=22' } }));
++ write('OpenVibe.Live/STATUS.json', JSON.stringify({ repository: 'OpenVibers/OpenVibe.Live', stage: 'beta', deployed: true, runtime: 'node 22, better-sqlite3', domain: 'openvibe.live', wave: 'W1', updated: '2026-10-01' }));
++ write('OpenVibe.Live/migrations/0001_initial.sql', 'CREATE TABLE t (id integer primary key);\n');
++ let r = run(root, '--out', out);
++ ok(r.status === 0, `estate generates from a checkout tree: ${r.stdout}${r.stderr}`);
++ const record = JSON.parse(fs.readFileSync(path.join(out, 'manifests/repositories/OpenVibe.Live.json'), 'utf8'));
++ ok(record.name === 'OpenVibe.Live' && record.service === 'live' && record.product === 'Live' && record.database === 'SQLite', 'the repository manifest is built from the checkout and its service manifest');
++ ok(record.pins['openvibe-contracts'] === 'v0.9.1' && record.migrations === 1 && record.authority.includes('live.*'), 'pins, migrations and authority are read');
++ const doc = fs.readFileSync(path.join(out, 'docs/ESTATE.md'), 'utf8');
++ ok(/\| OpenVibe\.Live \| Live \| live\.\* \|/.test(doc) && /contracts v0\.9\.1/.test(doc), 'docs/ESTATE.md is the records as the plan §1.1 table');
++ r = run(root, '--out', out, '--check');
++ ok(r.status === 0 && /estate: current/.test(r.stdout), `--check is clean after generation: ${r.stdout}${r.stderr}`);
++ write('OpenVibe.Live/package.json', JSON.stringify({ name: 'openvibe-live', version: '1.2.3', dependencies: { 'openvibe-contracts': pin('v0.9.2') }, engines: { node: '>=22' } }));
++ r = run(root, '--out', out, '--check');
++ ok(r.status === 1 && /estate drift: manifests\/repositories\/OpenVibe\.Live\.json/.test(r.stderr), `--check catches a drifted checkout: ${r.stderr}`);
++ r = run(root, '--out', out);
++ fs.appendFileSync(path.join(out, 'docs/ESTATE.md'), '\nstale\n');
++ const noRoot = run(path.join(tmp, 'absent'), '--out', out, '--check');
++ ok(noRoot.status === 1 && /estate drift: docs\/ESTATE\.md/.test(noRoot.stderr), `without a checkout root, --check catches a stale document: ${noRoot.stderr}`);
++ fs.rmSync(tmp, { recursive: true, force: true });
++}
++
// ── Generated output and compatibility gate ──────────────────────────────
sub(path.join(__dirname, 'platform-fabric.test.js'));
sub(path.join(__dirname, 'platform-run.test.js'));