From 08ca4e186ec3ab783cf8b543317a31378955c464 Mon Sep 17 00:00:00 2001 From: Lichen Dev Agent Date: Sat, 22 Aug 2026 22:44:22 +0000 Subject: [PATCH] Open wiki M1: contribution envelope is a strongRef to note + noteRevision Envelope no longer inlines title/content/message; the appview resolves the noteRevision against its parent and shows diffs from the submitter's records. --- docs/open-wiki-plan.md | 23 +++++++++--------- lexicons/wiki.lichen.contribution.json | 32 ++++++++------------------ src/server/db/schema.ts | 6 ++--- 3 files changed, 24 insertions(+), 37 deletions(-) diff --git a/docs/open-wiki-plan.md b/docs/open-wiki-plan.md index 349663d..5155faf 100644 --- a/docs/open-wiki-plan.md +++ b/docs/open-wiki-plan.md @@ -40,21 +40,20 @@ From there each submission can be **approved** (goes live for everyone) and opti - Default off → existing behavior unchanged (backwards compatible). ### 3.2 New lexicon — `wiki.lichen.contribution` (pending submission) -Authored by the **submitter on their PDS** (key: TID). Represents a proposed change. +Authored by the **submitter on their PDS** (key: TID). A lightweight envelope whose proposed +change lives on the submitter's repo and is fetched/diffed by the appview — no inline content. ``` -WikiRef (at-uri) — target wiki -kind "create-note" | "edit-note" -noteRef (at-uri) — required: the note on the submitter's PDS holding the - proposed change (new note for create-note; existing note for edit-note) -title string -content string -message? string — optional change note -createdAt datetime +wikiRef (at-uri) — target wiki +kind "create-note" | "edit-note" +note strongRef → wiki.lichen.note (the note holding the change) +noteRevision strongRef → wiki.lichen.noteRevision (the proposed diff) +createdAt datetime ``` **Design choice (recommended):** the submitter writes the underlying `note` (+`noteRevision`) -records to their own PDS *immediately* at submit time, and the `contribution` record ties them -together. The appview holds the note as **pending**, not live, until approved. This keeps content -on the submitter's repo (portable, matches Lichen's model). See Open Questions. +records to their own PDS *immediately* at submit time, and the `contribution` record envelopes +them via strongRefs. The appview shows diffs by resolving the noteRevision against its parent. +The note is held pending (not live) until approved — content stays on the submitter's repo +(portable, matches Lichen's model). See Open Questions. ### 3.3 New lexicon — `wiki.lichen.contributionApproval` (decision) Authored by the **owner/admin on their PDS** when they act on a submission. diff --git a/lexicons/wiki.lichen.contribution.json b/lexicons/wiki.lichen.contribution.json index a971b6e..0828d38 100644 --- a/lexicons/wiki.lichen.contribution.json +++ b/lexicons/wiki.lichen.contribution.json @@ -4,11 +4,11 @@ "defs": { "main": { "type": "record", - "description": "A proposed change to an open wiki, submitted by a non-contributor and held pending until an owner/admin approves it. The submitter writes the underlying note (and revision) to their own PDS at submit time; this record identifies and describes that proposed change.", + "description": "A proposed change to an open wiki, submitted by a non-contributor and held pending until an owner/admin approves it. The submitter writes the underlying wiki.lichen.note and wiki.lichen.noteRevision to their own PDS at submit time; this record carries strong refs to those records so the appview can fetch and diff the change. Nothing in this envelope affects the live wiki until approved.", "key": "tid", "record": { "type": "object", - "required": ["wikiRef", "kind", "noteRef", "title", "content", "createdAt"], + "required": ["wikiRef", "kind", "note", "noteRevision", "createdAt"], "properties": { "wikiRef": { "type": "string", @@ -21,27 +21,15 @@ "knownValues": ["create-note", "edit-note"], "description": "Whether this proposes a brand-new note or an edit to an existing note." }, - "noteRef": { - "type": "string", - "format": "at-uri", - "description": "AT-URI of the note on the submitter's PDS holding the proposed change. For kind=create-note this is the newly created note; for kind=edit-note this is the existing note being edited." - }, - "title": { - "type": "string", - "maxGraphemes": 256, - "maxLength": 2560, - "description": "Proposed title for the note." + "note": { + "type": "ref", + "ref": "com.atproto.repo.strongRef", + "description": "Strong ref to the wiki.lichen.note record holding this change. For kind=create-note this is the newly created note; for kind=edit-note this is the existing note being edited." }, - "content": { - "type": "string", - "maxLength": 1000000, - "description": "Proposed body content for the note." - }, - "message": { - "type": "string", - "maxGraphemes": 1024, - "maxLength": 10240, - "description": "Optional note describing this proposed change." + "noteRevision": { + "type": "ref", + "ref": "com.atproto.repo.strongRef", + "description": "Strong ref to the wiki.lichen.noteRevision record that proposes this change. Diffs against the revision's parent give the exact change." }, "createdAt": { "type": "string", diff --git a/src/server/db/schema.ts b/src/server/db/schema.ts index 24db53e..fd1b52e 100644 --- a/src/server/db/schema.ts +++ b/src/server/db/schema.ts @@ -193,6 +193,8 @@ export function initSchema(db: Database): void { // Open-contribution review queue. Mirrors wiki.lichen.contribution records; // holds a proposed change pending until an owner/admin approves or rejects it. + // The change's content is NOT stored here — note_at_uri + note_revision_at_uri + // point at the submitter's records on their PDS, fetched for the diff view. db.run(` CREATE TABLE IF NOT EXISTS pending_contributions ( id INTEGER PRIMARY KEY AUTOINCREMENT, @@ -200,10 +202,8 @@ export function initSchema(db: Database): void { wiki_at_uri TEXT NOT NULL REFERENCES wikis(at_uri) ON DELETE CASCADE, wiki_slug TEXT NOT NULL, note_at_uri TEXT NOT NULL, + note_revision_at_uri TEXT NOT NULL, kind TEXT NOT NULL CHECK (kind IN ('create-note', 'edit-note')), - title TEXT NOT NULL, - content TEXT NOT NULL, - message TEXT, submitter_did TEXT NOT NULL, status TEXT NOT NULL DEFAULT 'pending' CHECK (status IN ('pending', 'approved', 'rejected')), approved_at TEXT, -- 2.51.2