openvibers/openvibe.publishing Commits

Forward the AI summary, facts, updated and url through renderDocument

OP OpenVibers committed 4 Oct 2026 · ad320138d4 · 5 files, +72 −1

Asked: T11 step 4a — OpenVibe.Publishing: teach layout.renderDocument the AI summary and release v1.3.0. ONE pull request from current origin/main (git fetch first; the local checkout is stale). In lib/layout.js renderDocument (docstring near lines 38-45, the shell.page call near line 90) pass four option…
Harness job publishing-t11-step-4a-openvibe-publishi-210537.

Changes

ddiff --git a/CHANGELOG.md b/CHANGELOG.md
iindex 68c098e..7d04b4b 100644
---- a/CHANGELOG.md
++++ b/CHANGELOG.md
@@@ -4,6 +4,24 @@ All notable changes to `openvibe-publishing`. Versions follow [semver](https://s
breaking change to any exported function, table layout, reason code or document shape is a new
major (a minor while 0.x). A release is the git tag `vX.Y.Z`; consumers pin the tag's tarball.
++## 1.3.0 — 2026-10-04
++
++**Additive: `layout.renderDocument` now forwards the AI summary to the shell.** No existing export,
++table layout, reason code or document shape changes; a product on v1.2.0 upgrades by pinning v1.3.0.
++Every option defaults to what the product already renders, so a product that passes none of them is
++byte-identical.
++
++### Changed — `openvibe-publishing/layout`
++
++- `renderDocument(o)` passes four options through to `shell.page`, which builds them with
++ `seo.pageSummary`: **`summary`** (one-line AI summary), **`facts`** (`[[key, value], string]` or
++ `[string]` rows), **`updated`** (ISO date) and **`url`** (absolute; defaults to the document's
++ canonical). With a summary the document carries `<meta name="ai-summary">` and a `WebPage` JSON-LD
++ tag in `<head>`, and with facts the `<noscript><section data-ai-summary>` block at the top of
++ `<body>`; `updated` becomes `dateModified`. Nothing is invented: without a summary none of that is
++ emitted, an omitted fact or date is left out of the block, and `url` is only ever the caller's or
++ the canonical's. Styling is unchanged.
++
## 1.2.0 — 2026-10-04
**Additive: one new entry point, `openvibe-publishing/layout`.** No existing export, table layout, reason
ddiff --git a/docs/MIGRATION.md b/docs/MIGRATION.md
iindex 36579e9..07c31ec 100644
---- a/docs/MIGRATION.md
++++ b/docs/MIGRATION.md
@@@ -53,6 +53,27 @@ The five content products (News, Reviews, Deals, Coupons, Trade) each delete the
- **Test:** the product's own suite (`ov test`), including its ingest and publication tests; the
Publishing side is `test/ingest.test.js` and `test/publication.test.js`.
++## v1.2.0 → v1.3.0
++
++Additive: `layout.renderDocument` forwards the AI summary to the shell. Everything else is unchanged,
++and a product that passes none of the four options renders exactly as on v1.2.0.
++
++- **package.json:** `"openvibe-publishing": "https://codeload.github.com/OpenVibers/OpenVibe.Publishing/tar.gz/refs/tags/v1.3.0"`.
++ `openvibe-shared` stays at v2.6.0 or later (that is where `seo.pageSummary` and the shell's `summary`
++ support already live).
++- **Lockfile:** copy the `node_modules/openvibe-publishing` entry (`1.3.0`, the matching `resolved`,
++ the real `integrity`) from a product already on the tag.
++- **What changes in the product:** pass `summary` on the home render (and on any other page that has
++ a real one-line summary, e.g. an article or a review), so the `ai-summary` meta lands on the page.
++ - `layout.renderDocument({ …, summary, facts, updated, url })` — `summary` is the one-line AI
++ summary, `facts` the `[[key, value], string]` (or `[string]`) rows, `updated` the ISO date, `url`
++ absolute and defaulting to the canonical.
++ - `summary` alone gives `<meta name="ai-summary">` plus a `WebPage` JSON-LD tag; `facts` gives the
++ `<noscript><section data-ai-summary>` block; `updated` becomes `dateModified`.
++ - **Only pass what the facts support.** A missing rating, price, date or author is left out of the
++ facts block, never defaulted — the same rule as every other structured output here.
++- **Test:** the product's page/render tests (`ov test`); Publishing's side is `test/layout.test.js`.
++
## v1.1.0 → v1.2.0
Adds `openvibe-publishing/layout`; nothing existing changes. The seven publication sites each hand-wrote
ddiff --git a/lib/layout.js b/lib/layout.js
iindex 7649b3c..04d110c 100644
---- a/lib/layout.js
++++ b/lib/layout.js
@@@ -37,6 +37,8 @@ function json(value) {
* o: site (service id, e.g. 'news'), service (defaults to site), siteName ('OpenVibe.News'), lang,
* title (composed by the caller), description, canonical (absolute), type, image, imageAlt,
* author, keywords, locale, twitterSite, jsonLd [], prev, next, published, modified,
++ * summary (one-line AI summary), facts ([[key, value], string] or [string]), updated (ISO date),
++ * url (absolute; defaults to the canonical),
* decision (or robots), feeds [{ type, href, title }], navbar, footer, navLinks, home,
* css (stylesheet href), styles [openvibe-shared/serve names], scripts [<script> HTML],
* referrer, release, head (extra head HTML), iconSite, skipLabel, account (noscript account-bar
@@@ -98,6 +100,14 @@ document.addEventListener('DOMContentLoaded', function () {
description: o.description,
canonical,
robots,
++ // The shell builds the AI summary itself (seo.pageSummary): `summary` puts the ai-summary
++ // meta and a WebPage JSON-LD tag in <head>, `facts` the <noscript data-ai-summary> block at
++ // the top of <body>, and `url`/`updated` feed it. Nothing is invented: without a summary the
++ // shell emits none, and an omitted fact or date is left out of the block.
++ summary: o.summary || undefined,
++ facts: o.facts || undefined,
++ updated: o.updated || undefined,
++ url: o.url || canonical,
type: o.type || 'website',
image: o.image || undefined,
imageAlt: o.imageAlt || undefined,
ddiff --git a/package.json b/package.json
iindex 6814173..b1f60ac 100644
---- a/package.json
++++ b/package.json
@@@ -1,6 +1,6 @@
{
"name": "openvibe-publishing",
-- "version": "1.2.0",
++ "version": "1.3.0",
"description": "Shared publishing packages for the OpenVibe publication products, on PostgreSQL through openvibe-sdk/db: revisions, scheduling, taxonomy, citations, media attachments, discussion references, the SEO indexability gate, authorship, index hooks, SSR helpers, the ingest chassis (Sources client, change cursor, signed event consumer, normalisers, PSL) and the publication glue. Packages, not an authority.",
"main": "index.js",
"exports": {
ddiff --git a/test/layout.test.js b/test/layout.test.js
iindex ff181d1..3c75235 100644
---- a/test/layout.test.js
++++ b/test/layout.test.js
@@@ -85,4 +85,26 @@ test('no release, referrer, css or account: those extras are left out', () => {
assert.match(out, /window\.__OV_PAGE = \{"navbar":\{"service":"blog"\},"footer":\{"service":"blog","variant":"compact"\}\}/);
});
++test('summary reaches the shell, so the page carries the ai-summary meta, the facts block and the url', () => {
++ const out = renderDocument(doc({
++ summary: 'A story, in one line.', facts: [['Rating', '4/5'], ['Published', '2026-10-01']],
++ updated: '2026-10-02T09:00:00Z',
++ }));
++ assert.match(out, /<meta name="ai-summary" content="A story, in one line\.">/);
++ assert.match(out, /<noscript><section data-ai-summary><h2>A story · OpenVibe\.News<\/h2><p>A story, in one line\.<\/p><ul><li>Rating: 4\/5<\/li><li>Published: 2026-10-01<\/li><\/ul><\/section><\/noscript>/);
++ assert.match(out, /"dateModified":"2026-10-02T09:00:00\.000Z"/);
++ assert.match(out, /"url":"https:\/\/openvibe\.news\/a\/original"/);
++});
++
++test('without a summary no ai-summary meta, facts block or WebPage tag is invented', () => {
++ const out = renderDocument(doc());
++ assert.doesNotMatch(out, /ai-summary|data-ai-summary|"@type":"WebPage"/);
++});
++
++test('url defaults to the canonical, so an explicit url wins', () => {
++ const own = renderDocument(doc({ summary: 'S', url: 'https://openvibe.news/a/explicit' }));
++ assert.match(own, /"url":"https:\/\/openvibe\.news\/a\/explicit"/);
++ assert.doesNotMatch(own, /"url":"https:\/\/openvibe\.news\/a\/original"/);
++});
++
run();