Moderation: versions of works and the L2 → L1 → admin pipeline #54

Merged
anas merged 1 commits from feature/moderation-pipeline into main 2026-10-08 22:34:51 +00:00
Owner

Closes #31. Part of #51 (version history; other entities and tags follow). Builds on #53 (Divan-owned content): merge #53 first, or this one, which contains it.

How it works

  1. A moderator who may edit a work (their grants) sees ترمیم کریں and تاریخچہ on it. Edit opens their draft: one open draft per person and work, starting from the current text.

  2. The revision page has:

    • the Divan text in an editor box, with the Urdu keyboard, Roman typing and an edit summary;
    • a line diff against the text the draft started from;
    • a preview laid out like the site;
    • the timeline of every step.
  3. L2 submits for review. An L1 whose grants cover the work approves it, or returns or rejects it (a reason is required). An admin publishes. L1 drafts go straight to the admin, and an admin's own edits publish directly.

  4. Publishing:

    • numbers the version (۱، ۲، …);
    • writes it to divan-data's divan/ folder as Divan-owned content (author, reviewer and publisher recorded);
    • updates the site at once.

    If another version was published after the draft started, publishing is refused, so nothing is silently overwritten.

  5. /mod: my drafts, drafts to review, drafts to publish, and سرگرمی (who did what across the site). /mod/work/: a work's versions with author, reviewer, publisher, summary and dates.

Data: revisions and revision_events in db/schema.sql. Radif, matla, maqta and contents order of an edited work are recomputed by the next daily export and import.

Tested

  • npm test: 35 pass. The pipeline test covers:

    • permissions: readers and out-of-scope moderators refused, L2 cannot approve;
    • a return with its required reason, a resubmit and an approval;
    • version 1 written with author, reviewer and publisher;
    • the event trail in order;
    • an admin's own edit publishing directly, and an L1 draft going to the admin;
    • the publish conflict refused.

    The test restores the work it edits.

  • Headless Chrome, three people, against a temporary data folder:

    1. the L2 moderator edits one misra of Ghalib's ghazal and submits (one-line diff);
    2. the L1 moderator approves from their queue;
    3. the admin publishes version ۱;
    4. the poem page shows the new misra, and the history and timeline are complete.

    The test edit and accounts were removed afterwards.

Next: git commits on publish (#34), the Tiptap editor (#28), arranging (#41), tags (#52), history for poets, intros, books and the dictionary.

🤖 Generated with Claude Code

Closes #31. Part of #51 (version history; other entities and tags follow). **Builds on #53** (Divan-owned content): merge #53 first, or this one, which contains it. **How it works** 1. A moderator who may edit a work (their grants) sees **ترمیم کریں** and **تاریخچہ** on it. Edit opens their draft: one open draft per person and work, starting from the current text. 2. The **revision page** has: - the Divan text in an editor box, with the Urdu keyboard, Roman typing and an edit summary; - a **line diff** against the text the draft started from; - a **preview** laid out like the site; - the **timeline** of every step. 3. **L2** submits for review. An **L1** whose grants cover the work approves it, or returns or rejects it (a reason is required). An **admin** publishes. L1 drafts go straight to the admin, and an admin's own edits publish directly. 4. **Publishing**: - numbers the version (۱، ۲، …); - writes it to divan-data's `divan/` folder as Divan-owned content (author, reviewer and publisher recorded); - updates the site at once. If another version was published after the draft started, publishing is refused, so nothing is silently overwritten. 5. **/mod**: my drafts, drafts to review, drafts to publish, and **سرگرمی** (who did what across the site). **/mod/work/<id>**: a work's versions with author, reviewer, publisher, summary and dates. **Data**: `revisions` and `revision_events` in `db/schema.sql`. Radif, matla, maqta and contents order of an edited work are recomputed by the next daily export and import. **Tested** - `npm test`: 35 pass. The pipeline test covers: - permissions: readers and out-of-scope moderators refused, L2 cannot approve; - a return with its required reason, a resubmit and an approval; - version 1 written with author, reviewer and publisher; - the event trail in order; - an admin's own edit publishing directly, and an L1 draft going to the admin; - the publish conflict refused. The test restores the work it edits. - Headless Chrome, three people, against a temporary data folder: 1. the L2 moderator edits one misra of Ghalib's ghazal and submits (one-line diff); 2. the L1 moderator approves from their queue; 3. the admin publishes version ۱; 4. the poem page shows the new misra, and the history and timeline are complete. The test edit and accounts were removed afterwards. **Next**: git commits on publish (#34), the Tiptap editor (#28), arranging (#41), tags (#52), history for poets, intros, books and the dictionary. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
anas added 2 commits 2026-10-08 22:32:03 +00:00
- api/src/owned.ts: fromPoem (a work as Divan text, the start of an edit) and writeOwned (the .dtx
  source and the generated .json into divan-data/divan/); a CLI for writing one by hand.
- Divan text: chapter headings and sub-headings with levels, as their own verse entries.
- All 11,087 works convert to Divan text and back unchanged (apart from repeated spaces, and raw
  '== … ==' markers in prose becoming headings).
- docs/content-model.md: the content management model (what is identified, versions and who did
  what (#51), tags and tag search (#52), storage).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- revisions (a work's Divan text, summary, status, author/reviewer/publisher, the text it started from)
  and revision_events (who did what, with comments): the review thread and the moderation log.
- API (api/src/moderation.ts): what a moderator may do on a work, queue, drafts (one open draft per
  person and work), save, submit, approve, return/reject (reason required), publish; history; log.
  Rules come from the IAM grants: L2 drafts, an L1 covering the work reviews, an admin publishes; L1
  drafts go to the admin, admin drafts publish directly. Publishing numbers the version, writes it
  to divan-data (owned.ts) and updates the site; a draft based on an older version is refused (409).
- Line diff (api/src/diff.ts) against the text a draft started from.
- Site: ترمیم کریں / تاریخچہ on works, /mod (queue, activity log), /mod/rev/<id> (editor with the Urdu
  keyboard and Roman typing, diff, preview, actions, timeline), /mod/work/<id> (versions).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
anas merged commit 5929201efa into main 2026-10-08 22:34:51 +00:00
Sign in to join this conversation.
No reviewers
No Milestone
No project
No Assignees
1 Participants
Notifications
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

No due date set.

Dependencies

No dependencies set.

Reference: anas/divan#54
No description provided.