mirror of
https://github.com/paperclipai/paperclip-docs.git
synced 2026-11-07 06:16:07 -10:00
codex/github-low-trust-docs
23
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
39633c2217 |
docs: connectors catalog, operator-first third revision (#124)
Connector catalog, setup guides, and shared access documentation, followed by a final code-accuracy repair pass. This PR does not change application behavior. ## Final accuracy pass Checked against shipped Paperclip `v2026.916.0`, commit `dffc2b3ca1b9e88fa21cb17493083e682dffd1ca`, rather than the dirty local checkout or the earlier canary baseline. - Corrects connector/method-specific identity choices, human audiences, and managed GitHub versus PAT scope and verification. - Explains that **Always allow** covers different arguments for the same agent/action/connection, with project scope when present; uses actual button labels and removes the test-panel control from review instructions. - Qualifies new-action quarantine, including safe-default read exceptions. Removes unconditional approval guarantees. - Separates provider channel/repository reach from Paperclip sender authorization. - Completes sandbox sign-in and model credential-selection instructions from the current UI implementation. - Repairs Notion's hub summary, removes incomplete optional Notion/Sentry client-registration procedures, and corrects Linear's provider-versus-Paperclip attribution. - Removes unsupported Postman tier claims, contradictory Cloudinary/Mem0 verification assertions, API-equivalence claims, and overbroad successful-test assurances. - Retains Supabase's required project reference: both released method definitions explicitly require it. An earlier review suggested otherwise; release code is authoritative here. - Updates the release baseline and removes draft commit chronology from the public introduction. ## Availability and remaining runtime gates - **Google:** Verification is pending. Per the product-owner clarification, authorization can continue where Google offers an Advanced option; this is not available for every account and does not override Workspace policy or other Google prerequisites. Notices on the hub and all Google guides now distinguish an unverified-app warning from blocked authorization. - **Slack is available but not yet fully functional; follow-up fixes are planned.** For agent tools, the released OAuth endpoint/scope defaults differ from Slack's documented MCP contract. The page now warns against treating that route as production-ready and removes the misleading bot-scope walkthrough. Chat-channel bot setup remains separate. Engineering must reconcile the integration and run OAuth plus one read before removing this warning. Source: https://docs.slack.dev/ai/slack-mcp-server/ - No credentialed connector run was performed in this pass. Code consistency, provider documentation, UI rendering, and successful live execution are separate evidence levels. No claim of runtime certification for all connectors. ## Validation - Root build: 253 crawlable documentation pages. - Full `docs:test`: all eight suites passed locally, including root/subpath routes, sitemap history, fingerprints, navigation, skill source blocks, crawlable links, doc-link resolution, and SEO metadata. - Internal-link lint: 258 Markdown files; no broken internal links. - Latest cleanup: full build, all eight docs:test suites, internal-link lint and diff whitespace checks passed; 1,878 link-resolution checks. - Fixed the Calendar MCP reference URL; browser-checked the five inconclusive external destinations (Miro article loads; Asana, Google Cloud, OpenAI and xAI reach sign-in/console screens). Authenticated destinations were not tested beyond those screens. - Replaced the Sandbox providers editorial TODO with a scope note and removed the CLI overview's unfinished-guide announcement. - Browser client navigation: 687 checks passed using installed Chrome. Initial launch lacked the downloaded Playwright browser; rerun used `DOCS_CHROMIUM_PATH` without changing the tests. - Nine representative connector/shared pages at 1440px and 390px: HTTP 200, one article H1, no page-level horizontal overflow. Mobile screenshot inspected; approval page opened in the Codex browser. - Navigation audit reports three existing non-connector orphan files; this patch does not alter those files or their navigation. The previous revision's provider-page expansion, Google shared setup guide, navigation additions, and accidental node_modules-symlink cleanup remain in this PR. Internal review ledgers remain outside the publication branch. No merge or production deployment authorized. --------- Co-authored-by: DevRel Gemini <[email protected]> Co-authored-by: Paperclip <[email protected]> Co-authored-by: Claude Opus 5 (1M context) <[email protected]> Co-authored-by: Michael Nguyen <[email protected]> |
||
|
|
4644dda3a3 |
test: guard sitemap lastmod against shallow clones and metadata sweeps (#119)
Picks up the regression test from #104 (@cryppadotta) — closed as superseded, but its fixture design was the best part of it and worth landing on its own. ## Why this gap mattered The sitemap `lastmod` bug shipped twice, and **neither failure was detectable by any check in `docs:test`**: | | Symptom | Why nothing caught it | |---|---|---| | #117 | 0 dates in production for weeks | Shallow checkout doesn't make `git log` *fail* — it succeeds, returning one date per file | | #118 | 192 concurrent `git fetch --unshallow` fighting over `.git/shallow.lock`, dateless sitemap | Reproduces only on a shallow clone — never locally, never in CI | Both are Cloudflare-only failure modes. The only detector we had was reading the live sitemap, which is how this whole thread started. ## What landed `scripts/verify-sitemap-lastmod-history.mjs`, wired into `docs:test`. Three cases against **real depth-1 clones** — the fixture design and `--no-local` insight are @cryppadotta's: 1. Full history publishes real per-document dates 2. Shallow clone that can reach origin recovers history 3. Shallow clone with unreachable origin omits dates rather than lying Two changes on top of the original: **Overlay the working-tree builder into the clone.** The clone carries committed HEAD, so without this the shallow cases silently test the last commit instead of the change being made. No-op in CI; the difference is real locally, and it is what makes the check usable while iterating. **Assert no single date covers >75% of pages.** The existing "more than one distinct date" assertion passes at *2* distinct dates — precisely the state #118 fixed, where 190 of 193 pages carried the date of a frontmatter-only sweep. Bounding the largest cluster catches that class without pinning a date that legitimate edits will move. ## Verified by breaking it A check that has never failed proves nothing, so I reverted each fix in turn: **Revert body-aware dating (#118):** ``` ok lastmod values vary across documents (2 distinct) rather than one build date FAIL full history: no single date dominates (2026-08-25 covers 190/193, 98% <= 75%) ``` Note the pre-existing assertion passing right above the failure — that is the gap this closes. **Revert the single-flight memo (#118):** ``` FAIL unshallowed build dates every route (2/193) FAIL unshallowed build produces varied dates (1 distinct) ``` The `.git/shallow.lock` race, caught by Case 2 and nothing else. Restored, clean run is green: 193/193 dated, 31 distinct, largest cluster 28%. ## Cost **~55s** added to `docs:test` for three builds (`docs-test` was 32s). Steep for a test suite, and worth flagging rather than burying — but the alternative detector for this failure class is noticing the live sitemap is wrong. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5 (1M context) <[email protected]> |
||
|
|
325c9eb819 |
seo: hand-write titles and descriptions for all 192 pages (#115)
## Why
The build derived `<title>` from the **sidebar label** and the
description from the **first paragraph clipped to 220 characters**. Both
are the wrong source. A sidebar label is written for a 240px-wide nav
that supplies its own context; a search result has none.
On production that produced:
- **7 pages titled "Overview"**, and 32 pages sharing a title with
another page
- **109 of 193 descriptions cut off mid-word**, and 174 over 160
characters (where Google truncates)
```
/guides/welcome/key-concepts/
BEFORE (220): ...and if you misunderstand them early, the product wi
```
Near-identical titles are the weak-differentiation signal behind
"crawled — currently not indexed", which is the exact symptom on this
property.
## What changed
`seo_title` and `seo_description` frontmatter on **every one of the 192
pages**, written by hand against each page's real content and optimised
for the click — lead with what the reader can do, name the concrete
things covered, no keyword stuffing, no repeating the title.
| | Before | After |
|---|---|---|
| Duplicate titles | 32 pages | **0** |
| Duplicate descriptions | — | **0** |
| Descriptions cut mid-word | 109 | **0** |
| Descriptions over 160 chars | 174 | **0** |
| Title length (incl. suffix) | up to "Overview" | **26–60** |
| Description length | 31–240 | **122–158** |
**No prose was rewritten.** I measured content overlap between the
colliding pairs first — only 17–23%, so these were always distinct pages
wearing the same label, not duplicate content. The `docs/` diff is **564
insertions, zero deletions**.
### Examples
```
BEFORE — all seven identical: "Overview | Paperclip Docs"
AFTER: "Adapters Overview" "API Overview" "CLI Overview"
"Deployment Overview" "Bundled Skills" "Optional Skills"
"Experimental Features"
BEFORE — both identical: "Issues | Paperclip Docs"
AFTER: "Issues: Creating and Tracking Work" /guides/day-to-day/issues/
"Issues API" /reference/api/issues/
```
```
/guides/getting-started/five-minute-path/
BEFORE (220): The shortest route from a running Paperclip instance to an agent that has
just completed work for you. After Paperclip is installed, the path takes
about 5 minutes: create a company, hire a CEO agent, approve its first s
AFTER (131): Install Paperclip, create a company, hire a CEO agent, and approve its
first strategy — a working AI company in about five minutes.
```
## Plumbing
- **`site/build-release.mjs`** prefers the authored fields; the derived
path stays only as a fallback the CI gate prevents ever reaching.
`<title>`, `<meta name="description">`, and the `TechArticle` JSON-LD
all read from the same source.
- **`site/app.js`** uses the same fields, so a client-side transition
cannot leave a different title on screen than the crawler was served.
Verified in-browser: navigating `/guides/day-to-day/issues/` →
`/reference/api/issues/` swaps to exactly the server-rendered values, no
JS errors.
- **`scripts/verify-seo-metadata.mjs`** (new) enforces presence,
uniqueness, length, complete-sentence endings, and the
frontmatter-safety rules. Wired into `docs:test` and CI as its own step.
I negative-tested the gate rather than assuming it works — it correctly
fails on a duplicate title, a mid-word description, and a missing field,
and passes again once restored.
## Automating it going forward
`skills/sync-docs/SKILL.md` now:
- gains an **"Authored SEO metadata"** section: why the fields exist,
the enforced rules as a table, and how to actually write them (title
names the subject not the nav position; description writes for the
click; never generate mechanically from the first paragraph — *that is
the fallback the gate exists to prevent*).
- requires both fields in the **page-authoring instruction** given to
every subagent, with guidance on when to update an existing description
versus leave it.
- runs `docs:test:seo-metadata` and `docs:test:crawlable-links` as
**fail-loud gates in Phase 7** before commit.
- documents all six build gates in the files/gates section.
So once this lands, a new page cannot ship without hand-written
metadata, and a title collision cannot reach production.
## Verified
All six verifiers pass:
```
Static route verification passed
Asset fingerprint verification passed
Hierarchical Skills nav verification passed
Skill source block verification passed (11 skill pages, 1 control page)
Crawlable-link contract passed: 40886 internal links across 194 pages resolve in one hop
Authored-SEO contract passed: 192 pages carry a unique, hand-written seo_title and seo_description
```
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 5 (1M context) <[email protected]>
|
||
|
|
29beedeb6a |
seo: make the docs link graph crawlable (#113)
## Why Google has indexed effectively nothing on `docs.paperclip.ing`. Search Console over 3 months: **8 of 192 URLs** have ever drawn an impression, **681 of 975 impressions (70%) go to `/guides/agent-developer/comments-and-communication`** — a legacy URL that 301s and isn't in the sitemap. Strip that, the homepage, and the team's own `site:` queries, and the canonical docs corpus earned **~7 organic impressions in 90 days**. It isn't an eligibility problem. Pages are fetchable, self-canonical, `index, follow`, and identical under a Googlebot UA. It's the internal link graph. ## What was actually broken **1. Every relative markdown link 404'd.** Authors write `./glossary.md`, but pages are served from trailing-slash route directories, so links resolved one level too deep: ``` page: /guides/welcome/key-concepts/ href: ./glossary.md → /guides/welcome/key-concepts/glossary.md 404 ( /guides/welcome/glossary.md would be 200 ) ``` Crawled all 192 live pages: **all 742 unique `.md` targets return 404**. That's **1,283 of 1,306 in-body internal links — 98% — broken**, leaving **0.1 working in-body links per page**. It went unnoticed because `app.js:postProcessInternalLinks` repairs these at runtime. Crawlers index the pre-render. **2. The sidebar wasn't server-rendered.** Interior pages shipped `<div id="sb-sections"></div>`, filled by JS. So the raw HTML offered the logo, prev, next — and ~7 dead links. No link equity reached deep pages. Search Console classified 98% of crawling as refresh, 2% as discovery. Net effect: a crawler saw every page as a dead end that was 98% broken links. Crawl demand collapsed — 14 crawled of 184, 15% of crawl responses 404. ## Changes - **Rewrite markdown links to canonical routes at build time**, and fail the build on any link no nav page publishes. (Passed first run with zero unresolved — every link had a valid destination, just emitted in a 404ing form.) - **Server-render the sidebar and breadcrumb** into every interior route from the manifest `app.js` already uses. `app.js` detects `data-server-rendered` and keeps the DOM rather than re-rendering identical markup. The mobile drawer stays client-built — it's `inert`/`aria-hidden` until a JS toggle opens it, so rendering it duplicated 192 links/page (~41KB) for no crawl benefit. - **Intercept canonical route links in articles** so in-page navigation stays a soft transition instead of a full reload. - **Omit sitemap `<lastmod>` when git history is untrustworthy.** Reproduced the real mechanism: in a `--depth=1` clone, `git log -1` returns the single fetched commit's date for *every* file. Two files that differ locally (`2026-08-18` / `2026-07-08`) both come back `2026-08-25` shallow. That is exactly the live `192 × 2026-08-20`. Cloudflare Pages shallow-clones by default. - **Ship `redirects.json`** — `app.js` fetches it at boot and it 404'd. - **Canonicalise the footer's `/reference/api/overview`** link, which spent a 301 on all 193 pages. - **Add `scripts/verify-crawlable-links.mjs` + CI**, gating on raw `.md` hrefs, missing targets, redirecting targets, relative hrefs, an unrendered sidebar, and uniform sitemap dates. The last two were both caught by the new verifier on its first run. ## Result | | Before (production) | After (generated bundle) | |---|---|---| | Broken in-body links | 1,283 (98%) | **0** | | Working in-body links | 23 | **1,501** | | Crawlable nav links/page | 0 | **192** | | Distinct sitemap dates | 1 | **24** (0 on shallow CI) | `Crawlable-link contract passed: 41078 internal links across 194 pages resolve in one hop.` ## Verified Built and driven in a real browser at `localhost:4321`: - Raw HTML (crawler / no-JS view): 192 inline sidebar links, breadcrumb rendered, **0 local `.md` hrefs**, single H1. Remaining empty containers are JS-only UI (search modal, drawer, runtime status). - SPA intact, no reloads, no JS errors: in-article links, prev/next, sidebar links, mobile drawer (opens, 192 links, closes on navigate), search (8 results for "adapter"). Breadcrumb and active-link state follow correctly throughout. - Subpath build for gh-pages emits correct `/paperclip-docs/...` hrefs. - Homepage unchanged: 15 landing cards, 192 directory links. - Shallow-clone build confirmed to warn and emit zero `<lastmod>`. ## Notes for review - **This can't ship until the Cloudflare GitHub App is unsuspended.** Everything here is verified against a local build only. - **CI will be red on one pre-existing, unrelated failure**: `docs:test:skill-source-blocks` on `docs/reference/skills/optional/content/simplified-english.md` (missing its skill-source block, from release #95). Confirmed failing on clean `main`. Left in the aggregate rather than weakening the suite — fixing it needs the real `SKILL.md` from the parent repo, which is sync-docs work. - **Production will now ship no `lastmod` at all** on Cloudflare's shallow clone. Correct versus lying. To get real dates back, set the Pages build command to `git fetch --unshallow || true; npm run docs:build` — documented in the generated `DEPLOY.md`. - Page weight 67KB → 109KB from the inlined nav. Highly repetitive, compresses well, but it is a real increase. - Weak/duplicate titles were deliberately skipped, per the plan's own ranking (secondary). - Plan item 4 (contextual links from high-authority surfaces to 20–25 priority pages) is a `paperclip-website` change plus editorial judgment, so it's out of scope here. Worth noting that fixing the internal graph recovers most of what that item was meant to buy — the dominant authority failure was internal, not off-site. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5 (1M context) <[email protected]> |
||
|
|
631a6a0c53 |
Release docs for paperclip v2026.817.0 (content + screenshot overhaul) (#95)
Ships the v2026.817.0 docs to live: the doc content that was authored and verified earlier, plus a full screenshot overhaul. The content half (`cdf81e2` and the nightly commits behind it) has already been reviewed. **The screenshots are the part that needs eyes** — `f24d734`, 342 PNGs. ## Screenshots Everything was recaptured against the release commit `213dabab4f8e1f3bb1803a2924c0fea1289fcd4c` (`upstream/candidate/release-2026.817.0`). The previous shots were 375 parent-commits stale (v2026.720.0), with 228 of 310 entries flagged pending. All 342 registry entries now carry that SHA. > ⚠️ Worth knowing: the release commit is **not** an ancestor of the parent's > `master`, and local `master` was four days *older* than it. Capturing against > `master` would have both missed release UI and leaked 93 post-tag commits' worth > of UI into the release docs. ### New coverage (34 entries) Three guides shipped with **no images at all** despite being 115–136 lines. They now have them: | Guide | Shots | |---|---| | `guides/day-to-day/decisions.md` | queue, an agent-proposed decision with its options, triage strip, filter panel, named queue, Decided curtain, empty state | | `experimental/status-cards.md` | board, New card dialog, and all four drawer tabs | | `experimental/task-chat.md` | thread, composer mode picker, side pane | | `reference/api/secrets.md` | the board-side Proposals tab | Chat-Style Tasks is captured in a **separate pass**: `enableTaskChatRedesign` is instance-wide, so leaving it on would have turned every classic issue/task/work-mode shot into the redesigned chat. ## Two defects that were already live Both were shipping in the docs before this PR: 1. **`issues/detail-chat.png` and the `tasks/*` shots were mostly plugin debug output.** The Kitchen Sink example plugin mounts a panel into the issue page that dumps the raw issue JSON, and the seed installed it. Those guides were illustrating a debug panel instead of the task page. The seed no longer installs it. 2. **The seeded task thread was a monologue** — four board-user messages in a row, because the board actor cannot post agent comments. It is now a real two-sided exchange. ## Targets repointed because the UI moved | Target | Why | |---|---| | `activity/*` | `Activity.tsx` deleted; `/activity` renders `pages/audit/CompanyActivity.tsx` | | `experimental/cloud-upstream` | `CloudUpstream.tsx` deleted; the route now redirects to `/company/export` | | `settings/*`, `plugins/*`, `adapters/*` | `/instance/settings/*` are legacy redirects now | | `dashboard/stale-tasks-panel` | panel removed from `Dashboard.tsx` — target dropped | Plus eight targets with **silent selector drift**. A failed interaction step falls back to "whatever state was reached", so these had been quietly producing wrong images — a plain inbox where the doc promised a mode picker, a whole dashboard where it promised one panel. Full table in `docs/user-guides/screenshots/SCREENSHOTS_PLAN_v2026.817.0.md` §C2. ## Pipeline Decisions, status-card summaries and secret proposals are all **agent-authored**, and the board actor the seed runs as is refused by every one of them (`"Agent run context required"`, `"Secret proposals require a verified run-bound agent token"`). Rather than insert rows behind the API, `seed-new-surfaces.mjs` borrows the two doors a real run already has — a board-minted agent API key, and a locally signed agent JWT — so every write goes through the production code path. Also: - `capture.mjs` records a real `captured_against` on a detached checkout instead of the literal string `"HEAD"` (which is what every pre-2026.817 entry recorded), and gains a `scrollTo` step for content below the fold. - `run.mjs` gains `--serve` for iterating on the seed, the task-chat phase, and a keeper that holds the demo board's issue statuses steady — the demo `process` agents finish runs without declaring a disposition, so the stalled-work sweep re-blocks issues every few minutes over a capture that runs far longer. ## Reviewing Light and dark both captured at 1440×900 @2x. The new surfaces are the ones to scrutinise; the rest is a like-for-like refresh. ### One leftover Dropping `dashboard/stale-tasks-panel` leaves `{light,dark}/dashboard/stale-tasks-panel.png` tracked but unused — they need deleting by hand. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 4.8 (1M context) <[email protected]> Co-authored-by: DevRel Gemini <[email protected]> Co-authored-by: Paperclip <[email protected]> Co-authored-by: Dotta <[email protected]> Co-authored-by: ClaudeCoder <[email protected]> |
||
|
|
c7b3f4cacf |
Render full SKILL.md as a code snippet on per-skill pages (PAP-16980) (#89)
> **Updated on current main:** #87 and the #90 cache hotfix are merged. This PR now targets `main`, was rebased onto merge commit `8c5976f`, and has a fresh successful Cloudflare preview plus post-rebase browser smoke evidence. ## What & why (PAP-16980) Improves SEO/AEO on the bundled/optional **per-skill reference pages** (from PAP-16670) by clearly separating the *human-readable catalog summary* (top of page) from the *authoritative full skill definition*. Previously the "Full skill definition" section rendered the `SKILL.md` as **parsed Markdown**, so each skill's own `##` headings, lists, and fenced examples became real page content — competing with the summary, inflating the on-page TOC (e.g. qa-acceptance went from **16 → 6** "On this page" entries), and confusing crawlers/answer engines about the page's true structure. Now each per-skill page shows the **complete, verbatim `SKILL.md`** (frontmatter + body) inline, rendered through the **existing production code-snippet treatment**. ## Changes - **Content (10 per-skill pages):** replaced the old `### Skill frontmatter` bullets + parsed `### Skill instructions` layout with the full `SKILL.md` embedded in a fenced ````markdown skill-source` block. Source bytes were taken verbatim from each skill's authoritative `SKILL.md` at the page's pinned `paperclip_version`, verified against the page's own prior instructions body + frontmatter (name/description) so no content drifted. - **Renderers (`site/build-release.mjs`, `site/app.js`):** a `skill-source` fence renders as `<pre><code class="language-markdown" data-skill-download="SKILL.md">`. Every other code block is delegated to marked's default renderer, so existing snippets are byte-for-byte unchanged. Rendered identically in the crawler-visible build HTML and on client SPA navigation. - **Copy + Download (`site/app.js`, `site/styles.css`):** `decorateCodeBlocks` reuses the existing Copy affordance and adds a **Download `SKILL.md`** control beside it. Both share dimensions, styling, hover/focus visibility, and the copied-success state; both carry accessible labels/titles. - **Readable full-source wrapping (`site/styles.css`):** only the downloadable `SKILL.md` block uses `white-space: pre-wrap` and safe long-token wrapping. Ordinary code snippets retain the existing horizontal-scroll treatment. - **Test:** `scripts/verify-skill-source-blocks.mjs` (`npm run docs:test:skill-source-blocks`) asserts the render contract (single H1, single `<pre>`, frontmatter present, no legacy headings) and that ordinary docs pages emit no `data-skill-download`. ## Scope statement No catalog-page layout, navigation, typography, summary-copy, metadata-card, or information-architecture changes. `paperclip-website` and generated `.site/` output are untouched. `optional/finance/ramp.md` is intentionally excluded — it is a newer summary page with no embedded full-definition section to reformat. The diff is the 10 skill pages + the narrow renderer/Copy-Download support + the test. ## Evidence **1–2. Before/after (desktop, 1280×900) + mobile after (390×844)** — attached to Paperclip issue [PAP-16980](https://paperclip-dev.tail29c1aa.ts.net/PAP/issues/3098577a-4f6f-4eb2-9151-a4b61917dffd) (`before-desktop.png`, `after-desktop-controls.png`, `after-controls-zoom.png`, `after-mobile.png`). GitHub cannot render the auth-gated attachment URLs inline, so they live on the issue for reviewers. **4. Raw DOM/HTML (build output, qa-acceptance):** - Article renders exactly **one H1**: `<h1 id="qa-acceptance">QA Acceptance</h1>` - Full source inside a single code block: ```html <pre><code class="language-markdown" data-skill-download="SKILL.md">--- name: qa-acceptance description: Produce QA acceptance criteria and a manual validation plan … key: paperclipai/bundled/quality/qa-acceptance recommendedForRoles: - qa … </code></pre> ``` - Article `<pre>` count: **1** (inner ````` fences stay literal — no breakout). 10/10 skill pages carry the block; 0 non-skill pages do. **5. Copy + Download behavior (Playwright against the built site):** ```json { "downloadFilename": "SKILL.md", "downloadMatchesBlock": true, // downloaded file === code block text, byte-identical "downloadBytes": 3853, "copyShowsSuccess": true, // Copy toggles the existing is-copied success state "accessibleLabels": { "copy": "Copy code", "download": "Download SKILL.md" } } ``` Both controls reach `opacity: 1` on `.code-wrap:hover` and on keyboard `:focus-visible` (verified `matches(':focus-visible') === true` → computed opacity `1`). **H1 audit (every generated route, incl. all skill URLs — requested on the issue):** built the site on current `main` and scanned **all 184 generated `index.html` routes** (16 skill routes among them). **Every route has exactly one `<h1>`.** The skill source's own `#`/`##` headings live *inside* the `<pre><code>` block as literal text, so they do **not** create competing H1s — the SEO/AEO goal. ## Tests Run against current `main` after #87 and #90: ``` docs:build ............................. ok (184 routes) docs:test:static-routes ................ pass (5 routes + 404 + root + subpath) docs:test:skill-source-blocks .......... pass (10 skill pages, 1 control page) H1 audit (all 184 routes) .............. pass (exactly 1 h1 each; 16 skill routes) ``` **Do not merge/deploy** — returning to Dotta/reviewers for approval per PAP-16980. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: DevRel Gemini <[email protected]> Co-authored-by: Paperclip <[email protected]> |
||
|
|
8c5976ffd3 |
URGENT: fingerprint docs client bundle to fix mixed-version cache regression (PAP-16990) (#90)
## Urgent hotfix — mixed-version asset cache regression (PAP-16990) Restores reliable navigation on https://docs.paperclip.ing/ without undoing the PAP-16670/#87 server-render + single-H1 SEO/AEO work. ### Root cause The server-render change shipped new HTML that removed `#loading` / `#error-state`, but every generated route still loaded the client bundle from a **stable URL**: `<script src="app.js" defer>`. Cloudflare/browsers cache `/app.js` for up to ~4h independently of the HTML, so a returning visitor could pair freshly-revalidated new HTML with a **pre-merge cached `app.js`**. The old click path looked for the removed elements, threw before render, hid the homepage, and left a blank `#main` (no URL update). Forced refresh recovered — not acceptable. CSS was never part of this: it is inlined into every page and travels with the (revalidating) HTML. `content.json` is byte-identical prod↔preview. ### Fix (narrow cache-bust, no rollback) 1. **Content-fingerprint the client bundle.** Emit `app.<hash>.js` (+ `styles.<hash>.css`) and rewrite the shell so *every* route references the exact build's asset URL. A changed bundle gets a brand-new URL no cache can satisfy with stale bytes — closes the mixed-version window in **both** directions. No unversioned `/app.js` is emitted, so a stale cache has nothing to pin. 2. **`_headers`:** fingerprinted JS/CSS cached `immutable` (1y); HTML routes (`/`, `/*/`) and `content.json` set `max-age=0, must-revalidate`. 3. `inlineReleaseStyles` now matches the fingerprinted stylesheet link so CSS stays inlined. ### Before / after ``` - <script src="app.js" defer></script> # stable URL — cacheable across versions + <script src="app.1b669c8d0aec.js" defer></script> # content-hash URL — cache miss on any change ``` `_headers` (new): ``` /app.*.js Cache-Control: public, max-age=31536000, immutable / /*/ Cache-Control: public, max-age=0, must-revalidate /content.json Cache-Control: public, max-age=0, must-revalidate ``` ### Verification (local build, base path `/`) - `npm run docs:build` → emits `app.1b669c8d0aec.js`, `styles.9e3f24983c72.css`; **184/185** HTML files reference the fingerprinted bundle (185th is the scriptless `404.html`); **no** bare `app.js`/`styles.css` file or reference remains. - `npm run docs:test:asset-fingerprints` ✅ (new) — proves the URL is the content hash of the served bytes, changes on any content change, is referenced identically by root + interior routes, has no unversioned sibling, and the cache headers are present. - `npm run docs:test:static-routes` ✅ — single-H1 interior pages, no-JS server-render, homepage manifest completeness, 404 separation, root + `/paperclip-docs/` builds. ### Remaining (post-merge, needs a real browser on the live domain) Chromium can't launch in the CI/agent sandbox (missing system libs), so **cold + returning/cached** browser smoke on https://docs.paperclip.ing/ must be run after merge → redeploy from `main`. Checklist: cold load, homepage-card nav (URL updates, one H1, no blank), interior↔interior, Back/Forward, All Docs, search, 404, and a returning/cached session (prior `/app.js` in cache). Does **not** touch the SKILL.md formatting PR (PAP-16980), per the issue. Co-authored-by: DevRel Gemini <[email protected]> Co-authored-by: Claude Opus 4.8 <[email protected]> |
||
|
|
b214cdebda |
[codex] Improve docs Lighthouse scores (#41)
## Summary Improves the docs page Lighthouse profile for the report attached to [PAP-12441](/PAP/issues/PAP-12441). - Removed render-blocking third-party docs shell requests for Google Tag Manager, Google Fonts, and Lucide. - Vendored the existing `[email protected]` parser locally and copied `site/vendor` into release builds. - Replaced Lucide runtime icon rendering with inline SVG paths for the existing docs section icons. - Fixed reported accessibility issues: mobile zoom disabling, hidden drawer focusability, TOC ARIA semantics, footer heading order, muted-text contrast, TOC button accessible name, and article link affordance. - Added smaller responsive WebP variants for the dashboard overview screenshot and explicit dimensions/fetch hints for article images. - Minified CSS in release builds. ## Canary - Commit preview: https://2f89e7fc.paperclip-docs-74t.pages.dev - Branch preview: https://codex-lighthouse-docs-scores.paperclip-docs-74t.pages.dev - Target route from the report: https://2f89e7fc.paperclip-docs-74t.pages.dev/guides/getting-started/five-minute-path/ ## Root Cause The docs shell was paying for several third-party render-blocking requests before first contentful render, and the target guide loaded a large full-size dashboard PNG for a small mobile viewport. Several Lighthouse accessibility audits were caused by UI semantics rather than content: a hidden but focusable drawer, menu roles around plain links, a non-zoomable viewport, footer heading level skips, and color-only article links. ## Validation - `npm run docs:build` passes. - `npm run docs:test:static-routes` passes. - `npm run docs:test:hierarchical-skills-nav` passes. - `node --check site/app.js`, `node --check .site/app.js`, and `node --check site/build-release.mjs` pass. - Static release checks pass for preserved SEO metadata, removed third-party assets, zoomable viewport, inert drawer, no footer headings, underlined article links, responsive screenshot wiring, TOC nav semantics, and copied optimized assets. - Cloudflare Pages check passed for commit `129baaa`. Local Lighthouse could not complete in this container because the available cached Chromium binaries fail to start without `libatk-1.0.so.0`. PageSpeed Online is also quota-limited from this environment. The canary URL above is ready for a browser-capable Lighthouse run. --------- Co-authored-by: DevRel Gemini <[email protected]> Co-authored-by: Paperclip <[email protected]> |
||
|
|
5b4b489154 |
[codex] Add Skills docs hierarchy (#34)
## Summary - Add generated reference pages for bundled and optional skills. - Add Skills index pages and hierarchy-aware sidebar/breadcrumb navigation. - Add verification scripts for hierarchical Skills routes and static docs routes. ## Verification - `npm run sync:verify-nav` - `npm run docs:test:static-routes` - `npm run docs:build` - QA in PAP-11775 also verified browser rendering for hierarchy, bundled `task-planning`, and optional `last30days` pages with screenshots. ## Issues - PAP-11770 - PAP-11783 --------- Co-authored-by: Paperclip <[email protected]> |
||
|
|
91bbe2cdbd |
[codex] Add static SEO docs routes (#32)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - The public docs site is how operators and contributors discover reference material and guides. > - The docs pages were routed through hash URLs, which makes individual pages harder for crawlers and link previews to treat as first-class documents. > - The release build needs to emit static HTML pages for each docs route while preserving the client-side docs experience. > - This pull request adds static docs route generation and verification around canonical, crawlable route output. > - The benefit is that docs pages can be indexed and shared as normal path-based URLs without losing local navigation behavior. ## Linked Issues or Issue Description No public GitHub issue exists for this docs-site improvement. Problem: - Docs pages were reachable primarily through hash routes such as `/#/reference/skills`. - Crawlers and social previews benefit from static route-specific HTML at normal paths such as `/reference/skills`. - The release build should publish those pages predictably and verify that they include canonical metadata, page titles, and visible article content. ## What Changed - Added static route generation to the docs release build so each nav route gets an `index.html` output with route-specific title, canonical URL, article content, and sitemap entry. - Updated the browser app bootstrap to support path-based docs routes and retain compatibility with hash links. - Added `docs:test:static-routes` coverage for generated static route output. - Documented Cloudflare Pages preview and production deployment details. ## Verification - `npm run docs:test:static-routes` - `npm run docs:build` ## Risks Low to medium risk. This changes release output shape for the docs site, so the main risk is a Pages deployment configuration mismatch. The build keeps existing client-side rendering behavior and includes a static-route verification script to catch missing canonical pages before publishing. ## Model Used OpenAI GPT-5 Codex coding agent, tool-using software engineering mode. Exact service context window was not exposed in this runtime. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes #` / `Refs #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip` URLs) - [x] My branch name describes the change (e.g. `docs/...`, `fix/...`) and contains no internal Paperclip ticket id or instance-derived details - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [ ] All Paperclip CI gates are green - [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [x] I will address all Greptile and reviewer comments before requesting merge --------- Co-authored-by: Paperclip <[email protected]> |
||
|
|
e4be96d643 |
Add screenshot-coverage gate to sync:check
New scripts/sync/verify-screenshots.mjs asserts that every screenshot referenced in a guide page is (a) a CAPTURE_TARGET in routes.mjs and (b) indexed in registry.json with a non-empty depends_on. It exits non-zero otherwise, so a doc image added without a capture target fails loudly instead of silently going stale. Wired into `npm run sync:check`, which the /sync-docs skill runs every sync. Currently: 112 referenced screenshots, all tracked and indexed. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> |
||
|
|
5f00622542 |
Add automated screenshot capture pipeline + skill wiring
Builds an end-to-end pipeline that keeps docs screenshots in sync with the
latest Paperclip build, replacing the previously-manual capture step.
scripts/screenshots/:
- config.mjs isolated-instance env helpers (local_trusted/loopback,
scratch PAPERCLIP_HOME, never touches ~/.paperclip)
- seed.mjs idempotent demo-data seeding via REST (company/agents/
project/goal/issues/routine/workspace); writes .seed-ids.json
- routes.mjs 56 CAPTURE_TARGETS with real ui/src depends_on paths;
resolveRoute() uses the server-derived issuePrefix
- capture.mjs Playwright capture (light+dark, 1440x900 @2x), theme seeded
to match ui/index.html, stamps registry captured_sha
- sync-registry.mjs back-fills route + depends_on into registry.json
- run.mjs one-command orchestrator: onboard -> health -> seed ->
sync-registry -> capture -> teardown
- README.md prerequisites, flow, overhaul mode, safety notes
Wiring:
- package.json: screenshots:{seed,capture,sync-registry,refresh,refresh:all}
+ @playwright/test devDependency
- screenshots:refresh:all is the full re-take mode for major UI/UX overhauls
- sync-docs SKILL.md Phase 6: capture is now automatable (still PR-reviewed,
never auto-pushed); maintenance.md updated to match
- .gitignore: scripts/screenshots/.seed-ids.json
Auth is free in local_trusted mode (every request is admin), so no login flow
is needed. Capture/staleness still surface to a PR for human review.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
|
||
|
|
c071db51e4 |
Add post-edit code verification helper
Drift detection catches the coarse case (doc references X, parent
doesn't have X). Verification is finer-grained: doc claims specific
properties (flag names, env vars, route paths, schema field names,
config values) and we check each individual claim against parent
code, post-edit.
scripts/sync/verify-edit.mjs (1266 lines):
- Takes a doc file + parent ref. Extracts claims by kind:
cli-command, cli-flag, env-var, rest-route,
adapter-config-field, adapter-config-value, file-path.
- Uses structural cues (Config sections, code fences, tables) to
avoid extracting from natural prose — guard against
false-positives on phrasing like "use the `model` field".
- Per-claim verification:
- cli-* → grep parent cli/src/** for command/flag literals
- env-var → check .env.example, server/src/config.ts, packages/**/env.ts
- rest-route → grep matching routes/<surface>.ts with {id}↔:id normalization
- adapter-* → grep packages/adapters/<name>/** for literal field/value
- file-path → gh contents API existence check
- Three output buckets: verified, unverified (with Levenshtein-≤3
fuzzy "did you mean" suggestion), suspicious.
- Cached gh fetches under /tmp/paperclip-sync/verify-<ref-sha>/.
- Same fixture-loader pattern as compare-window/check-drift.
Tests: 6 new (CLI pass/fail, adapter field typo with suggest,
REST route normalization, env var pass, prose-guard
false-positive). 26/26 total passing.
Live test against test/sync-docs-catchup-run:cursor-cloud.md →
20/20 claims verified. The catchup agent's worry that adapter docs
might invent field names didn't materialize on cursor-cloud (every
config field is present in packages/adapters/cursor-cloud/**) — but
the tool would have caught it. acpx-local hasn't been verified yet;
worth running before the next merge decision.
Wired as sync:verify-edit. SKILL.md update to follow.
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
|
||
|
|
16b910db88 |
Add directory-rename detection helper
GitHub's compare API marks individual file renames via status='renamed' when git's similarity heuristic catches them, but DIRECTORY renames (common case: packages/adapters/foo-local → foo-cloud) appear as a bunch of add/remove pairs. Without aggregation, the adapters watcher would treat the addition as a genuinely-new adapter and create a duplicate doc page. scripts/sync/detect-renames.mjs (252 lines): - Input: a compare-window.mjs JSON file (or stdin). - Detects directory renames under hard-coded watched prefixes: packages/adapters/, packages/plugins/sandbox-providers/, server/src/adapters/, skills/. - Aggregates added/removed files into depth-2 dirs, requires ALL files in a candidate dir to share the add/remove status (mixed with 'modified' = the dir is alive, not a rename). - Three matching signals, ranked by reliability: - file-content-overlap (≥50% suffix-path intersection) → high - shared-prefix (≥3 chars; ≥50% of longer leaf → high) - levenshtein (≤30% of shorter leaf) → medium - Greedy 1:1 assignment when multiple pairs compete. - Reports: renames, added_dirs_genuinely_new, removed_dirs_no_match. Wired as sync:detect-renames in package.json. Tests: 5 new (pure rename, shared-prefix, genuine new dir, genuine removal, multi-candidate tiebreak). 20/20 total passing. Live run on v2026.318.0...v2026.512.0: 0 renames detected (correct — no dirs got renamed in that window; cursor-cloud is a genuinely new sibling of cursor-local, not a rename). 7 dirs flagged as genuinely new and verified. A follow-up commit will wire this into the SKILL Phase 3 manifest build so the adapter watcher consults it before creating doc pages. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> |
||
|
|
72f56f64ad |
Add drift detection — catches docs referencing removed parent surfaces
The wet-run found POST /api/companies/{companyId}/logo documented in
docs/reference/api/companies.md but absent from the current parent
server/src/routes/companies.ts. The cumulative-diff path is blind to
this — it sees what's new, not what's vanished. Drift is independent
of the diff window: it can exist regardless of when the last sync ran.
scripts/sync/check-drift.mjs (712 lines):
- Four drift classes:
- parent-path-missing (high confidence): doc references to
cli/src/..., packages/..., server/src/... paths that 404 in parent.
- cli-command-missing/ambiguous (high/medium): documented
`paperclipai <subcommand>` not found in parent cli source.
- env-var-missing (high): table rows in environment-variables.md
that don't appear in .env.example or watched config files.
- rest-route-missing (medium): ## METHOD /api/... headers in
docs/reference/api/*.md not registered in matching routes/*.ts.
Normalisation handles {id} ↔ :id and prefix-stripping for
relative-mounted routers.
- Cached gh contents under /tmp/paperclip-sync/drift-* (per-ref).
- Same fixture-loader pattern as compare-window.mjs for hermetic
unit tests via PAPERCLIP_SYNC_FIXTURE_DIR.
- Never auto-resolves — always surfaces for human review.
SKILL.md changes:
- New Phase 1.5: runs check-drift against parent default branch
HEAD, independent of the diff window. Drift records form a new
manifest tier (not auto-merge, not pr, not context-only).
- Phase 4 dry-run summary includes drift counts.
- Phase 7 PR bodies get a "⚠ Drift" section.
Tests: 4 new (12/12 total passing). Live run against parent master
caught the motivating logo-route case plus 40 other genuine drift
candidates that should be triaged separately (env vars in untracked
packages, the goals-and-projects → goals.ts+projects.ts file-split
mismatch, etc.).
sync:check now chains lint-links + verify-nav + check-drift.
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
|
||
|
|
0dded0ef75 |
Implement cumulative-diff bisection as a helper script
The wet-run hit the GitHub compare API's 300-file/250-commit cap on the first call against the 318→512 window. The SKILL had the algorithm in prose — that's load-bearing now, so it gets a real implementation. scripts/sync/compare-window.mjs (392 lines): - Recursive midpoint SHA bisection. Truncation detected by files.length===300 OR total_commits>commits.length. - SHA enumeration via compare's .commits[].sha (up to 250); falls back to paginated /commits for windows exceeding that. - Leaf responses cached under /tmp/paperclip-sync/<a..b>.json so re-runs within a day are cheap. - Status-merge rules when unioning: added+removed → drop; added+modified → modified; modified+removed → removed; renamed wins over modified. - CLI: node scripts/sync/compare-window.mjs <A> <B> [--json] [--repo] - Fixture-driven testability via PAPERCLIP_SYNC_FIXTURE_DIR env var so unit tests stay hermetic without HTTP mocks. Test coverage (3 new unit + 1 live integration in test.mjs): - No-truncation case (1 leaf, no recursion). - Single split (2 leaves, status-merge verified). - Status merge cancellation (added+removed → drop). - Live: real 318→512 window returns 1,726 unique files across 22 leaves with truncated_leaves=0 and the three sentinel files present. SKILL.md Phase 2: replace the algorithm prose with a one-line helper invocation. Status-merge rules stay in SKILL as contract. 8/8 tests passing. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> |
||
|
|
e09564a34d |
Add sync:test — unit tests for helper scripts
Self-contained test runner (no framework). Creates tmpdir fixtures per test and asserts script behaviour via subprocess invocation. Covers: - lint-links: positive + 9 skip-case patterns (external URLs, schemes, anchors, absolute SPA routes, template placeholders). - verify-nav: dangling + orphan classification, screenshots-dir exclusion, --strict mode. - backfill-screenshots: idempotency (preserves entries with non-empty depends_on, scaffolds the rest). 4/4 passing on the current source. No source-script bugs found. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> |
||
|
|
c29d44a92e |
Add sync helper scripts and address deferred gotchas
Closes the five deferred items from the prior gotcha audit:
A. Link checker — scripts/sync/lint-links.mjs walks docs/**/*.md and
verifies every relative markdown/image link resolves to an existing
file. Skips external URLs, absolute SPA routes (/...), URI schemes
(mailto:, agent://, etc.), and template placeholders. Found 3 real
pre-existing broken links to missing screenshots — not fixed in
this commit, but the tool will now block any new ones.
B. Nav verification — scripts/sync/verify-nav.mjs cross-checks
site/content.json against the docs filesystem. Dangling entries
(in nav but missing on disk) are errors; orphans (on disk but not
in nav) are warnings. Found 1 real orphan (require-board-approval-
before-spend.md from
|
||
|
|
9e8398cc3a |
Add Cloudflare Pages docs deployment
Co-Authored-By: Paperclip <[email protected]> |
||
|
|
55f08088b5 |
Prepare docs deployment for custom domain
Co-Authored-By: Paperclip <[email protected]> |
||
|
|
6f380a1620 |
Move site shell to /site, split into html/css/js, add favicon
Separate the docs SPA from its content: docs/docs-website/ becomes site/ at the repo root so docs/ holds only markdown. Extract inline <style> and <script> from index.html into styles.css and app.js. Add an inline-SVG favicon using the header paperclip glyph. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> |
||
|
|
5b2367875c | (chore): cleanup | ||
|
|
3a852ec7e9 | Initial docs site import |