Artifact 477e53c2a2f4d835e06b35c0d62dbe16ea48c2738e7cf0bf372c1e932154b596:
- File
docs/superpowers/plans/2026-04-26-docs-site-cf-deploy.md
— part of check-in
[6273a0750d]
at
2026-07-22 23:03:24
on branch trunk
— fix: correct go.mod module path to go-libfossil (#76) (#117)
* fix: correct go.mod module path to go-libfossil (#76)
The module directive still declared github.com/danmestas/libfossil after the repository was renamed to go-libfossil. pkg.go.dev/godoc scraping matches the declared module path against its hosting path, so the stale path broke SDK doc generation.
Rename the module path across all four in-repo modules (root, the two db/driver submodules, observer/otel, and docs/site) and every internal import, plus the release workflow, the doc-generation script, and the docs.
No tag has been published under the go-libfossil path yet, so the cross-module requires cannot resolve from the proxy. Add replace directives pointing the in-repo requires at their local directories so GOWORK=off builds and the CI matrix resolve without a published version. These replaces are ignored by downstream consumers and can be dropped once new-path tags are cut.
* Guard releases against replace directives; regenerate SDK docs
The go-libfossil rename bridges the in-repo modules with `replace` directives. Go refuses a main module that carries them, so a tag cut while they are present breaks `go install .../cmd/libfossil@<tag>` and every `@version` fetch of the driver submodules. Add a Release workflow step that fails the job before anything is published if any tracked go.mod still contains a replace directive, so the bridge is a CI-enforced TODO rather than a silent landmine. The step comment records what unblocks a release: re-tag the submodules with the corrected module paths, bump the root requires, then drop the replace blocks.
Regenerate the SDK reference docs. The previous pass only substituted URLs over the pre-existing generated files, leaving stale signatures (Repo.Timeline was still documented as taking LogOpts rather than TimelineOpts). Note that gomarkdoc must be run outside a git worktree — inside one it silently strips every source link.
Also reword the README rename note, which read as though the import path had not changed, and re-sort the ncruces require block. (user: [email protected], size: 37587) [annotate] [blame] [check-ins using]
# libfossil Docs Site + Cloudflare Auto-Deploy Implementation Plan > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. **Goal:** Add a Hugo + Hextra documentation site, a self-contained landing page, auto-generated Go SDK reference, and Cloudflare Workers Static Assets auto-deploy — mirroring the sibling project `dagnats` and serving from a single Cloudflare Worker. **Architecture:** All web content lives under `docs/site/`. Hugo + Hextra theme (via Hugo modules) renders the docs site; a `layouts/index.html` override at the same site renders the landing page at `/`. SDK reference is generated by `gomarkdoc` from Go doc comments and committed to the repo; a pre-commit hook keeps it in sync with code. Cloudflare Workers' git integration auto-deploys on push to `main` using an inline build defined in `wrangler.jsonc` (download Hugo → run llms-txt script → `hugo --minify` → serve `docs/site/public/` via Workers Static Assets). No GitHub Action handles deploy. **Tech Stack:** Hugo extended v0.160.0, Hextra theme via Hugo modules, gomarkdoc, bash, `wrangler` (Cloudflare Workers Static Assets), Geist Mono via Google Fonts (loaded by the landing page). **Branch:** `feat/docs-site-cf-deploy` (already created and contains the spec). **Spec:** `docs/superpowers/specs/2026-04-25-docs-site-cf-deploy-design.md`. **Commit policy:** No `Co-Authored-By: Claude` trailers. No `Generated with Claude Code` PR footers. Per-project user preference. --- ## File structure All paths relative to repo root. **New:** - `docs/site/hugo.yaml` — Hugo + Hextra config - `docs/site/go.mod`, `docs/site/go.sum` — Hugo module pins (Hextra) - `docs/site/content/_index.md` — site root frontmatter (body empty; layout override handles rendering) - `docs/site/content/docs/_index.md` — docs section landing - `docs/site/content/docs/quickstart.md` — drafted from README - `docs/site/content/docs/reference/_index.md`, `docs/site/content/docs/reference/sdk/_index.md` — section stubs - `docs/site/content/docs/reference/sdk/{libfossil,cli,db,db/driver/modernc,db/driver/ncruces,observer/otel,dst}/api.md` — generated by `gomarkdoc`, committed - `docs/site/content/examples/_index.md` — examples section stub - `docs/site/layouts/index.html` — landing page (Hugo home override; forked from dagnats) - `scripts/gen-llms-txt.sh` — llms.txt generator - `wrangler.jsonc` — CF Worker config **Changed:** - `Makefile` — adds `docs-serve`, `docs-build`, `docs-gen-sdk`, `docs-gen-llms`, `docs` targets - `.githooks/pre-commit` — adds SDK-drift check - `.gitignore` — adds Hugo + llms artifacts + downloaded `hugo` binary - `README.md` — points at the docs site **Deleted (after move):** - `docs/architecture.md`, `docs/extension-points.md`, `docs/migration-from-fossil.md`, `docs/testing.md` (moved into `docs/site/content/docs/`) **Reference for forks/copies:** sibling project at `../dagnats/`. The plan calls out specific paths to copy from when relevant. --- ## Task 1: Bootstrap Hugo + Hextra docs site **Files:** - Create: `docs/site/hugo.yaml` - Create: `docs/site/go.mod`, `docs/site/go.sum` (auto-generated by `hugo mod`) - Create: `docs/site/content/_index.md` - Create: `docs/site/content/docs/_index.md` - Modify: `.gitignore` - [ ] **Step 1: Add gitignore entries** Append to `.gitignore`: ``` # Hugo build artifacts docs/site/public/ docs/site/resources/ docs/site/.hugo_build.lock docs/site/_vendor/ # Generated llms.txt artifacts (built at deploy time, not committed) docs/site/static/llms.txt docs/site/static/llms-full.txt # Downloaded Hugo binary (used by CF and local manual builds) docs/site/hugo hugo ``` - [ ] **Step 2: Install Hugo extended locally (one-time)** Hugo extended ≥0.160 is required for the Hextra theme. If `hugo version` does not report `extended`, install it: ```bash # macOS via Homebrew (installs the latest extended) brew install hugo # Or pin to v0.160.0 to match the CF build curl -sSL https://github.com/gohugoio/hugo/releases/download/v0.160.0/hugo_extended_0.160.0_darwin-universal.tar.gz \ | tar xz hugo -C /tmp \ && sudo mv /tmp/hugo /usr/local/bin/hugo hugo version # expect: hugo v0.160.0+extended ... ``` Linux/WSL: substitute `linux-amd64` for `darwin-universal` and `/usr/local/bin/hugo` for the destination. - [ ] **Step 3: Initialize the Hugo module + pull Hextra** ```bash cd docs/site hugo mod init github.com/danmestas/go-libfossil/docs/site hugo mod get github.com/imfing/hextra cd - ``` This creates `docs/site/go.mod` and `docs/site/go.sum`. (These are Hugo's module files; they are independent of the project's main `go.mod` at the repo root.) - [ ] **Step 4: Write `docs/site/hugo.yaml`** ```yaml title: libfossil baseURL: "" module: imports: - path: github.com/imfing/hextra markup: goldmark: renderer: unsafe: true highlight: noClasses: false menu: main: - name: Docs pageRef: /docs weight: 1 - name: SDK pageRef: /docs/reference/sdk weight: 2 - name: Examples pageRef: /examples weight: 3 - name: Search weight: 4 params: type: search - name: GitHub weight: 5 url: "https://github.com/danmestas/go-libfossil" params: icon: github params: navbar: displayTitle: true displayLogo: false theme: default: system displayToggle: true search: enable: true type: flexsearch flexsearch: index: content tokenize: forward footer: displayCopyright: true displayPoweredBy: false editURL: enable: true base: "https://github.com/danmestas/go-libfossil/edit/main/docs/site/content" page: width: wide ``` - [ ] **Step 5: Write `docs/site/content/_index.md`** ```markdown --- title: libfossil layout: landing --- ``` (Body intentionally empty: `docs/site/layouts/index.html` from Task 8 takes precedence for the home page.) - [ ] **Step 6: Write `docs/site/content/docs/_index.md`** ```markdown --- title: Documentation cascade: type: docs --- libfossil is a pure-Go library and CLI for reading and writing Fossil SCM repositories. Zero CGo. Static binaries. WASM-ready. Start with the [Quickstart](./quickstart) for a five-minute tour. The [Architecture](./architecture) page explains how the pieces fit. The [SDK reference](./reference/sdk) is generated from Go doc comments. ``` - [ ] **Step 7: Verify Hugo serves the stub site** ```bash cd docs/site && hugo server --port 1313 ``` Expected: Hugo prints `Web Server is available at http://localhost:1313/`. Open that URL: nav shows `libfossil`, `Docs`, `SDK`, `Examples`, `Search`, `GitHub`. Visiting `/docs/` shows the cascade page rendered with the body text from Step 6. Stop the server with Ctrl-C. - [ ] **Step 8: Commit** ```bash git add .gitignore docs/site/hugo.yaml docs/site/go.mod docs/site/go.sum docs/site/content/_index.md docs/site/content/docs/_index.md git commit -m "docs(site): bootstrap Hugo + Hextra skeleton" ``` The pre-commit hook runs the existing test suite (~45s) on every commit; expect it to pass with no code changes. --- ## Task 2: Move existing markdown into Hugo content tree **Files:** - Move: `docs/architecture.md` → `docs/site/content/docs/architecture.md` - Move: `docs/extension-points.md` → `docs/site/content/docs/extension-points.md` - Move: `docs/migration-from-fossil.md` → `docs/site/content/docs/migration-from-fossil.md` - Move: `docs/testing.md` → `docs/site/content/docs/testing.md` For each file: `git mv` the file, then prepend a five-line YAML frontmatter block at the top so Hextra has a stable title and weight for nav ordering. Frontmatter weights (lower = earlier in nav): | File | Title | Weight | |---|---|---| | `quickstart.md` (Task 3) | Quickstart | 10 | | `architecture.md` | Architecture | 20 | | `migration-from-fossil.md` | Migration from Fossil | 30 | | `extension-points.md` | Extension Points | 40 | | `testing.md` | Testing | 50 | - [ ] **Step 1: Move architecture.md and prepend frontmatter** ```bash git mv docs/architecture.md docs/site/content/docs/architecture.md ``` Then open `docs/site/content/docs/architecture.md` and insert at the very top, before any existing content: ```markdown --- title: Architecture weight: 20 --- ``` (Note the blank line after the closing `---`.) - [ ] **Step 2: Move migration-from-fossil.md** ```bash git mv docs/migration-from-fossil.md docs/site/content/docs/migration-from-fossil.md ``` Prepend: ```markdown --- title: Migration from Fossil weight: 30 --- ``` - [ ] **Step 3: Move extension-points.md** ```bash git mv docs/extension-points.md docs/site/content/docs/extension-points.md ``` Prepend: ```markdown --- title: Extension Points weight: 40 --- ``` - [ ] **Step 4: Move testing.md** ```bash git mv docs/testing.md docs/site/content/docs/testing.md ``` Prepend: ```markdown --- title: Testing weight: 50 --- ``` - [ ] **Step 5: Verify nav order** Run `cd docs/site && hugo server` and visit `/docs/`. Expected nav order under "Docs": Architecture, Migration from Fossil, Extension Points, Testing. (Quickstart with weight 10 is added in Task 3.) Click each one and verify the body content renders. - [ ] **Step 6: Verify the old `docs/*.md` paths no longer exist** ```bash ls docs/*.md 2>/dev/null # Expected: no output (all four moved) ``` - [ ] **Step 7: Commit** ```bash git add docs/site/content/docs/ git commit -m "docs(site): move existing docs into Hugo content tree" ``` --- ## Task 3: Add quickstart, examples, and SDK section stubs **Files:** - Create: `docs/site/content/docs/quickstart.md` - Create: `docs/site/content/docs/reference/_index.md` - Create: `docs/site/content/docs/reference/sdk/_index.md` - Create: `docs/site/content/examples/_index.md` - [ ] **Step 1: Write `docs/site/content/docs/quickstart.md`** ````markdown --- title: Quickstart weight: 10 --- A five-minute tour of libfossil: install the CLI, clone a repo, and read its history from Go. ## Install ```sh go install github.com/danmestas/go-libfossil/cmd/libfossil@latest libfossil --help ``` The binary is statically linked. No CGo required. ## Clone a repository ```sh libfossil clone https://example.com/repo.fossil ./repo.fossil ``` ## Open from Go ```go package main import ( "context" "fmt" "log" "github.com/danmestas/go-libfossil" ) func main() { repo, err := libfossil.Open("repo.fossil") if err != nil { log.Fatal(err) } defer repo.Close() timeline, err := repo.Timeline(context.Background(), 10) if err != nil { log.Fatal(err) } for _, c := range timeline { fmt.Println(c.UUID, c.Comment) } } ``` ## Sync over HTTP ```go import "github.com/danmestas/go-libfossil" t := libfossil.NewHTTPTransport("https://example.com/repo.fossil") if err := repo.Sync(ctx, t); err != nil { log.Fatal(err) } ``` ## Next steps - [Architecture](../architecture) — how the pieces fit - [Migration from Fossil](../migration-from-fossil) — command mapping from upstream - [SDK reference](../reference/sdk) — every package, every exported symbol ```` (If the actual `Timeline` signature or `NewHTTPTransport` factory does not match what's in the codebase, update the snippet against the real exports — verify by reading `libfossil.go` before writing.) - [ ] **Step 2: Write `docs/site/content/docs/reference/_index.md`** ```markdown --- title: Reference weight: 100 --- ``` - [ ] **Step 3: Write `docs/site/content/docs/reference/sdk/_index.md`** ```markdown --- title: SDK weight: 10 --- Auto-generated Go API reference. One page per package, generated by [gomarkdoc](https://github.com/princjef/gomarkdoc) from doc comments. Run `make docs-gen-sdk` to regenerate. The pre-commit hook fails if the working tree drifts. ``` - [ ] **Step 4: Write `docs/site/content/examples/_index.md`** ```markdown --- title: Examples weight: 60 --- Runnable examples live in [`examples/`](https://github.com/danmestas/go-libfossil/tree/main/examples) in the repo. (This page will index them once the directory is populated.) ``` - [ ] **Step 5: Verify** `cd docs/site && hugo server`. Visit `/docs/`: nav order is Quickstart → Architecture → Migration from Fossil → Extension Points → Testing → Reference (with SDK as a child). Visit `/docs/quickstart/` and verify code blocks render with syntax highlighting. Visit `/examples/` and confirm the placeholder renders. - [ ] **Step 6: Commit** ```bash git add docs/site/content/docs/quickstart.md docs/site/content/docs/reference/ docs/site/content/examples/ git commit -m "docs(site): add quickstart, examples, and SDK section stubs" ``` --- ## Task 4: gomarkdoc Makefile target + initial SDK generation **Files:** - Modify: `Makefile` - Create: `docs/site/content/docs/reference/sdk/{libfossil,cli,db,db/driver/modernc,db/driver/ncruces,observer/otel,dst}/api.md` - [ ] **Step 1: Update `Makefile` to add `docs-gen-sdk`** In the existing `.PHONY:` line at the top of `Makefile`, append `docs-gen-sdk`: ```makefile .PHONY: test test-drivers test-otel test-all vet build setup-hooks docs-gen-sdk ``` Append the target at the bottom of the file: ```makefile docs-gen-sdk: @command -v gomarkdoc >/dev/null 2>&1 || go install github.com/princjef/gomarkdoc/cmd/gomarkdoc@latest gomarkdoc --output docs/site/content/docs/reference/sdk/libfossil/api.md ./ gomarkdoc --output docs/site/content/docs/reference/sdk/cli/api.md ./cli/ gomarkdoc --output docs/site/content/docs/reference/sdk/db/api.md ./db/ gomarkdoc --output docs/site/content/docs/reference/sdk/db/driver/modernc/api.md ./db/driver/modernc/ gomarkdoc --output docs/site/content/docs/reference/sdk/db/driver/ncruces/api.md ./db/driver/ncruces/ gomarkdoc --output docs/site/content/docs/reference/sdk/observer/otel/api.md ./observer/otel/ gomarkdoc --output docs/site/content/docs/reference/sdk/dst/api.md ./dst/ ``` - [ ] **Step 2: Run target to generate SDK pages** ```bash make docs-gen-sdk ``` Expected: gomarkdoc auto-installs (if missing), then writes seven markdown files. Verify: ```bash find docs/site/content/docs/reference/sdk -name api.md | wc -l # Expected: 7 ``` - [ ] **Step 3: Verify Hextra renders the generated pages** `gomarkdoc` writes plain markdown without YAML frontmatter. The `cascade.type: docs` from `docs/_index.md` covers theme treatment, and the page title comes from gomarkdoc's `# package` first heading. ```bash cd docs/site && hugo server ``` Visit `/docs/reference/sdk/libfossil/api/`. Expected: full package API renders — exported types, functions, methods — with monospace headings. The page title in the nav should read the package name (e.g., "libfossil"). If the title in the sidebar shows the file path instead of the package name (theme quirk), prepend `--- title: <pkg> ---` frontmatter to each `api.md`. Otherwise leave them as gomarkdoc emits them — keeps regeneration idempotent. - [ ] **Step 4: Commit** ```bash git add Makefile docs/site/content/docs/reference/sdk/ git commit -m "docs(site): add docs-gen-sdk Makefile target + commit generated SDK pages" ``` --- ## Task 5: gen-llms-txt.sh + Makefile target **Files:** - Create: `scripts/gen-llms-txt.sh` - Modify: `Makefile` - [ ] **Step 1: Write `scripts/gen-llms-txt.sh`** ```bash #!/usr/bin/env bash set -euo pipefail SITE_DIR="docs/site" STATIC_DIR="$SITE_DIR/static" mkdir -p "$STATIC_DIR" # llms.txt: curated summary cat > "$STATIC_DIR/llms.txt" << 'HEADER' # libfossil > Pure-Go library and CLI for Fossil SCM repositories. > Zero CGo. Static binaries. WASM-ready. ## Key Concepts - Repo — open .fossil SQLite-backed repository handle - Transport — pluggable sync interface (HTTP, custom) - Driver — pluggable SQLite implementation (modernc, ncruces) - Observer — hooks for sync/checkout/commit (OpenTelemetry-ready) - DST — deterministic simulation harness for fault injection ## Quick Start 1. go install github.com/danmestas/go-libfossil/cmd/libfossil@latest 2. libfossil clone <url> repo.fossil 3. Open from Go: repo, err := libfossil.Open("repo.fossil") 4. Sync: repo.Sync(ctx, transport) ## Go SDK Packages - libfossil — Repo handle, Open/Create, public API - cli/ — Kong command structs (embeddable) - db/ — SQLite abstraction with pluggable drivers - db/driver/modernc — pure-Go SQLite (default) - db/driver/ncruces — Wasm-friendly SQLite - observer/otel — OpenTelemetry integration - dst/ — deterministic simulation harness ## Documentation Full docs: https://github.com/danmestas/go-libfossil/tree/main/docs/site HEADER # llms-full.txt: all content concatenated, frontmatter stripped echo "# libfossil Documentation (Full)" > "$STATIC_DIR/llms-full.txt" echo "" >> "$STATIC_DIR/llms-full.txt" echo "Generated: $(date -u +%Y-%m-%dT%H:%M:%SZ)" >> "$STATIC_DIR/llms-full.txt" echo "" >> "$STATIC_DIR/llms-full.txt" find "$SITE_DIR/content" -name "*.md" -type f | sort | while read -r file; do echo "---" >> "$STATIC_DIR/llms-full.txt" echo "# Source: $file" >> "$STATIC_DIR/llms-full.txt" echo "" >> "$STATIC_DIR/llms-full.txt" sed -n '/^---$/,/^---$/!p' "$file" >> "$STATIC_DIR/llms-full.txt" echo "" >> "$STATIC_DIR/llms-full.txt" done echo "Generated llms.txt and llms-full.txt in $STATIC_DIR/" ``` - [ ] **Step 2: Make it executable** ```bash chmod +x scripts/gen-llms-txt.sh ``` - [ ] **Step 3: Add `docs-gen-llms` target to `Makefile`** In `.PHONY:` append `docs-gen-llms`. Append the target: ```makefile docs-gen-llms: bash scripts/gen-llms-txt.sh ``` - [ ] **Step 4: Run and verify** ```bash make docs-gen-llms ``` Expected last line: `Generated llms.txt and llms-full.txt in docs/site/static/` ```bash head -2 docs/site/static/llms.txt # Expected: # # libfossil # (blank line) wc -l docs/site/static/llms-full.txt # Expected: > 100 lines (concatenated content from quickstart + 4 moved docs + 7 SDK pages + section indexes) grep -c '^# Source: ' docs/site/static/llms-full.txt # Expected: number equal to all .md files under content/, i.e. > 12 ``` - [ ] **Step 5: Commit** ```bash git add scripts/gen-llms-txt.sh Makefile git commit -m "docs(site): add gen-llms-txt.sh + docs-gen-llms target" ``` The generated `static/llms*.txt` files are gitignored (Task 1 Step 1). --- ## Task 6: Pre-commit hook drift check for SDK pages **Files:** - Modify: `.githooks/pre-commit` The existing hook runs unit tests, vet, otel sub-module, and a binary build. Add an SDK-drift check after the binary build and before the final "all passed" line. - [ ] **Step 1: Add SDK drift check** Open `.githooks/pre-commit`. After the line `run_test "cmd/libfossil" go build ./cmd/libfossil/` and before `echo "=== pre-commit: all passed ==="`, insert: ```sh echo "=== pre-commit: docs SDK drift ===" if ! command -v gomarkdoc >/dev/null 2>&1; then if ! go install github.com/princjef/gomarkdoc/cmd/gomarkdoc@latest 2>/dev/null; then echo " gomarkdoc: SKIPPED (install failed; commit allowed)" fi fi if command -v gomarkdoc >/dev/null 2>&1; then if make -s docs-gen-sdk >/dev/null 2>&1; then if git diff --quiet -- docs/site/content/docs/reference/sdk; then echo " SDK pages: in sync" else echo " SDK pages: OUT OF DATE" echo "" echo " Run 'make docs-gen-sdk' and commit the result." echo "" git diff --stat -- docs/site/content/docs/reference/sdk exit 1 fi else echo " docs-gen-sdk: SKIPPED (target not available)" fi fi ``` The graceful skips (gomarkdoc install fails, target absent) keep the hook usable on fresh clones before Task 4 has run; once everything is wired, the drift check is enforced. - [ ] **Step 2: Verify hook catches drift** Pick any exported symbol in the codebase with a doc comment. Edit its comment to add a sentinel word, then attempt to commit: ```bash # Example: edit any doc comment in libfossil.go (adjust to whatever's actually there) grep -n '^// ' libfossil.go | head -3 # Pick a line, append a word to that comment line via your editor # e.g. change "// Open opens a repo." to "// Open opens a repo. SENTINEL" git add libfossil.go git commit -m "test: drift to verify hook" ``` Expected: hook prints `SDK pages: OUT OF DATE`, shows `git diff --stat`, exits 1, commit aborted. Restore: ```bash git checkout -- libfossil.go ``` - [ ] **Step 3: Verify hook passes when in sync** ```bash git commit --allow-empty -m "test: verify hook clean" ``` Expected: all sections pass, including `SDK pages: in sync`. Then drop the test commit: ```bash git reset HEAD~ ``` - [ ] **Step 4: Commit the hook change** ```bash git add .githooks/pre-commit git commit -m "hooks: pre-commit fails when SDK pages drift from code" ``` --- ## Task 7: Add docs-serve, docs-build, docs aggregate targets **Files:** - Modify: `Makefile` - [ ] **Step 1: Update `.PHONY:` and append targets** In `.PHONY:` append `docs-serve docs-build docs`. Append at the bottom of the file: ```makefile docs-serve: cd docs/site && hugo server -D --port 1313 docs-build: cd docs/site && hugo --minify docs: docs-gen-sdk docs-gen-llms docs-build @echo "=== docs: built into docs/site/public/ ===" ``` - [ ] **Step 2: Verify `docs-build`** ```bash make docs-build ls docs/site/public/index.html docs/site/public/docs/index.html # Expected: both exist grep -o '<title>[^<]*</title>' docs/site/public/index.html # Expected: <title>libfossil</title> (will change in Task 8 once landing layout exists) ``` - [ ] **Step 3: Verify `docs` aggregate** ```bash make docs ``` Expected: `docs-gen-sdk` runs (no diff if already in sync), `docs-gen-llms` runs, `docs-build` runs, final line `=== docs: built into docs/site/public/ ===`. - [ ] **Step 4: Verify `docs-serve`** ```bash make docs-serve ``` Expected: Hugo server starts on `:1313`. Stop with Ctrl-C. - [ ] **Step 5: Commit** ```bash git add Makefile git commit -m "docs(site): add docs-serve, docs-build, docs aggregate targets" ``` --- ## Task 8: Fork landing page — chrome (topbar, hero, metadata, footer) **Files:** - Create: `docs/site/layouts/index.html` Strategy: copy dagnats's live Hugo home layout verbatim, then perform targeted text replacements on the chrome regions (topbar, hero, metadata table, footer, `<head>` title/meta). The seven `§NN` content sections still contain dagnats prose at the end of this task; Task 9 rewrites them. - [ ] **Step 1: Copy the dagnats layout** ```bash cp ../dagnats/docs/site/layouts/index.html docs/site/layouts/index.html ``` (Adjust the source path if dagnats lives elsewhere relative to libfossil.) - [ ] **Step 2: Replace `<head>` title and description** In `docs/site/layouts/index.html`, replace the existing `<title>` and `<meta name="description">` lines with: ```html <title>libfossil — pure-Go Fossil SCM library</title> <meta name="description" content="libfossil — pure-Go library and CLI for Fossil SCM repositories. Zero CGo, static binaries, WASM-ready."> ``` - [ ] **Step 3: Replace topbar** Find the topbar region (near the top of `<body>`, contains `DAGNATS` left wordmark and version/license/github/docs on the right). Replace: - `DAGNATS` → `LIBFOSSIL` - Version label → `v 0.4.1` - License label → `license MIT` (replacing `Apache-2.0`) - GitHub link href → `https://github.com/danmestas/go-libfossil` - Docs link href → `/docs/` (already a relative URL in dagnats; verify it still points to `/docs/`) - [ ] **Step 4: Replace hero** Find the hero block (`<h1>dagnats</h1>` plus a paragraph tagline). Replace: - `<h1>dagnats</h1>` → `<h1>libfossil</h1>` - Tagline paragraph text → `A pure-Go library for Fossil SCM repositories. Zero CGo. Static binaries. WASM-ready.` - [ ] **Step 5: Replace spec-sheet metadata table** Find the metadata table (six rows: PROJECT, LANGUAGE, RUNTIME DEPS, BINARIES, LICENSE, STATUS). Replace right-column values: | Key | Value | |---|---| | PROJECT | libfossil — pure-Go Fossil SCM library | | LANGUAGE | Go (module github.com/danmestas/go-libfossil) | | RUNTIME DEPS | none · pluggable SQLite drivers (modernc · ncruces) | | BINARIES | 1 (libfossil CLI) | | LICENSE | MIT | | STATUS | v0.4.1 · alpha · breaking changes possible | If dagnats's table embeds a `<code>` link (e.g., `github.com/danmestas/dagnats`), update that link target to `github.com/danmestas/go-libfossil`. - [ ] **Step 6: Replace footer** Find the footer at the bottom of the page. Replace its text with: ``` libfossil · MIT · v0.4.1 · 2026 · github.com/danmestas/go-libfossil ``` (If the dagnats footer uses a `<a>` for the repo link, preserve the link; just swap the URL and label.) - [ ] **Step 7: Verify the chrome renders** ```bash make docs-serve ``` Open `http://localhost:1313/`. Expected: - Topbar: `LIBFOSSIL` left, `v 0.4.1 · license MIT · github · docs` right - Hero: "libfossil" plus the new tagline - Metadata table: rows with libfossil values from Step 5 - Footer: the libfossil text from Step 6 - The seven `§01`–`§07` sections still show dagnats content (Task 9 fixes them) - Toggle your OS color scheme: page swaps between cream-paper and dark-paper `grep -c 'dagnats' docs/site/layouts/index.html` should show several remaining dagnats references — those live in the §NN content blocks and are Task 9's job. Stop the server. - [ ] **Step 8: Commit** ```bash git add docs/site/layouts/index.html git commit -m "site(landing): fork dagnats home layout; rebrand chrome" ``` --- ## Task 9: Landing page — rewrite §01–§07 content for libfossil **Files:** - Modify: `docs/site/layouts/index.html` Each step rewrites one `<section>` block. The blocks are identifiable by `<span class="num">§NN</span>` and the `<h2>` slug anchor (`install`, `overview`, `why`, `architecture`, `comparison`, `limits`, `start`). Preserve the surrounding HTML structure (section tags, classes, COPY buttons, code-block markup, anchor IDs) and only replace text content. - [ ] **Step 1: §01 Install** Replace the install code block content with: ```sh go install github.com/danmestas/go-libfossil/cmd/libfossil@latest libfossil --help ``` Replace the prose below the code block with: > That's the entire install. The binary is pure Go, statically linked, and CGo-free — drop it on any Linux/macOS/Windows host with no runtime dependency. Keep the COPY-on-click button on the code block. - [ ] **Step 2: §02 What it is** Replace prose with two paragraphs: > libfossil is a pure-Go reader and writer for Fossil SCM repositories — the SQLite-backed `.fossil` files used by [Fossil](https://fossil-scm.org). It exposes a Go API for opening repos, reading the timeline, committing, and syncing over HTTP, plus a `libfossil` CLI driving the same code paths. > No CGo means static binaries that cross-compile cleanly. A `GOOS=wasip1` build runs in browsers and edge runtimes. Two SQLite drivers (`modernc`, `ncruces`) are swappable depending on whether you need pure-Go ergonomics or smaller WASM footprints. - [ ] **Step 3: §03 Why you might want it** Replace prose with: > Two reasons to reach for libfossil instead of shelling out to the upstream `fossil` binary or using CGo bindings. > **You're embedding Fossil into a Go service.** Build pipelines, code-search indexers, mirror servers, and AI-agent tools that read repository history all benefit from a library API instead of subprocess parsing. No exec calls, no version drift between hosts, observable through Go's standard `context.Context` and OpenTelemetry hooks. > **You're shipping to constrained targets.** Static binaries cross-compile in seconds; CGo bindings don't. WASM compilation works out of the box. The cost is a slightly slower SQLite (modernc is pure Go); the upside is everywhere-runs without a build farm. - [ ] **Step 4: §04 How it fits together** If dagnats's section uses an SVG diagram (`<svg class="diagram">…</svg>`), replace its node labels with libfossil's. If it uses a `<pre>` ASCII block, replace with this ASCII (preserving any wrapping `<pre>` and class): ``` ┌─────────┐ ┌──────────┐ ┌────────────┐ │ CLI │ ──▶ │ Repo │ ──▶ │ DB Driver │ └─────────┘ └────┬─────┘ │ modernc / │ │ │ ncruces │ ▼ └────────────┘ ┌──────────┐ │Transport │ │ (HTTP) │ └──────────┘ │ ▼ ┌──────────┐ │ Observer │ │ (OTel) │ └──────────┘ ``` Replace prose with: > Three pluggable axes. The **DB driver** decides how SQLite runs — `modernc` for pure Go, `ncruces` for smaller WASM footprints. The **Transport** decides how sync talks to a remote — HTTP today, custom transports for unusual networks. **Observers** hook into sync, checkout, and commit events for telemetry and auditing. > The CLI is a Kong-based wrapper over the same `Repo` API exposed to library users. Embedding the CLI commands into another Go binary is a one-import operation. - [ ] **Step 5: §05 Compared to neighbors** Replace prose with three short blocks: > **vs. shelling out to the `fossil` binary.** No subprocess, no parsing CLI output, no version drift between developer machines and CI. Read the timeline as `[]Commit` instead of regex-grepping text. Trades: the upstream binary still has features libfossil hasn't reimplemented; you can't reuse Fossil's GUI. > **vs. CGo bindings to the C libfossil library.** No CGo toolchain, no `libsqlite3.so` dependency, static binaries cross-compile cleanly, WASM works. Trades: pure-Go SQLite is slower than C SQLite (~2-3× on heavy queries); a few low-level Fossil internals are not yet exposed. > **vs. plain `git`.** Different VCS — pick libfossil only if your repos are Fossil. Use go-git for git. - [ ] **Step 6: §06 What it is not** Replace prose with three points: > **No GUI.** Fossil ships an embedded web UI; libfossil does not. If you need it, run the upstream `fossil` binary alongside. > **No hosting.** libfossil reads and writes `.fossil` files; it does not run an HTTP server that serves them to other Fossil clients (yet). Mirroring works as a sync target, not a host. > **No magic schema migrations.** libfossil tracks the upstream Fossil schema; if upstream introduces a breaking change, libfossil needs updating before you can open the new format. - [ ] **Step 7: §07 Get going** Replace prose with: > Read the [Quickstart](/docs/quickstart) — five-minute tour. Browse the [SDK reference](/docs/reference/sdk) — every package, every exported symbol. File issues at [github.com/danmestas/go-libfossil](https://github.com/danmestas/go-libfossil). Replace any code block in this section with: ```sh go install github.com/danmestas/go-libfossil/cmd/libfossil@latest ``` Preserve the COPY button if dagnats uses one here. - [ ] **Step 8: Verify** ```bash make docs-serve ``` Open `http://localhost:1313/`. Walk through every section: - `§01`–`§07` numbering and anchor slugs intact - All seven section bodies show libfossil content (no `dagnats` text remaining) - `grep -ci 'dagnats' docs/site/layouts/index.html` should return `0` - Code blocks copy cleanly when you click COPY - Internal links (`/docs/quickstart`, `/docs/reference/sdk`, `https://github.com/danmestas/go-libfossil`) work - §04 diagram lays out without horizontal overflow - Light/dark toggle still swaps cleanly Stop the server. - [ ] **Step 9: Commit** ```bash git add docs/site/layouts/index.html git commit -m "site(landing): rewrite §01–§07 content for libfossil" ``` --- ## Task 10: wrangler.jsonc + local CF build simulation **Files:** - Create: `wrangler.jsonc` - [ ] **Step 1: Write `wrangler.jsonc`** ```jsonc { "$schema": "node_modules/wrangler/config-schema.json", "name": "libfossil-docs", "compatibility_date": "2026-04-25", "observability": { "enabled": true }, "assets": { "directory": "./docs/site/public" }, "build": { "command": "curl -sSL https://github.com/gohugoio/hugo/releases/download/v0.160.0/hugo_extended_0.160.0_linux-amd64.tar.gz | tar xz hugo && bash scripts/gen-llms-txt.sh && cd docs/site && ../../hugo --minify" }, "compatibility_flags": ["nodejs_compat"] } ``` - [ ] **Step 2: Locally simulate the CF build** Cloudflare's build environment is Linux; we cannot use the exact curl URL on macOS. Substitute the local Hugo to verify the rest of the pipeline: ```bash rm -rf docs/site/public docs/site/static/llms.txt docs/site/static/llms-full.txt bash scripts/gen-llms-txt.sh ( cd docs/site && hugo --minify ) ``` - [ ] **Step 3: Verify deployable artifacts** ```bash ls docs/site/public/index.html docs/site/public/docs/index.html docs/site/public/llms.txt docs/site/public/llms-full.txt # All four should exist. grep -o '<title>[^<]*</title>' docs/site/public/index.html # Expected: <title>libfossil — pure-Go Fossil SCM library</title> grep -c 'libfossil' docs/site/public/index.html # Expected: > 20 matches grep -c 'dagnats' docs/site/public/index.html # Expected: 0 ``` - [ ] **Step 4: Commit** ```bash git add wrangler.jsonc git commit -m "deploy: add wrangler.jsonc for CF Workers Static Assets" ``` --- ## Task 11: Update README + open PR **Files:** - Modify: `README.md` - [ ] **Step 1: Update or add a "Documentation" section in `README.md`** Find any existing "Documentation" section. If it lists the four `docs/*.md` files individually, replace that list with a pointer to the docs site: ```markdown ## Documentation Full documentation: **https://libfossil-docs.<your-cf-subdomain>.workers.dev** _(set the actual URL after the CF dashboard is connected)_. Local preview: `make docs-serve`, then visit http://localhost:1313/. Markdown sources: [`docs/site/content/`](./docs/site/content/). ``` If no "Documentation" section exists, add one near the top after the project blurb. - [ ] **Step 2: Audit for stale links to moved docs** ```bash grep -nE '\(docs/(architecture|extension-points|migration-from-fossil|testing)\.md\)' README.md ``` For each match, update the path to either `docs/site/content/docs/<file>.md` (GitHub source) or to the served URL once known. Resolve any matches found. - [ ] **Step 3: Final dry-run of the full pipeline** ```bash make docs git status ``` Expected: `docs` target completes; `git status` reports `working tree clean` (the pre-commit hook would have caught any drift on previous commits). - [ ] **Step 4: Push the branch** ```bash git add README.md git commit -m "docs: point README at the new docs site" git push -u origin feat/docs-site-cf-deploy ``` - [ ] **Step 5: Open the PR** ```bash gh pr create --title "docs site + landing + Cloudflare auto-deploy" --body "$(cat <<'EOF' ## Summary - Adds Hugo + Hextra docs site at `docs/site/` - Adds Hugo home-page layout override (`docs/site/layouts/index.html`) for the landing page, mirroring the dagnats live design (mono-only, dual-mode, single ember accent, 7 numbered sections) - Moves the four existing `docs/*.md` files under `docs/site/content/docs/` - Adds `gomarkdoc`-generated SDK reference under `docs/site/content/docs/reference/sdk/` (committed; pre-commit hook fails on drift) - Adds `scripts/gen-llms-txt.sh` for `/llms.txt` and `/llms-full.txt` - Adds `wrangler.jsonc` for Cloudflare Workers Static Assets auto-deploy via CF's git integration ## Operational follow-ups (manual, after merge) 1. Cloudflare dashboard → Workers & Pages → Create → Connect to Git → select this repo 2. Production branch = `main`; build/output come from `wrangler.jsonc` 3. Default URL: `libfossil-docs.<your-cf-subdomain>.workers.dev`. Attach a custom domain when ready. 4. Update `README.md` and `hugo.yaml`'s `baseURL` once the final URL is known. ## Test plan - [ ] `make docs` builds without errors - [ ] `make docs-serve` shows the landing at `/`, docs at `/docs/`, SDK at `/docs/reference/sdk/` - [ ] Pre-commit hook fails when SDK pages drift, passes when in sync - [ ] After CF connects to the repo, the deployed URL serves the same content as local EOF )" ``` Per project policy, do NOT add a "Generated with Claude Code" footer to the PR body, and do NOT auto-merge. The user merges manually after review. --- ## Self-review checklist (engineer running this plan) After all tasks complete, before opening the PR: 1. `git log feat/docs-site-cf-deploy --oneline | head -20` — every commit has a meaningful, conventional message. 2. `git log feat/docs-site-cf-deploy --grep='Co-Authored-By: Claude'` returns nothing. 3. `make docs` runs cleanly from a fresh checkout (test by `git stash && rm -rf docs/site/{public,static/llms*.txt,resources,_vendor} && make docs`). 4. The pre-commit hook actually catches SDK drift (Task 6 Step 2 verified once; re-verify if any later task touched the hook). 5. Landing page renders identically in light and dark mode; toggle the OS color scheme to confirm. 6. `grep -ci 'dagnats' docs/site/layouts/index.html` returns `0`.