Follow-up to #117. That PR got `<lastmod>` publishing at all; this one makes the dates worth resubmitting. ## The problem **190 of 193 pages read `2026-08-25`** — the date of #115, which added `seo_title`/`seo_description` to all 192 pages. Here is everything it changed in a representative file: ```diff +--- +seo_title: "Key Concepts: Companies, Agents, Tasks" +seo_description: The seven ideas behind Paperclip — company, agent, task... +--- + # Key Concepts ``` Not a word of prose. `lastmod` is meant to carry the date a page's **content** last changed, so resubmitting this hands Search Console the "whole site changed at once" signal that `dropUniformLastmod` and the shallow-checkout guard both exist to prevent. It cleared `dropUniformLastmod` only because 3 pages happened to differ. ## The fix Walk each file's history and report the newest commit that changed the **rendered body** — comparing `parseFrontmatter().body` across consecutive commits, so frontmatter-only commits are skipped. Reusing the build's own parser means the body compared is exactly the body the site renders. | | Before | After | |---|---|---| | Distinct dates | **2** | **31** | | Largest single-date cluster | 190 pages | 54 pages | | Range | 2 days | 2026-04-17 → 2026-08-27 | Top of the new distribution: | Date | Pages | |---|---| | 2026-08-18 | 54 | | 2026-06-04 | 21 | | 2026-08-25 | 17 | | 2026-07-08 | 16 | | 2026-07-20 | 15 | | 2026-04-22 | 12 | Spot-checked both directions: - `key-concepts.md` — last touched by the frontmatter sweep, now dates to **2026-07-08**, the commit that changed "Claude Local" to "Claude Code" in the body. - `day-to-day/issues.md` — **keeps 2026-08-25**, because release commit `6c4519a` that day added a real "Who may resolve a card" section. Its frontmatter-only `paperclip_version` bump in the same release was correctly skipped. ## Two supporting fixes **Concurrency.** ~600 git round-trips in series cost 40s. Resolving them in parallel: **11s**. **A race the concurrency exposed** — and the reason this is worth reading closely. `hasTrustworthyGitHistory()` memoised its *resolved value*, so once all 192 pages asked at once, every one of them passed the guard before the first resolved: ``` Could not deepen the shallow checkout: fatal: Unable to create '.git/shallow.lock': File exists. Still shallow: omitting sitemap <lastmod> rather than dating every page alike. × 192 ``` 192 concurrent `git fetch --unshallow` calls fighting over the lock, and **a sitemap with no dates at all**. This only reproduces on a shallow clone — which is to say only on Cloudflare Pages, never locally or in `docs-test`. Fixed by memoising the promise (single-flight). ## Verification | Scenario | Result | |---|---| | Full clone | 193 dates, 31 distinct, 11s | | `git clone --depth 1` (reproduces CF) | one unshallow, **193 dates, 31 distinct** | | Shallow + unreachable remote | no dates, exit 0 | | `npm run docs:test` (6 suites) | pass | | `npm run sync:test` | 39 pass, 0 fail (+1 new) | New unit test covers the `--follow --name-status` rename parsing — 55 docs files have been renamed, and reading the wrong field silently drops their pre-rename history. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 5 (1M context) <[email protected]>
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 —
paperclipcommand 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
maindeploy production athttps://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.
That auto-deploy is not guaranteed — it has silently failed to fire on a push to main before. After anything reader-facing lands, check the live site rather than the merge, and republish by hand if it's stale:
npm run docs:build
npx wrangler pages deploy .site --project-name paperclip-docs --branch main
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.