openvibers/openvibe.wiki Commits

AGENTS.md: the context an agent starts from (#301)

OP OpenVibers committed 1 days ago · 05dee8f901 · 2 files, +101 −0

Adds `AGENTS.md` (111 lines) as the context an agent starts from, plus `CLAUDE.md` (`@AGENTS.md`).

It covers: Wiki as spaces/page trees with immutable revisions; the ten `wiki_*` authority tables and
what stays in Community/Media/Sources/Search/Network/VIP; the person/service token and space-role
model; the `ov_wiki` migrations and trigger-enforced immutability; AI proposals and the unreviewed
seed/noindex rule; deploy; tests; conventions; and the immutable-revision, citation-provenance,
external-thread/media, slug-redirect and Search-downstream pitfalls.

Checked: every cited path exists on main; no code, dependency or version changed.

Changes

ddiff --git a/AGENTS.md b/AGENTS.md
nnew file mode 100644
iindex 0000000..1ef781d
---- /dev/null
++++ b/AGENTS.md
@@@ -0,0 +1,100 @@
++# AGENTS.md — OpenVibe.Wiki
++
++## Project overview
++
++OpenVibe.Wiki (`https://openvibe.wiki`) is the network's knowledge product: wiki spaces (official
++editorial and community), page trees with canonical slugs, immutable revisions with diff and revert,
++typed infoboxes, `[[internal links]]`, citations bound to the revision that used them, Media
++attachments and Community discussion — server-rendered and useful without JavaScript.
++
++- **Entry:** `server/index.js`; app `server/app.js`; port `4800`, service id `wiki`.
++- **API/pages:** `server/http/api.js` (`/api/v1`), `pages.js`, `machine.js`, `openapi.js`,
++ `actor-limits.js`, `common.js`, `frame-groups.js`; rendering `server/render/` (`layout.js`,
++ `views.js`, `blocks.js`, `short.js`); public CSS/JS in `public/`.
++- **Wiki domain:** `server/wiki/` — `service.js`, `content.js`, `writer.js`, `access.js`,
++ `images.js`, `review-kind.js`, `import.js`, `seed.js`, `vibe.js`, `account-data.js`.
++- **Integrations:** `server/integrations/` — `platform.js` (Network/SSO), `image-fetch.js`,
++ `projects-module.js`, `vip.js` (gated spaces/pages).
++- **Docs:** `docs/threat-review.md`; seeds in `seeds/` and `scripts/seed.js`; `STATUS.json`.
++
++## Owns, does not own
++
++Owns the ten authority tables in `ov_wiki`: `wiki_spaces` (official/user, public/members/private),
++`wiki_pages` (canonical identity, slug, tree via `parent_id`, publication state, published revision),
++`wiki_page_revisions` (**immutable**; PL/pgSQL triggers refuse UPDATE/DELETE), plus the page
++content/blocks, citations, discussion refs, media refs, review queue, shares, proposals and outbox.
++
++Does not own: discussion (Community owns threads; Wiki stores only `wiki_discussion_refs` and
++renders/posts through Community); attachments (Media owns bytes; Wiki stores `med_…` ids and their
++last known state); source items (Sources; a citation stores the item id plus URL, title, retrieval
++time and licence read from its provenance); the network index (Search; Wiki sends
++`wiki.index_document.*`). It also does not own identity (Network), entitlements (VIP/Billing) or the
++projects module (Services).
++
++## Who calls it, what it calls
++
++People use their Network JWT (Bearer, or the `ov_token` cookie; cross-site cookie writes are refused);
++services use client-credentials tokens for audience `openvibe.wiki` with one capability per route, and
++content writes need the person in `X-OV-Subject` (whose space role applies). SDK `openvibe-sdk/wiki`
++is the read client; `openvibe-sdk/community` is used for discussion. Wiki calls Network (SSO, JWKS,
++names), Community (threads), Media (upload/read), Sources (citation provenance), Search (index events
++out), Events (outbox), AI (`wiki` proposals/drafts) and VIP (gated spaces/pages). AI proposals are
++drafts: a person reviews and publishes them.
++
++## Database and migrations
++
++PostgreSQL `ov_wiki` through `openvibe-sdk/db` (PgBouncer for the app, direct owner role for
++migrations); `migrations/0001..0009` cover spaces/pages/revisions, review kinds, shares, the outbox
++rejection index and defaults. Applied at boot; never edit one that has run. Revision immutability is
++enforced by database triggers, not only by code. Tests use PGlite or `WIKI_TEST_STORE=pg`; a migration
++that a rollback cannot tolerate is not allowed.
++
++## Auth and privacy
++
++Network RS256 JWTs (offline JWKS) and SSO. A person's space role decides writes; services act for a
++person only with `X-OV-Subject` and the capability. Visitors without SSO read public content only; a
++members/private space is gated through VIP and the entitlement check. Cross-site cookie writes are
++refused. AI generates proposals/drafts that need a person's approval before publication; every seed
++page is currently `noindex` because no person has reviewed it, and none is in Search. Public pages are
++cacheable; a signed-in page is `private, no-store`.
++
++## Deploy
++
++`sudo ovhost deploy wiki` (git-checkout: fast-forward `/opt/openvibe.wiki`, install on a lockfile
++change, restart, wait for `/api/ready`; rollback `sudo ovhost rollback wiki --to <sha>`). Unit
++`openvibe-wiki.service` on `127.0.0.1:4800`, env `/etc/openvibe/wiki.env`, database `ov_wiki`, nginx
++`deploy/nginx/openvibe.wiki.conf`; the release migrates at boot.
++
++## Commands and tests
++
++```bash
++npm ci
++npm run dev # http://127.0.0.1:4800
++npm test # every test/*.test.js, temp PGlite + stub upstreams
++npm test -- revisions seo # only matching files
++npm run test:pg # the same on PostgreSQL
++npm run seed # the official space's seed pages
++```
++
++`test/run.js` uses `openvibe-shared/test-runner` (60 s, serial): temporary PGlite, a generated RSA key
++and stub upstreams, never the network. Coverage includes page trees/slugs, immutable revisions and
++diff/revert, blocks/infoboxes/internal links, citations and the citation inspector, Media and
++Community integration, access/VIP/delegation, AI proposals/review, discovery/SEO, account data and the
++empty states.
++
++## Conventions and pitfalls
++
++- CommonJS, 4-space indent, single quotes, semicolons; RFC 9457 problem+json, escaped HTML and a safe
++ block renderer (user content never becomes raw markup).
++- **A revision is immutable.** Publish a new revision; never UPDATE/DELETE a `wiki_page_revisions` row
++ or bypass the trigger. Diff/revert and citations are computed from revisions.
++- **Citations are provenance, not decoration:** store the Sources item id, URL, title, retrieval time
++ and licence; a changed/removed source must be visible as such, never silently rewritten.
++- **Discussion and bytes live elsewhere:** store the Community thread id / Media `med_` id, verify the
++ access with the owning service, and never copy a thread or a file into Wiki.
++- **Seed/AI text is unreviewed until a person approves it:** keep `noindex` and out of Search until
++ reviewed; AI drafts are proposals, not publications.
++- **Slug and tree changes are redirects:** a moved page keeps its canonical identity and leaves a
++ redirect; `[[internal links]]` resolve through the canonical slug.
++- **Search is downstream:** publish/update/remove through `wiki.index_document.*` in the transaction
++ (or the outbox), never write Search's tables.
ddiff --git a/CLAUDE.md b/CLAUDE.md
nnew file mode 100644
iindex 0000000..43c994c
---- /dev/null
++++ b/CLAUDE.md
@@@ -0,0 +1 @@