e2adb1c167 PR1 — server-render the docs root and split it from interior routes (Phase 1) [DO NOT MERGE] (#87)
# PR1 — server-render the docs root and split it from interior routes
(Phase 1)

**Do not merge — Dotta alone may merge.** This PR is opened for review
only, per the hard write-gate on the approved plan.

## Provenance

| item | link |
|---|---|
| Accepted plan | [PAP-16672 · plan
document](https://paperclip-dev.tail29c1aa.ts.net/PAP/issues/PAP-16672#document-plan)
|
| Phase 0 review packet | [PAP-16671 · packet
comment](https://paperclip-dev.tail29c1aa.ts.net/PAP/issues/PAP-16671#comment-f653273d-6450-4f8d-bcd6-f805ad28b923)
|
| Implementation candidate |
[PAP-16682](https://paperclip-dev.tail29c1aa.ts.net/PAP/issues/PAP-16682)
|
| Independent QA — **PASS** |
[PAP-16683](https://paperclip-dev.tail29c1aa.ts.net/PAP/issues/PAP-16683)
|
| Finalization / this PR |
[PAP-16684](https://paperclip-dev.tail29c1aa.ts.net/PAP/issues/PAP-16684)
|
| Full evidence gallery (screenshots + raw JSON probes) |
https://pages.paperclip.ing/pap-16684-pr1-evidence/ |

Base: approved `main` @ `2ab76e8e4`. Head: `f5bbba24c` (exactly one
commit on top; `main` has not moved).

## What changed

- **1.1 — root-only landing shell.** The hero is *physically absent*
from interior documents (removed at build time, not CSS/JS/ARIA hidden);
the build throws if removal fails. Header, footer, search, sidebar,
breadcrumbs and article nav are preserved on every page.
- **1.2 — server-rendered homepage directory** generated from the same
canonical `site/content.json` manifest that already feeds the sidebar,
breadcrumbs, prev/next, routes and sitemap. No second URL manifest was
introduced.
- **Single descriptive H1 per interior page** (was 2 — the hero H1 bled
onto every route).
- **Truthful runtime UI.** `Loading…` / `Could not load this guide.` no
longer ship inside success HTML; they are produced only by `app.js` at
runtime on the empty, `hidden` `#runtime-status` mount, so they can only
appear during a genuine client transition or failure.

```
 scripts/verify-static-routes.mjs | 200 +++++++++++++++++++++++++++++++++-
 site/app.js                      |  90 ++++++++++++----
 site/build-release.mjs           | 189 ++++++++++++++++++++++++++++++--
 site/index.html                  |  17 ++--
 site/styles.css                  |  70 ++++++++++-
 5 files changed, 528 insertions(+), 38 deletions(-)
```

`.site/` is **generated build output and remains uncommitted**
(gitignored): `git ls-files | grep '^\.site/'` → 0 files.

## Before → after, raw HTTP/HTML for the six representative routes

Both trees built with the identical command and base path, then served
by a production-equivalent static server (real 404 status, honors
`_redirects`). "Before" is a real build of approved `main` @
`2ab76e8e4`.

| route | HTTP | content type | H1 count before → **after** | H1 text
(after) | `id="landing"` before → after | `Loading…` | `Could not load
this guide.` | internal directory anchors |
|---|---|---|---|---|---|---|---|---|
| `/` | 200 | `text/html` | 1 → **1** | Everything you need to run
Paperclip. | 1 → 1 (root-only ✓) | 1 → **0** | 1 → **0** | 4 → **184** |
| `/guides/welcome/what-is-paperclip/` | 200 | `text/html` | 2 → **1** |
What is Paperclip? | 1 → **0** | 1 → **0** | 1 → **0** | 4 → 4 |
| `/reference/api/overview/` | 200 | `text/html` | 2 → **1** | API
Overview | 1 → **0** | 1 → **0** | 1 → **0** | 4 → 4 |
| `/how-to/add-mcp-server-to-agent/` | 200 | `text/html` | 2 → **1** |
Add an MCP server to an agent's toolkit | 1 → **0** | 1 → **0** | 1 →
**0** | 4 → 4 |
| `/reference/changelog/` | 200 | `text/html` | 2 → **1** |
Documentation Changelog | 1 → **0** | 1 → **0** | 1 → **0** | 4 → 4 |
| `/guides/welcome/this-route-does-not-exist/` | **404** | `text/html;
charset=utf-8` | 1 → **1** | Not found | 0 → 0 | 0 → 0 | 0 → 0 | 0 → 0 |

Machine-readable probes:
[before-raw.json](https://pages.paperclip.ing/pap-16684-pr1-evidence/before-raw.json)
·
[after-raw.json](https://pages.paperclip.ing/pap-16684-pr1-evidence/after-raw.json).

### Homepage bleed and false state — absence proof

- `id="landing"` returns **zero** matches in every interior document.
Only inlined `#landing-*` **CSS selector text** remains (stylesheet, not
markup).
- Interiors carry no directory manifest (4 internal anchors — the
footer); the 184-anchor directory is root-only.
- The two false-state phrases appear in **0** built HTML files across
the whole bundle.

### 404 / noindex / sitemap

- Real HTTP **404** for the nonexistent route (root base and
`/paperclip-docs/` subpath build).
- `404.html`: exactly one `<h1>Not found</h1>`, `<meta name="robots"
content="noindex, nofollow">`, `<title>Not found | Paperclip
Docs</title>`.
- Absent from `sitemap.xml` (0 occurrences); no built file for the
route.
- `sitemap.xml` has 184 `<loc>` (183 routes + root) and the root
directory anchor set covers **183/183** non-root sitemap routes — 0
missing.
- `_redirects` contains **no wildcard `200` rewrite** (0 lines with a
`200` status), so unknown routes cannot be masked as success.
- `robots.txt`: `User-agent: * / Allow: /` + `Sitemap:
https://docs.paperclip.ing/sitemap.xml`.

## Focused checks — rerun on the final revision (`f5bbba24c`)

| command | result |
|---|---|
| `npm run docs:test:static-routes` | **PASS** — "Static route
verification passed (5 representative routes +
/guides/welcome/this-route-does-not-exist/, root and /paperclip-docs/
builds)." |
| `npm run docs:build` | **PASS** — "Generated 183 crawlable route pages
plus sitemap.xml and robots.txt." |

`git status` is clean after the build (no generated output enters the
tree).

## Browser matrix — desktop 1440×900 and mobile 390×844

Run with Playwright + Chromium 1228. The QA sandbox could not exercise
this (blank-glyph text rendering); the root cause turned out to be that
**no fonts are installed on the host at all** — installing DejaVu TTFs
with a minimal fontconfig makes text render correctly, which unblocked
the full interactive matrix here. Screenshots:
https://pages.paperclip.ing/pap-16684-pr1-evidence/

### Per-route, JavaScript enabled (both viewports)

All six routes on both viewports: expected HTTP status, **exactly 1
`<h1>`** with the correct text, `#landing` present **only** on `/`, no
`Loading`/`Could not load this guide.` text in the rendered body,
`#runtime-status` hidden, **no horizontal overflow**.

### Interactive

| check | result |
|---|---|
| root directory link → interior (client nav) | ✅
`/guides/welcome/what-is-paperclip/`, single H1 "What is Paperclip?",
hero gone, not stuck loading |
| direct load vs client-nav parity | ✅ identical H1 set |
| back / forward history | ✅ back → `/` with hero restored; forward →
interior with correct H1 |
| home nav from an interior doc | ✅ `/` with hero |
| theme toggle | ✅ toggles (`dark` → light) |
| desktop sidebar / TOC / breadcrumbs / prev-next / search / footer | ✅
191 sidebar links, 14 TOC links, breadcrumbs rendered, article nav
present, search control present, 15 footer links |
| mobile drawer | ✅ opens (screenshot `mobile-drawer-open.png`) |
| mobile horizontal scroll | ✅ none |
| unknown route | ✅ 404 page, single H1, noindex |

### JavaScript disabled

| check | result |
|---|---|
| `/` directory usable with JS off | ✅ 202 anchors present in raw
markup; hero + card directory fully rendered |
| clicking a directory link with JS off | ✅ full page load to
`/reference/api/overview/` |
| that interior page with JS off | ✅ 1 H1 ("API Overview"), no hero, no
false loading/error state, 6368 chars of real article text |
| mobile with JS off | ✅ root and interior both render correctly |

### Console / network

No JavaScript errors and no page errors on any route. Exactly two
non-2xx requests, **both reproduced identically on approved `main` @
`2ab76e8e4`** — neither is introduced by this PR:

- `GET /redirects.json` → 404. Pre-existing: neither the before nor
after build emits this file. Verified byte-for-byte identical behaviour
on `main`.
- `GET https://api.github.com/repos/paperclipai/paperclip` → 403.
Unauthenticated GitHub API rate limit for the header star counter;
environmental, external, not a product defect.

Also confirmed unchanged before → after on the same interior page:
header (1), footer (1), search controls (4), sidebar links (191), TOC
links (14), article nav (2), article text length (4328 chars). The
**only** rendered difference is the intended H1 2 → 1 and the removed
hero.

## Verified against the real Cloudflare Pages deploy, not just a local
server

The Cloudflare Pages check on this PR is **green** and produced a live
preview:
**https://pap-16682-server-render-root.paperclip-docs-74t.pages.dev**

I reran the entire matrix against that real deploy. Every invariant
reproduces **identically** to the local run — so these results hold on
actual production hosting, including Cloudflare's own 404 handling:

| route | HTTP | H1 | `#landing` | `Loading…` | `Could not load…` |
noindex | anchors |
|---|---|---|---|---|---|---|---|
| `/` | 200 | 1 — Everything you need to run Paperclip. | present
(root-only) | 0 | 0 | – | 184 |
| `/guides/welcome/what-is-paperclip/` | 200 | 1 — What is Paperclip? |
absent | 0 | 0 | – | 4 |
| `/reference/api/overview/` | 200 | 1 — API Overview | absent | 0 | 0 |
– | 4 |
| `/how-to/add-mcp-server-to-agent/` | 200 | 1 — Add an MCP server to an
agent's toolkit | absent | 0 | 0 | – | 4 |
| `/reference/changelog/` | 200 | 1 — Documentation Changelog | absent |
0 | 0 | – | 4 |
| nonexistent route | **404** | 1 — Not found | absent | 0 | 0 | **yes**
| 0 |

Browser matrix on the live preview, desktop 1440×900 and mobile 390×844:
identical outcomes — client nav, direct-load parity, back/forward,
home-nav, theme toggle, 191 sidebar links, 14 TOC links, mobile drawer
opens, no horizontal scroll, and with **JavaScript disabled** the root
directory (202 anchors) navigates by real page load into an interior
with 1 H1, no hero, no false state and 6368 chars of article text.

**Console/network on the live deploy: zero page errors.** The same two
known non-product requests appear (`/redirects.json` and the GitHub
star-count API). `/redirects.json` returns **404 on live production
`docs.paperclip.ing` today** and on the `main` branch preview —
conclusively pre-existing and unrelated to this PR.

Screenshots from both the Cloudflare deploy and the local server are in
the gallery: https://pages.paperclip.ing/pap-16684-pr1-evidence/

## Out of scope — pre-existing observation, not a regression

On the `--base-path /paperclip-docs/` subpath build, 4 footer links
(`/reference/api/overview`, `/guides/org/agents/`,
`/guides/org/adapters/`, `/guides/getting-started/five-minute-path/`)
are emitted un-prefixed. Building approved `main` @ `2ab76e8e4` with the
identical flag reproduces **exactly the same 4 links**, so this is a
pre-existing footer-template issue untouched by this PR. Smallest fix
would be routing the footer's hardcoded hrefs through the base-path
prefixer, in a separate ticket.

## Merge policy

`.site/` is generated output and is intentionally uncommitted. **This PR
must remain open and unmerged — Dotta alone may merge it.**

Co-authored-by: ClaudeCoder <[email protected]>
Co-authored-by: Claude Opus 5 (1M context) <[email protected]>
2026-08-10 13:11:38 -07:00
2026-04-30 11:04:18 -05:00

Paperclip Docs

Source for the Paperclip documentation site — user guides, API and CLI reference, adapter docs, and deployment notes.

Read the docs: https://docs.paperclip.ing/

What's inside

  • Guides — Getting started, core concepts, and day-to-day usage.
  • API reference — Control-plane endpoints and payload schemas.
  • CLI reference — paperclip command surface.
  • Adapters — Plugin authoring and the integrations catalogue.
  • Deploy — Self-hosting, upgrades, and operational guidance.

The site is a single static shell that renders the Markdown files in docs/ directly — no framework, no build step required to read the raw pages.

Build and deployment

Build a release bundle locally with:

npm run docs:build

Cloudflare Pages is connected directly to paperclipai/paperclip-docs through GitHub. There is no normal Wrangler publish step for this repo:

  • Pushes to main deploy production at https://docs.paperclip.ing/.
  • Pushes to other branches create Cloudflare Pages preview/canary deployments.
  • Canary URLs are created by Cloudflare for each deployment, for example https://92b9a99c.paperclip-docs-74t.pages.dev. Use the URL shown in the Cloudflare Pages deployment row or GitHub deployment/check for the pushed branch and commit.

Contributing

Spotted a typo, a broken link, or something that could be clearer? There are two easy paths, both linked from the footer of every docs page:

  • Suggest an edit — opens the underlying Markdown file in GitHub's web editor. Make the change and submit a PR without leaving the browser.
  • Report an issue — opens a prefilled Docs feedback issue with the page URL already attached.

For larger changes, fork the repo and open a PR against main. Screenshots live under docs/user-guides/screenshots/{light,dark}/ and should be provided in both themes when replacing UI captures.

Community

License

Copyright (c) 2026 Paperclip Community.

The Paperclip software is open source — see the main paperclip repository for its license.

The documentation in this repository is licensed under Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International (CC BY-NC-ND 4.0). You may read and share it for non-commercial purposes with attribution. Commercial use of any kind requires prior written permission from the copyright holders. See LICENSE for the full text.

S
Description
Paperclip documentation
Readme CC-BY-4.0
491 MiB
0 Stars 1 Watchers 0 Forks
Languages
JavaScript 91.1%
CSS 7.1%
HTML 1.6%
Shell 0.2%