# AGENTS.md — OpenVibe.Publishing ## Project Overview OpenVibe.Publishing is the shared **library** of publishing behaviour for the OpenVibe publication products (Wiki, Blog, News, Reviews, Deals, Coupons, Trade) and for the record products that use its Search feed/relay: revisions, scheduling, taxonomy, citations, media/discussion references, the indexability gate, feeds/sitemaps, authorship and AI disclosure, Search documents and envelopes, server-rendered page layout, the block model, the Sources ingest chassis and the publication glue. - **Package:** `openvibe-publishing` · **Version:** 1.7.1 · **Runtime:** CommonJS, Node ≥20 (production Node 22) · **License:** MIT - **It is code, never state.** It has no runtime, port, domain, database, API, migration directory or deploy of its own. It must never get one. Products pin the release tarball; the state lives in each product's database with that product's table prefix. - **Read first:** [README.md](README.md), [docs/MIGRATION.md](docs/MIGRATION.md), [CHANGELOG.md](CHANGELOG.md), [examples/two-products/](examples/two-products/). **Network context:** the package implements **no capability and holds no grant**. Modules call services with the **product's** token client: `discussion` → OpenVibe.Community `community.comment.write`; `ai` → OpenVibe.AI `ai.run.create`/`ai.run.read`; `media.verify()` → OpenVibe.Media `media.object.read`; `ingest` → OpenVibe.Sources `sources.item.read`/`sources.source.read`. It stores references, never comment content, media bytes or source items. ## Commands ```bash npm ci # install (devDependencies include contracts; peers come from the consumer) npm test # every test/*.test.js on a fresh in-process PGlite schema, no network eval "$(node_modules/openvibe-sdk/scripts/test-services.sh up)" && npm test # PostgreSQL 18 + PgBouncer, as CI node --check # syntax check (no linter configured) ``` There is no start/dev/build/deploy script. A release is a git tag `vX.Y.Z` recorded in [CHANGELOG.md](CHANGELOG.md); products pin the tag's tarball and ship it with their own `sudo ovhost deploy `. A bad release is undone by the products pinning the previous tag. ## Module Map Each entry point loads alone (`require('openvibe-publishing/')`; the root export also exposes them lazily). | Module | What it does | Tables (in the product's DB, `_…`) | |---|---|---| | `revisions` | drafts, immutable revisions with parent pointers, optimistic concurrency (412 `revision.conflict`), diff, revert-as-new-revision, audited purge | `_revisions`, `_drafts`, `_revision_purges` | | `schedule` | idempotent scheduled publish/unpublish, lease-based claim, retries/backoff | `_schedule_jobs` | | `taxonomy` | tags/terms, hierarchical categories, cycle checks | `_terms`, `_term_links` | | `citations` | append-only Sources item/URL references, retrieved_at, quote span, licence note, carry-forward | `_citations`, `_citation_purges` | | `media` | attachments by `med_…`/`legacy:…`, explicit `broken` state, `verify()` never flips on an outage, `
` with an honest placeholder | `_attachments` | | `discussion` | Community thread references (thread id only) | `_discussion_refs` | | `seo` | deterministic indexability gate, canonical URLs, slug redirects (301/410), meta/robots, sitemaps, RSS/Atom/JSON Feed, JSON-LD from given fields | `_redirects` | | `authorship` | human/ai/hybrid/imported records, workflow + run id, disclosure labels, AI held as draft + noindex until review | `_reviews` | | `index-hooks` | `search.index-document@1` docs/tombstones, `.index_document.*` events, monotonic sequencer, product publish events | `_index_revisions` | | `ai` | run a registered OpenVibe.AI workflow, poll a slow run, read citations, coded `AiRunError`s | — | | `ssr` | auto-escaping `html` templates + `raw`, safe Markdown subset, text extraction, pagination, breadcrumbs, diff, `