Files
shard-wiki/research/260614-forge-wikis-deep-dive/findings.md
tegwick dbc42f05db research: git-forge wikis deep dive (Gitea/GitLab/GitHub); UC-76-77
SHARD-WP-0003 T5. The home case: a forge wiki is a separate .wiki.git repo of
Markdown -> page model, history, coordination journal map 1:1 with near-zero
adapter. git-clone universal across all three; wiki content API
capability-varying (GitLab/Gitea yes, GitHub git-only). git IS the canonical
store (not a mirror), so write-by-commit is safe -- resolves the UC-68/Q22
race for this case. UC-76 (clone .wiki.git attach), UC-77 (forge wiki API,
capability varies). Enriched UC-40/02/68/38. Marks T5 done.
Feeds SHARD-WP-0002 T14/T11.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-14 19:40:46 +02:00

174 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# git-forge wikis (Gitea · GitLab · GitHub) — deep dive (findings)
**Date:** 2026-06-14 · **Source:** SHARD-WP-0003 T5 · **Subject:** the Markdown wikis
hosted by the three major git forges — **Gitea**, **GitLab**, **GitHub** — treated as one
family because they share one architecture: *a wiki is a separate git repo of Markdown.*
## Why this dive
INTENT names **Gitea wikis** as a shard participant, and the whole project is "a **Git-based
Markdown** wiki orchestrator." The forge wikis are therefore the **least exotic, highest-
fit** backend in the entire study: the page store is *literally a git repository of Markdown
files*. After fourteen dives into DBs, CRDTs, graphs and SaaS, this one confirms the
**home case** — and sharpens it by contrasting *git-IS-the-store* (forge wikis) against
*git-is-a-mirror* (Wiki.js, UC-68).
## 1. The shared architecture — a wiki is a `.wiki.git` repo
All three forges implement a project/repo wiki as a **second, dedicated git repository**
alongside the code repo, addressable as `<repo>.wiki.git`:
- `git@host:owner/project.wiki.git` (GitLab), `…/owner/repo.wiki.git` (Gitea/GitHub).
- **Pages are Markdown files** (`Home.md`, `Some-Page.md`), one file per page; the **page
title ↔ filename** (spaces ↔ hyphens by convention). Other markups are accepted
(AsciiDoc, Textile, reStructuredText, Org) — GitHub/Gitea via **Gollum** (the Ruby
git-backed wiki library), GitLab via its own renderer.
- **History is git history** — every page edit (web or pushed) is a git commit with
author/timestamp/message. *The wiki's revision history is a real git log.*
- **Special pages** by convention: `_Sidebar`, `_Footer`, `_Header` (GitHub/Gitea),
`_sidebar` (GitLab) — engine-rendered chrome stored as ordinary files.
- **Subdirectories / nested pages**: GitLab and Gitea support directory structure; GitHub
wikis are historically flat (Gollum supports paths but the GitHub UI is shallow).
The decisive property: **you can `git clone` the wiki repo, edit files, commit, and push**,
and the forge UI reflects it — *and vice versa*. Git is **a** (often **the**) first-class
write path. This is exactly shard-wiki's native medium with no impedance.
## 2. Where they differ — the API matrix
| | git clone/push of `.wiki.git` | wiki content **API** | nested dirs | markups |
|--|--|--|--|--|
| **Gitea** | ✅ yes | ✅ **REST wiki endpoints** (list/get/create/edit/delete pages) | ✅ | Markdown (+Gollum-style) |
| **GitLab** | ✅ yes | ✅ **REST Wikis API** (project & group wikis) | ✅ | Markdown/AsciiDoc/RDoc/Org |
| **GitHub** | ✅ yes | ❌ **no wiki REST API** — wiki is **git-only** (Gollum) | ⚠️ flat UI | Markdown + Gollum markups |
The key asymmetry: **GitHub exposes wiki content *only* through git** (the REST/GraphQL API
covers issues/PRs/code but **not** wiki pages); **GitLab and Gitea offer both** a wiki API
*and* git access. So the **git-clone path is the universal one** (works for all three); the
API path is an *optional, capability-varying* alternative.
## 3. git-IS-the-store vs git-is-a-mirror (the UC-68 contrast)
Wiki.js (UC-68) keeps a **DB as canonical** and *maintains a git mirror* — so writing by
commit risks **racing the engine's DB↔git sync** (catalog open-Q22). Forge wikis are the
opposite: **the git repo IS the canonical store**; there is *no* separate DB of record for
wiki content. Therefore:
- **The source-of-truth question (Q22) is resolved for this case:** the `.wiki.git` repo is
authoritative. shard-wiki can **write by commit/push directly** with no engine to race —
the forge merely *renders* what git holds.
- The forge **API** (GitLab/Gitea), where present, is a *convenience over the same git
repo*, not a competing store — so API-write and git-write converge on one history.
This makes forge wikis the **cleanest possible write-through file-store shard**: clone =
projection/mirror, commit = overlay-applied/write, git log = the coordination journal *as
is*.
## 4. Capability profile
| Dimension (synthesis spectrum) | Gitea / GitLab / GitHub wiki |
|--------------------------------|------------------------------|
| Attachment mode | **file-store (native: git clone)** + optional **external-API** (GitLab/Gitea wiki REST) |
| Addressing granularity | **page = file**; sub-page = path (GitLab/Gitea) |
| Content identity | path/filename within the wiki repo (title-derived) |
| Identity vs placement | placement-bound (path = identity), like a plain git repo |
| Structure | flat or directory tree of Markdown files; `_Sidebar`/`_Footer` chrome |
| History | **native git history** (real commits, authors, messages) |
| Merge model | **git** (3-way merge, branches) — though wiki repos are usually single-branch |
| Native query | none (it's files); forge full-text search over the wiki |
| Translation | **Markdown-native** (+ AsciiDoc/Org via renderer) — minimal/no translation needed |
| Attachment/write granularity | **file (page)** per commit |
| Operational envelope | ordinary git + forge; clone is cheap; API rate limits apply to API path |
| Access grant | **forge repo permissions** (delegated auth; per-repo/role ACL) |
| Content opacity | transparent Markdown in git |
| Provenance | git author/committer/timestamp per commit — native |
## 5. INTENT mapping
### Reinforcements (this is the home case)
- **Git-based Markdown orchestrator** (INTENT core): forge wikis *are* git repos of
Markdown. The **wiki page model** (Markdown-first, path-addressed, git-versioned) maps 1:1
— minimal adapter, maximal fit.
- **Coordination journal = git** (INTENT): the wiki repo's **git log is already the
coordination journal** — no synthesis needed; adopt it directly.
- **Overlay before mutation**: overlays are **branches/commits** on the cloned wiki repo;
applying = push (or open an MR/PR where the forge supports wiki MRs — GitLab does not for
wikis, so push-to-branch + manual is the path).
- **Graceful degradation**: even GitHub (no wiki API) is fully usable via git-clone — the
*universal* path means a limited forge is still a first-class read/write shard.
- **No silent remote mutation**: writes are explicit git pushes (or explicit API calls)
under the user's forge credentials and repo permissions.
### Divergences (boundaries / notes — minor)
- **Capability varies by forge**: GitHub = git-only (no content API); GitLab/Gitea = git +
API. The adapter must **model the API as an optional capability**, defaulting to the
universal git path (T11/T14). Not a bug — exactly the capability-awareness INTENT mandates.
- **Wiki repos rarely use branches/MRs for review**: forge wikis usually edit a single
branch directly; the rich PR-review flow is on the *code* repo, not the wiki. So
"overlay → review → merge" needs shard-wiki to provide the review layer, not the forge.
- **Identity = path** (like any git repo) — cross-shard identity (T16) is layered above, as
for plain git/`wiki/` subdir shards.
### What to keep
1. **git-clone as the universal, canonical file-store attach** for forge wikis — Markdown +
git history directly as page model + coordination journal (UC-76). The reference
easy-case backend.
2. **Forge wiki API as an optional capability** (GitLab/Gitea), with **git-only fallback**
(GitHub) — capability-aware binding (UC-77).
3. **git-IS-store ⇒ write-by-commit is safe** (no engine race) — record this as the
resolution of the Wiki.js mirror dilemma (Q22) for forge wikis.
## 6. UC seeds
| # | Seed | Disposition |
|---|------|-------------|
| UC-76 | Attach a **git-forge wiki** by **cloning its dedicated `.wiki.git`** — git is the native store; Markdown files = pages, git log = coordination journal; commit/push = write (no engine to race) | **new** |
| UC-77 | Attach/write a forge wiki via the **forge's wiki API** (GitLab/Gitea REST) where git-clone is unavailable or API-write is preferred; **git-only fallback** for GitHub — capability varies by forge | **new** |
| — | git-native file-store as the *canonical store* (not mirror) | enrich **UC-40** |
| — | dual-path attach (git clone vs forge API) | enrich **UC-02** |
| — | git-IS-store vs engine-maintained mirror (resolves Q22) | enrich **UC-68** |
| — | forge as an API host for the wiki resource | enrich **UC-38** |
## 7. Architecture notes for SHARD-WP-0002
- **T14 (adapter binding / attach path):** forge wikis are the canonical **file-store
attach** — bind to the `.wiki.git` clone as the universal path; model the **wiki API as an
optional, forge-specific capability** (present: GitLab, Gitea; absent: GitHub). One shard,
two possible bindings converging on the same git history.
- **T11 (capability model):** "has-content-API" is a **per-forge capability flag**; git
clone/push is the baseline every forge satisfies. Minimal adapter profile — near the
Oddmuse-simple end but Markdown-native and git-versioned.
- **Coordination journal:** adopt the wiki repo's **git log directly** — the one backend
where INTENT's git-backed journal needs *zero* synthesis.
- **Resolves Q22 (UC-68):** because git **is** the store (not a mirror), **write-by-commit
is safe** — no engine DB↔git sync to race. Record the distinction *engine-mirror*
(Wiki.js: DB canonical, careful) vs *git-canonical* (forge wikis: commit freely).
## 8. Open questions
1. For overlay → **review** → apply, does shard-wiki supply the review layer over a forge
wiki (which lacks wiki-MRs), e.g. via a branch + its own diff/approve, or push directly?
2. When a forge offers **both** git and a wiki API (GitLab/Gitea), which does the adapter
prefer by default — git (universal, full history) with API as a fallback for hosts where
clone is disabled? (cf. UC-43 backend-swap under stable binding.)
3. Should the **code-repo `wiki/` subdir** shard and the **forge wiki repo** shard share one
adapter (both git+Markdown) with a "which repo / which path" parameter, or stay distinct?
## 9. Sources
- GitLab Docs — *Wiki* (separate git repo; web/git/API; `.wiki.git`) — docs.gitlab.com
- Gitea — wiki via git clone + repository **wiki API**; forum/issue threads on
`.wiki.git` clone (go-gitea/gitea #1426, #15420) — gitea.com / github.com/go-gitea
- GitHub — wiki = Gollum git repo (`<repo>.wiki.git`), no wiki REST API — docs.github.com
- Gollum (git-based wiki library) — github.com/gollum/gollum
- prior: `research/260614-wikijs-deep-dive/` (engine-maintained mirror contrast, UC-68)
## 10. Traceability
New UCs **UC-76UC-77** carry the marker **⎇** in the wikiengines column of
`spec/UseCaseCatalog.md`. Enriched: UC-40, UC-02, UC-68, UC-38. Architecture cross-refs:
SHARD-WP-0002 T14, T11; coordination-journal-from-git; resolves catalog open-Q22.