openvibers/openvibe.contracts Commits

v0.21.0: OpenVibe.News is alpha (news.* capabilities)

OP OpenVibers committed 23 Sept 2026 · 4382badd20 · 13 files, +282 −9


Changes

ddiff --git a/manifests/capabilities/news.cluster.manage.json b/manifests/capabilities/news.cluster.manage.json
nnew file mode 100644
iindex 00000000..604e5264
---- /dev/null
++++ b/manifests/capabilities/news.cluster.manage.json
@@@ -0,0 +1,23 @@
++{
++ "id": "news.cluster.manage",
++ "version": "1.0",
++ "owner": "news",
++ "status": "active",
++ "visibility": "first-party",
++ "description": "Merge clusters, split items into a new cluster, and reverse a merge or split. Every change is audited with exactly which items moved.",
++ "permissions": [
++ "news:write"
++ ],
++ "resourceConstraints": [
++ "none"
++ ],
++ "quotaClass": "news-write",
++ "events": [
++ "news.cluster.updated"
++ ],
++ "implementedBy": [
++ "POST /api/v1/clusters/:id/merge",
++ "POST /api/v1/clusters/:id/split",
++ "POST /api/v1/clusters/audit/:auditId/reverse"
++ ]
++}
ddiff --git a/manifests/capabilities/news.cluster.read.json b/manifests/capabilities/news.cluster.read.json
nnew file mode 100644
iindex 00000000..227d670e
---- /dev/null
++++ b/manifests/capabilities/news.cluster.read.json
@@@ -0,0 +1,22 @@
++{
++ "id": "news.cluster.read",
++ "version": "1.0",
++ "owner": "news",
++ "status": "active",
++ "visibility": "first-party",
++ "description": "Read clusters (members, the stored explanation of every membership, merge/split history), source items with their dedupe outcome, and ingestion runs and source health.",
++ "permissions": [
++ "news:read"
++ ],
++ "resourceConstraints": [
++ "none"
++ ],
++ "quotaClass": "news-read",
++ "events": [],
++ "implementedBy": [
++ "GET /api/v1/clusters",
++ "GET /api/v1/clusters/:id",
++ "GET /api/v1/source-items/:id",
++ "GET /api/v1/ingest"
++ ]
++}
ddiff --git a/manifests/capabilities/news.perspective.update.json b/manifests/capabilities/news.perspective.update.json
nnew file mode 100644
iindex 00000000..622edc0a
---- /dev/null
++++ b/manifests/capabilities/news.perspective.update.json
@@@ -0,0 +1,21 @@
++{
++ "id": "news.perspective.update",
++ "version": "1.0",
++ "owner": "news",
++ "status": "active",
++ "visibility": "first-party",
++ "description": "Create or remove editor-labelled perspective groupings on a story and assign its sources to them. Labels are written by editors, never generated.",
++ "permissions": [
++ "news:write"
++ ],
++ "resourceConstraints": [
++ "none"
++ ],
++ "quotaClass": "news-write",
++ "events": [],
++ "implementedBy": [
++ "POST /api/v1/stories/:id/perspectives",
++ "DELETE /api/v1/stories/:id/perspectives/:pid",
++ "PUT /api/v1/stories/:id/sources/:item/perspective"
++ ]
++}
ddiff --git a/manifests/capabilities/news.source.attach.json b/manifests/capabilities/news.source.attach.json
nnew file mode 100644
iindex 00000000..bea0cdb9
---- /dev/null
++++ b/manifests/capabilities/news.source.attach.json
@@@ -0,0 +1,20 @@
++{
++ "id": "news.source.attach",
++ "version": "1.0",
++ "owner": "news",
++ "status": "active",
++ "visibility": "first-party",
++ "description": "Attach a source item to a story's source table (numbered, never renumbered) or detach it.",
++ "permissions": [
++ "news:write"
++ ],
++ "resourceConstraints": [
++ "none"
++ ],
++ "quotaClass": "news-write",
++ "events": [],
++ "implementedBy": [
++ "POST /api/v1/stories/:id/sources",
++ "DELETE /api/v1/stories/:id/sources/:item"
++ ]
++}
ddiff --git a/manifests/capabilities/news.story.create.json b/manifests/capabilities/news.story.create.json
nnew file mode 100644
iindex 00000000..7e5e2833
---- /dev/null
++++ b/manifests/capabilities/news.story.create.json
@@@ -0,0 +1,21 @@
++{
++ "id": "news.story.create",
++ "version": "1.0",
++ "owner": "news",
++ "status": "active",
++ "visibility": "first-party",
++ "description": "Open a story from a cluster (the cluster's live, non-duplicate source items become its first sources). Editors only: the service acts for an editor named in X-OV-Subject.",
++ "permissions": [
++ "news:write"
++ ],
++ "resourceConstraints": [
++ "none"
++ ],
++ "quotaClass": "news-write",
++ "events": [
++ "news.story.created"
++ ],
++ "implementedBy": [
++ "POST /api/v1/stories"
++ ]
++}
ddiff --git a/manifests/capabilities/news.story.publish.json b/manifests/capabilities/news.story.publish.json
nnew file mode 100644
iindex 00000000..caf9c91d
---- /dev/null
++++ b/manifests/capabilities/news.story.publish.json
@@@ -0,0 +1,26 @@
++{
++ "id": "news.story.publish",
++ "version": "1.0",
++ "owner": "news",
++ "status": "active",
++ "visibility": "first-party",
++ "description": "Publish a revision (refused while it cites no source, rests on a source removed upstream or detached, or is AI-generated/AI-assisted without a person's approval), or unpublish a story. Publishing resolves the upstream flags the revision answers and publishes pending correction/update notes.",
++ "permissions": [
++ "news:write"
++ ],
++ "resourceConstraints": [
++ "none"
++ ],
++ "quotaClass": "news-write",
++ "events": [
++ "news.story.published",
++ "news.story.updated",
++ "news.story.unpublished",
++ "news.index_document.upserted",
++ "news.index_document.deleted"
++ ],
++ "implementedBy": [
++ "POST /api/v1/stories/:id/publish",
++ "POST /api/v1/stories/:id/unpublish"
++ ]
++}
ddiff --git a/manifests/capabilities/news.story.read.json b/manifests/capabilities/news.story.read.json
nnew file mode 100644
iindex 00000000..37fa8c02
---- /dev/null
++++ b/manifests/capabilities/news.story.read.json
@@@ -0,0 +1,22 @@
++{
++ "id": "news.story.read",
++ "version": "1.0",
++ "owner": "news",
++ "status": "active",
++ "visibility": "first-party",
++ "description": "Read any story, including drafts, revisions, diffs, publishability problems and editorial flags. Published stories need no capability.",
++ "permissions": [
++ "news:read"
++ ],
++ "resourceConstraints": [
++ "none"
++ ],
++ "quotaClass": "news-read",
++ "events": [],
++ "implementedBy": [
++ "GET /api/v1/stories/:id",
++ "GET /api/v1/stories/:id/revisions",
++ "GET /api/v1/stories/:id/revisions/:n",
++ "GET /api/v1/stories/:id/diff"
++ ]
++}
ddiff --git a/manifests/capabilities/news.story.retract.json b/manifests/capabilities/news.story.retract.json
nnew file mode 100644
iindex 00000000..b46cb465
---- /dev/null
++++ b/manifests/capabilities/news.story.retract.json
@@@ -0,0 +1,22 @@
++{
++ "id": "news.story.retract",
++ "version": "1.0",
++ "owner": "news",
++ "status": "active",
++ "visibility": "first-party",
++ "description": "Retract a published story with a public note. The story stays at its URL with the retraction notice, becomes noindex and leaves sitemaps and Search. Final.",
++ "permissions": [
++ "news:write"
++ ],
++ "resourceConstraints": [
++ "none"
++ ],
++ "quotaClass": "news-write",
++ "events": [
++ "news.story.retracted",
++ "news.index_document.deleted"
++ ],
++ "implementedBy": [
++ "POST /api/v1/stories/:id/retract"
++ ]
++}
ddiff --git a/manifests/capabilities/news.story.revise.json b/manifests/capabilities/news.story.revise.json
nnew file mode 100644
iindex 00000000..a6d8a1fb
---- /dev/null
++++ b/manifests/capabilities/news.story.revise.json
@@@ -0,0 +1,21 @@
++{
++ "id": "news.story.revise",
++ "version": "1.0",
++ "owner": "news",
++ "status": "active",
++ "visibility": "first-party",
++ "description": "Save a new immutable revision (headline and paragraphs that each cite attached sources, with the source table, timeline and perspectives snapshotted), add correction/update notes, or request an OpenVibe.AI draft. With X-OV-Origin: ai the revision is AI-generated and cannot be published until a person approves it.",
++ "permissions": [
++ "news:write"
++ ],
++ "resourceConstraints": [
++ "none"
++ ],
++ "quotaClass": "news-write",
++ "events": [],
++ "implementedBy": [
++ "POST /api/v1/stories/:id/revisions",
++ "POST /api/v1/stories/:id/ai-drafts",
++ "POST /api/v1/stories/:id/flags"
++ ]
++}
ddiff --git a/manifests/capabilities/news.timeline.update.json b/manifests/capabilities/news.timeline.update.json
nnew file mode 100644
iindex 00000000..25138b38
---- /dev/null
++++ b/manifests/capabilities/news.timeline.update.json
@@@ -0,0 +1,20 @@
++{
++ "id": "news.timeline.update",
++ "version": "1.0",
++ "owner": "news",
++ "status": "active",
++ "visibility": "first-party",
++ "description": "Add or remove a story's timeline entries; each entry has a date the source states and rests on one attached source.",
++ "permissions": [
++ "news:write"
++ ],
++ "resourceConstraints": [
++ "none"
++ ],
++ "quotaClass": "news-write",
++ "events": [],
++ "implementedBy": [
++ "POST /api/v1/stories/:id/timeline",
++ "DELETE /api/v1/stories/:id/timeline/:eid"
++ ]
++}
ddiff --git a/manifests/capabilities/news.topic.manage.json b/manifests/capabilities/news.topic.manage.json
nnew file mode 100644
iindex 00000000..176a05b0
---- /dev/null
++++ b/manifests/capabilities/news.topic.manage.json
@@@ -0,0 +1,19 @@
++{
++ "id": "news.topic.manage",
++ "version": "1.0",
++ "owner": "news",
++ "status": "active",
++ "visibility": "first-party",
++ "description": "Add a canonical topic (slug and name).",
++ "permissions": [
++ "news:write"
++ ],
++ "resourceConstraints": [
++ "none"
++ ],
++ "quotaClass": "news-write",
++ "events": [],
++ "implementedBy": [
++ "POST /api/v1/topics"
++ ]
++}
ddiff --git a/manifests/services/news.json b/manifests/services/news.json
iindex 927bd6c7..2302f79d 100644
---- a/manifests/services/news.json
++++ b/manifests/services/news.json
@@@ -1,16 +1,52 @@
{
"version": "0.1.0",
"contractRanges": {
-- "openvibe-contracts": "^0.1.0"
++ "openvibe-contracts": ">=0.19.0 <1.0.0"
},
"id": "news",
"name": "OpenVibe.News",
-- "status": "placeholder",
++ "status": "alpha",
"repository": "OpenVibers/OpenVibe.News",
-- "domains": [],
-- "capabilities": [],
-- "eventsProduced": [],
-- "eventsConsumed": [],
-- "namespacesOwned": [],
-- "notes": "Source-backed stories: clustered coverage, cited summaries, perspectives and timelines. Charter only; no runtime."
++ "domains": [
++ "openvibe.news"
++ ],
++ "publicOrigin": "https://openvibe.news",
++ "health": "/api/health",
++ "ready": "/api/ready",
++ "capabilities": [
++ "news.story.create",
++ "news.story.read",
++ "news.story.revise",
++ "news.story.publish",
++ "news.story.retract",
++ "news.cluster.read",
++ "news.cluster.manage",
++ "news.source.attach",
++ "news.timeline.update",
++ "news.perspective.update",
++ "news.topic.manage"
++ ],
++ "eventsProduced": [
++ "news.source.ingested",
++ "news.source.failed",
++ "news.cluster.updated",
++ "news.story.created",
++ "news.story.flagged",
++ "news.story.published",
++ "news.story.updated",
++ "news.story.unpublished",
++ "news.story.retracted",
++ "news.index_document.upserted",
++ "news.index_document.deleted"
++ ],
++ "eventsConsumed": [
++ "sources.item.created",
++ "sources.item.updated",
++ "sources.item.removed",
++ "sources.fetch.failed"
++ ],
++ "namespacesOwned": [
++ "news.*"
++ ],
++ "notes": "Port 4820. Owns news_topics, news_source_items, news_story_clusters, news_stories, news_story_revisions, news_story_sources, news_perspectives, news_timeline_entries, news_editorial_flags in its own SQLite (openvibe-publishing packages, prefixes news_story and news). Consumes OpenVibe.Sources (sources.item.read, optionally sources.source.read) through an Events subscription (sources.item.*, sources.fetch.failed; events.subscription.manage) and a cursor pull; OpenVibe.Network (SSO, identity.subject.resolve); optionally OpenVibe.AI (ai.run.create, namespace news) for news.summarize_story and news.compare_perspectives drafts; publishes through OpenVibe.Events (events.event.publish); OpenVibe.Search reads news.index_document.* from Events."
}
ddiff --git a/package.json b/package.json
iindex 2927089e..52f00916 100644
---- a/package.json
++++ b/package.json
@@@ -1,6 +1,6 @@
{
"name": "openvibe-contracts",
-- "version": "0.20.0",
++ "version": "0.21.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",