# content-on-sx: Documents, blocks & collaborative editing on Smalltalk

> **DRAFT outline.** The CMS vertical — blog, WYSIWYG editor, Ghost sync. Depends
> on `persist-on-sx` (document history as an event log). Ghost/CMS sync stays a thin
> external adapter (Python/FFI) until a native replacement exists.

rose-ash's `blog` domain is content management: a block-based WYSIWYG editor,
navigation, Ghost CMS sync. A document is a tree of live blocks; editing is a
stream of operations; collaboration needs conflict-free merge. That is an object
model — blocks are objects, edits are messages, and a document is the object graph
responding to them. Smalltalk's "everything is an object responding to messages"
maps directly to a block/WYSIWYG model, and a semilattice (CRDT) merge keeps
concurrent edits conflict-free.

End-state: a Smalltalk-on-SX document model (typed blocks, structural ops),
operation log + CRDT merge for collaborative editing, versioning/history via the
event store, and a render boundary to HTML/SX. External CMS (Ghost) sync is an
injected adapter, not core.

## Status (rolling)

`bash lib/content/conformance.sh` → **0/0** (not yet started)

## Ground rules

- **Scope:** only `lib/content/**` and `plans/content-on-sx.md`. May **import**
  from `lib/smalltalk/`, and (once it exists) `lib/persist/`. Do not edit substrates.
- **Architecture:** a document is an ordered tree of blocks (objects); an edit is a
  message (`insert`/`update`/`move`/`delete`); concurrent edits merge via a
  commutative (CRDT/semilattice) operation so order doesn't matter. History is the
  `persist` event stream; any version is a replay.
- **Determinism:** merge must be commutative + idempotent (test: apply ops in any
  order / twice → same document).
- **Commits:** one feature per commit. Progress log + tick boxes.

## Architecture sketch

```
Edit op                                 Rendered document
  (insert block after id) ...             HTML / SX tree
        │                                       ▲
        ▼                                       │
lib/content/block.sx                    lib/content/render.sx
  — typed blocks as objects               — block tree → HTML/SX
  — heading/text/image/embed              — (reuses SX render boundary)
        │                                       ▲
        ▼                                       │
lib/content/doc.sx                      lib/content/merge.sx
  — ordered block tree                    — CRDT/semilattice op merge
  — apply op, structural moves            — concurrent-edit reconciliation
        │                                       ▲
        ▼                                       │
lib/content/api.sx ── (content/edit) (content/render) (content/history) ──┐
        │                                                                  │
        ├── op log + versions → persist                                      │
        └── Ghost/CMS sync  → injected external adapter (thin, non-core) ──┘
```

## Phase 1 — Block document model
- [ ] `block.sx` — typed block objects
- [ ] `doc.sx` — ordered tree, apply edit op, structural moves
- [ ] `render.sx` — block tree → HTML/SX
- [ ] `api.sx` + tests + scoreboard + conformance.sh

## Phase 2 — Op log + versioning
- [ ] edit ops as `persist` events; replay to any version
- [ ] `(content/history doc)`, diff between versions

## Phase 3 — Collaborative merge (CRDT)
- [ ] commutative/idempotent op merge
- [ ] concurrent-edit tests (any order, double-apply → identical)

## Phase 4 — External sync + federation
- [ ] Ghost/CMS sync via injected adapter (import/export)
- [ ] federated documents (peer-authored blocks) — trust-gated stub
- [ ] tests: round-trip import/export, conflict on concurrent external edit

## Progress log
(loop fills this in)

## Blockers
(loop fills this in)
