coulomb-social/docs/adr/ADR-0003-page-centric-markdown-sor.md
tegwick ce65b359af Add ADR-0003 and ADR-0004 for page SoR and thin git content plane
Accept page-centric markdown SoR with dual authoring, live includes, and
lazy projections; accept thin dir+git plane with PageOps and supported
upgrades to shard-wiki and kontextual. Demote ADR-0002 Forgejo-as-SoR.
2026-08-13 10:04:46 +02:00

7.7 KiB
Raw Blame History

id title status date deciders related workplans
ADR-0003 Page-centric markdown SoR, authoring, and live derived content accepted 2026-08-13 bernd
ADR-0001
ADR-0002
ADR-0004
docs/architecture/ArchitectureBlueprint.md
docs/decisions/2026-08-12-feature-cut-stage-1.md
CSOC-WP-0004
CSOC-WP-0006
CSOC-WP-0001

ADR-0003 — Page-centric markdown SoR, authoring, and live derived content

Status

Accepted (2026-08-13).

Supersedes the parts of ADR-0002 that treated “one Forgejo repo per space as the product SoR.” ADR-0002 remains valid as a historical vertical slice and as one optional git/Forgejo binding shape under ADR-0004.

Context

The rebuild must exit Bubble with user-owned content that is easy to export and reuse. Live coulomb models spaces, pages, and chunks; stage-1 cut requires Title, Abstractor, Visual, copy, and transfer without recreating Bubbles full chunk graph.

Prior work delivered a Forgejo-bound read/write slice (ADR-0002). Architecture discussion established:

  • Markdown (+ assets) as exportable system of record; database as cache.
  • markitect-tool as structure library (not product host); markitect-main avoided.
  • Page-first knowledge work with transclusion rather than a priori chunks.
  • Dual authoring (top-down and bottom-up) and lazy display fields.
  • Live includes by default (spreadsheet-like); pin as exception.

Decision

1. System of record

  1. Content SoR is markdown files (frontmatter + body + backmatter) and assets under a directory tree versioned in git (see ADR-0004).
  2. Postgres (or any app DB) holds only:
    • identity linkage (Member ↔ NetKingdom subject),
    • product indexes / projections for cards and lists,
    • optional pointers into the tree (paths, digests) — not long-form page bodies as SoR.
  3. Anything a user should take on export must live in the tree; DB content for bodies is disposable and rebuildable.

2. Information model

Concept Meaning
Page Primary knowledge object: one markdown document (or connected set after explode).
Space Container / context for pages (directory + metadata; may itself be a page of type: space).
Card UI projection only (title, abstractor, visual, actions) — not a storage type.
Region / “chunk” Addressable unit within a page (heading, named region, selector). Not a required first-class storage row.
Member / tenant NetKingdom + app session (ADR-0001); not stored as page content.

Stage-1 fields on pages and spaces (frontmatter or equivalent):

  • title
  • abstractor (short description; Bubble “Abstractor”)
  • visual (ref to an asset)
  • body (markdown)

3. Page-first structure (not a priori chunks)

  1. Authors write pages fluently; structure is headings and optional named regions.
  2. Reuse uses address → live transclude → promote (markitect include/selector/region), not a mandatory Chunk entity graph.
  3. Bubble custom.chunk migration prefers sections/regions or connected pages with includes — not permanent recreation of chunk tables.
  4. Outline flexibility uses markitect explode / implode (and related compose/include): split one document into smaller connected documents; merge selected docs back into one. Connectivity after explode prefers live includes or content references.

4. Dual authoring modes

Both are first-class; no forced mode toggle:

Mode Flow
Top-down Title → abstractor → outline (headings) → elaborate body → optional visual
Bottom-up Body first; title / abstractor / structure / visual established later or while writing

Creating or saving content must not require a complete metadata form.

5. Lazy projections, lock, and staleness

For display fields (title, abstractor, visual) and other derived values:

  1. If absent and display needs them → generate lazilypersist as normal field values (as if user-provided for export).
  2. Per-field regenerate and lock (lock blocks auto-fill and regenerate until unlock).
  3. If inputs change and the field is unlocked derived → May be stale badge → Generate updated version → side-by-side Current | AlternativeKeep current or Use alternative (replace). Optional ack updates fingerprint without changing text.
  4. Never silently overwrite locked or last-user-edited values without consent.

Generator policy: deterministic first (e.g. first H1, first paragraph); optional LLM/pipeline behind the same contract.

6. Live by default; pin as exception

Mode Behavior
Live (default) Includes and formula-like derived content resolve to current inputs on view/export (spreadsheet metaphor).
Pinned (exception) Explicit freeze of a snapshot in the host document; refresh uses stale → alternative → replace | keep.

Pure live includes do not require replace/keep; pinned/mkt:derived (markitect proposal) and projection fields do.

7. markitect-tool role

  1. coulomb depends on markitect-tool for parse, frontmatter, query/extract, live include, explode/implode, and (when implemented) derived-slot stale/refresh APIs.
  2. coulomb does not “run on” markitect as a product platform; markitect-main is not a runtime foundation.
  3. Cross-repo: pin/derived contract tracked in markitect-tool docs/proposed-stale-derived-and-include-pinning.md.

8. Relationship to ADR-0002

ADR-0002 item Under ADR-0003/0004
Markdown as content form Confirmed
Assets beside content Confirmed (tree layout; asset-store later)
Postgres not body SoR Confirmed
One Forgejo repo per space as SoR Demoted — optional remote/export; SoR is the content tree + git (ADR-0004)
Edit-in-Forgejo as primary write Optional path; product PageOps may write tree/git directly

Consequences

Positive

  • User data remains files; agents and humans edit without the app DB.
  • Stage-1 fields and card UI align with live Bubble vocabulary (Title, Abstractor, Visual).
  • Fluency: top-down and bottom-up; live reuse; outline explode/implode without chunk ontology tax.
  • Clear demotion of Forgejo-as-unique-SoR reduces lock-in to one forge layout.

Negative / costs

  • App must implement projection index invalidation when files change.
  • Live includes need careful caching and security (path sandbox, cycles) via markitect.
  • Lazy/AI generators need UX discipline so writers are not interrupted or overwritten.
  • Explode/implode UX is non-trivial product work (connectivity, navigation).

Neutral

  • Identity remains ADR-0001; content authz consolidation is NetKingdom-side (ArchitectureBlueprint D3).
  • Content plane storage mechanics and upgrades are ADR-0004.

Non-goals

  • Recreating Bubble marketplace, social graph, or monetization in this ADR.
  • Choosing shard-wiki or kontextual as the initial plane (upgrade path only).
  • Extracting a fleet asset-store repo (deferred).
  • Full markitect derived-slot implementation timeline (owned by markitect-tool).

References

  • docs/architecture/ArchitectureBlueprint.md (D1D4, R1R12)
  • docs/architecture/transclusion-and-chunks.md
  • docs/architecture/authoring-modes-and-lazy-projections.md
  • docs/architecture/stale-derived-content.md
  • docs/capability/stage-1-cutover.md
  • docs/capability/existing-bubble-capability-map.md
  • ADR-0002 (provisional slice; partially superseded)
  • markitect-tool: docs/transform-compose-include.md, docs/explode-implode.md, docs/proposed-stale-derived-and-include-pinning.md