Commit Graph
16 Commits
Author SHA1 Message Date
Aron PrinsandClaude Opus 5 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]>
2026-08-25 22:51:42 +02:00
Aron PrinsandClaude Opus 5 63e10a3cd6 sync-docs: require verifying the release is live before calling it shipped (#112)
## What happened

Today's `v2026.824.0` release **did** write its docs-changelog entry.
The accordion is on `main` in `docs/reference/changelog.md`, every one
of the 15 pages the release touched is listed, and `npm run docs:build`
renders it correctly with exactly one open accordion.

What didn't happen is publication. `docs.paperclip.ing` is still serving
the build from `ebbab07` (#100, the pre-release hotfix):

| Check | Expected | Live |
|---|---|---|
| `/reference/deploy/tailscale-https-broker/` | `200` | `404` |
| `paperclipai channels` on `/reference/cli/installation/` | present |
absent |
| top changelog entry | `v2026.824.0` | `v2026.817.0` |

The `main`-branch Pages preview is stale in the same way, so neither
commit pushed to `main` today (#110, #111) deployed. `npm run
docs:build` succeeds from a clean checkout of `main`, so it isn't a
build break — the Cloudflare Pages deploy simply never ran.

## Why the skill didn't catch it

Phase 7 builds into `.site`, which is gitignored — nothing reader-facing
is ever committed — and Phase 8 hands off at the merge. There was no
step that looked at the live site. The runbook had a soft "Confirm
`docs.paperclip.ing` is rebuilt successfully" bullet with no method and
no fallback, and it was skipped along with its siblings: the
`docs/v2026.824.0` tag was never cut (last one is `docs/v2026.707.0`),
and `nightly`'s `.sync-state.json` still reads `branch_mode: "release"`.

## What this changes

- **`skills/sync-docs/SKILL.md`** — new **Phase 9 — Publish & verify
live** (release mode only): do every post-merge follow-up (tag, realign,
flip `branch_mode`), verify production against three independent live
signals, republish via `wrangler pages deploy` if stale, and report the
status codes as evidence. Verification is mandatory even when pushing
isn't approved. Plus a failure-mode row, a diagnosis-order special case,
and a pointer from Phase 6.5 (the entry isn't done until it's live).
- **`maintenance/runbook.md`** — replaces the soft "confirm" bullet with
the actual checks and the manual republish fallback.
- **`README.md`** — it claimed there is no Wrangler publish step for
this repo; notes the auto-deploy isn't guaranteed and documents the
fallback.

No changelog content changed — the `v2026.824.0` entry needed no
correction. Merging this PR pushes to `main`, which should also
re-trigger the deploy that stalled.

## Verification

- `npm run docs:build` — clean
- `npm run sync:check` — lint-links and verify-nav pass; the 5
`rest-route-missing` drift candidates are pre-existing medium-confidence
`Verify:` entries from the release, untouched here

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 5 (1M context) <[email protected]>
2026-08-25 12:25:33 +02:00
Aron PrinsandClaude Opus 4.8 8a858777f2 sync-docs: tag-accuracy gate + release-channel-aware skill (#111)
Makes /sync-docs correct under the canary→nightly→beta→stable channel model. Adds the release-mode tag-accuracy gate (scripts/sync/check-tag-accuracy.mjs) that verifies every changed page against the stable tag and flags post-tag leaks; stable-CalVer guard + channel glossary, tag-scoped drift/verify, and Phase 5.7 in the skill; realign fix that keeps nightly's drafts; a release-channels watcher; the analysis/plan doc; and a hermetic test (sync:test 38/38).

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-08-25 11:48:38 +02:00
Aron PrinsandClaude Opus 5 6e6990539c Add the v2026.817.0 docs changelog entry, and a step that writes it (#96)
The documentation changelog was never updated for v2026.817.0. This adds
the
missing entry, and closes the hole that let it go missing.

## Why it was missing

`docs/reference/changelog.md` has **exactly one commit in its entire
history** —
`8c21935` (#73), which created the page on 2026-07-23 with the
v2026.722.0 and
v2026.720.0 entries written by hand. `skills/sync-docs/SKILL.md`
contains zero
occurrences of "changelog": release mode merges nightly → main, tags,
and ships,
but never writes that page.

The timing is why nobody noticed:

| | |
|---|---|
| 2026-07-23 08:10 | v2026.722.0 release merged (#72) |
| 2026-07-23 11:47 | changelog page created (#73), already current |
| 2026-08-18 10:43 | v2026.817.0 merged (#95) ← first release that had
to append |

The page was born ~3.5 hours after the previous release, so it was
correct on
arrival. v2026.817.0 is the first one it could have missed, and it did.
The
frontmatter was stale by the same marker (`paperclip_version:
v2026.722.0`).

## What's here

**The entry** — 8 new pages (Decisions API, Status Cards API and guide,
Chat-Style Tasks, the `service` CLI, three catalog skills), the
substantive
rewrites grouped by what a reader gains, and a short block on the
screenshot
overhaul. Frontmatter bumped to `v2026.817.0`.

**Phase 6.5 in the sync-docs skill** (release mode only) — writes this
page from
the manifest the run already holds. It pins down the details that are
easy to get
wrong on a monthly cadence:

- the `<details class="accordion">` shape, newest first
- **only the newest entry carries `open`** — miss this and the page
renders with
  two expanded
- links are relative to `docs/reference/`
- exclude repo maintenance files (`docs/user-guides/screenshots/`,
  `SCREENSHOTS_*.md`) — they're in the repo, not in the docs
- skip entirely in nightly mode, so an unreleased tag can't leak

Phase 7's `sync:check` already validates relative links, so a typo in a
future
entry fails the run instead of shipping.

## Checks

- `npm run docs:build` — clean, 191 pages
- `npm run sync:lint-links` — no broken internal links
- Rendered output verified: v2026.817.0 sits on top with **exactly one**
open
  accordion

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 5 <[email protected]>
2026-08-18 12:52:00 +02:00
8121a9c947 Release docs for paperclip v2026.720.0 (#67)
Ships the docs for **paperclip v2026.720.0** to `docs.paperclip.ing`.

Window: `v2026.707.0...v2026.720.0` — 196 commits, 1,523 changed files,
0 truncated leaves.

Most of this release was already drafted on `nightly` over the past
cycle (Cases, Folders, Decision Training, Attention, Built-in Agents,
company skill policy, inbox agent policy, `costStatus`, the ACP engine
graduation and `acpx_local` retirement, and
`PAPERCLIP_TOOL_ACTION_SIGNING_SECRET`). This PR adds the surfaces
nightly had **not** yet covered, then stamps the release version across
everything touched this cycle.

## New pages

**`docs/reference/api/tool-gateway.md`** — the MCP Tool Gateway, the
headline experimental feature. Covers applications and connections,
catalog entries and risk levels, profiles/entries/bindings, the
tool-access policy and its decision outcomes, named MCP gateways and
their tokens, the audit feed, and the Smoke Lab. Documents the five
`tools:*` permission keys and the viewer/membership rules on routes that
don't use a permission key. Flags both experimental gates (`enableApps`,
and `enableSmokeLab` separately).

**`docs/reference/api/summary-slots.md`** — the built-in Summarizer and
summary slots. Covers slot addressing (`scopeKind` / `scopeId` /
`slotKey`), generation, revisions, and the `enableSummaries` gate.
Cross-references Built-in Agents rather than duplicating it.

## Updated

**`docs/guides/org/skills.md`** — Skill Studio (the three-pane authoring
workspace, saved inputs, test runs and their unavailability reasons, run
templates, version history), nested skill folders and the My Skills
view, importing skills straight from a project, and company skill forks.
Cross-references the existing Folders and Company Skill Policy API pages
instead of restating them.

## Release stamping

`paperclip_version: v2026.720.0` applied to all 26 pages touched this
cycle — bumped on 10 that already had frontmatter, added to 13 that had
none (including the API pages nightly authored, which were intentionally
versionless until they merged to `main`).

## ↻ Renames detected

`detect-renames.mjs` reports no directory renames. It flags one
`removed_dirs_no_match`:

- `packages/adapters/acpx-local` — **already reconciled on nightly.**
The adapter was retired upstream (ACP became a capability of each
harness adapter rather than a standalone adapter).
`docs/reference/adapters/acpx-local.md` is now a retired stub that
points at Claude Code / Codex / Gemini CLI and documents the automatic
migration. No action needed.

## Screenshots — full overhaul recommended

**248 of 298** registry entries are stale. This release landed a
design-system convergence (single-source design tokens, retuned theme,
broad Card/Badge adoption, a multiplicative radius ladder, unified list
surfaces, Lucide task-status glyphs), so nearly every captured surface
moved. This is an overhaul cycle, not a targeted refresh:

```sh
npm run screenshots:refresh:all
```

Full per-entry breakdown in `SCREENSHOTS_PENDING.md`. Captures go to
their own PR for review — nothing is auto-pushed.

## Verification

- **Drift:** clean — 0 records against `903bd15` (8 parent paths, 57 CLI
commands, 66 env vars, 389 REST routes, 18 permission keys, 4 roles).
- **Claim verification:** `verify-edit.mjs` against `v2026.720.0` — **0
unverified, 0 suspicious** across all three authored files.
- **Reconciliation:** no disappeared manifest entries.
- **`npm run docs:build`:** passes — 169 markdown files, sitemap and
robots generated.
- **`npm run sync:check`:** links clean (172 files), nav clean,
screenshots indexed, drift clean.
- **Nav orphans:** `docs/reference/cli/commands.md` and
`docs/reference/cli/control-plane-commands.md` — intentional redirect
anchors, expected.

`.sync-state.json` bumped to `base_release_tag: v2026.720.0` /
`base_release_sha: 903bd157…`, which also repoints the drift checker's
pinned ref (the previous `f12bb27…` no longer resolves upstream).

No open `nightly-draft/*` PRs to fold in.

## Checklist

- [ ] Tool Gateway page reads correctly for an operator enabling
`enableApps` for the first time
- [ ] Summary Slots experimental gating is accurate
- [ ] Skill Studio guide matches what you see in the running UI
- [ ] Decide whether to run the screenshot overhaul before or after
merging this
- [ ] **After squash-merging: run `node scripts/sync/realign-nightly.mjs
release/v2026.720.0 --push` before the next nightly sync.** The squash
severs `main`↔`nightly` ancestry; the realign (shipped in this PR)
prevents add/add conflicts at the next nightly run.

🤖 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]>
2026-07-20 19:59:18 +02:00
Aron PrinsandClaude Opus 4.8 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]>
2026-05-30 12:05:26 +02:00
Aron PrinsandClaude Opus 4.7 fc74da5f6b Document nightly catch-up; stop rendering version footer
Two related cleanups noticed during the v2026.513.0 release run:

1. Release ceremony was missing the nightly catch-up step. After the
   release PR merges, main is ahead of nightly by the release commits
   and nightly's .sync-state.json still says branch_mode "nightly"
   with the old base_release_tag. The merge-down + branch_mode flip is
   manual the first time and must be done by hand. Added as the 4th
   post-merge follow-up, plus a troubleshooting row for the symptom
   "nightly run reports release mode unexpectedly".

2. paperclip_version frontmatter was being rendered as a visible
   "Documenting Paperclip vX.Y.Z" footer on every release-stamped
   page. Treat the field as internal metadata only: build-release.mjs
   still parses and surfaces it into content.json (queryable for
   tooling), but the SPA no longer prints it on-page. Updated the
   skill's builder note to match.

Co-Authored-By: Claude Opus 4.7 <[email protected]>
2026-05-14 10:07:29 +02:00
Aron PrinsandClaude Opus 4.7 840c2a7d54 maintenance: split MAINTENANCE.md into maintenance/ folder
Reorganize the operational docs into a dedicated maintenance/ folder
with two focused files plus the deferred-items tracker:

- MAINTENANCE.md (deleted) → split into:
  - maintenance/maintenance.md — repo layout, commands, base paths,
    pages, screenshots, publishing, and the /sync-docs design
    reference (Sync workflow + Architecture diagrams).
  - maintenance/runbook.md — operational guide (first-time setup,
    creating the nightly branch, daily operation, release ceremony,
    review workflow, helper scripts, troubleshooting).
- FOLLOW-UPS.md (deleted) → maintenance/follow-ups.md (verbatim).

Cross-references updated:
- skills/sync-docs/SKILL.md pointers to MAINTENANCE.md retargeted
  to maintenance/maintenance.md (via relative path).
- Inside maintenance.md: anchor refs to Runbook subsections
  (#first-time-setup, #helper-scripts-full-list) rewritten as
  cross-file links to runbook.md#anchor.
- Inside runbook.md: anchor ref to Screenshots rewritten as
  maintenance.md#screenshots. Heading levels demoted one level
  (the section title is now top-level # Runbook). Intro paragraph
  updated to reflect the standalone file.
- Inside both files: pointer paragraphs at the top linking to the
  sibling file so readers can jump between design and operations.

No prose was rewritten or removed — only relocated and re-linked.
Verified zero remaining references to the old filenames across .md,
.mjs, .json, .js, .html. Build clean (90 md files). Lint-links
exit 1 only for the 3 pre-existing broken screenshot refs already
tracked in follow-ups.md.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
2026-05-13 16:15:57 +02:00
Aron PrinsandClaude Opus 4.7 f68cbbd29a Wire detect-renames, frontmatter, and verify-edit into SKILL
Closes the design loop for the three helpers shipped in the
preceding commits and addresses the batched-exhaustiveness gap.

SKILL.md (+43 net):

- Phase 3: new substep runs detect-renames.mjs and routes detected
  renames to the EXISTING doc page with change_kind: renamed (not a
  new page). removed_dirs_no_match surfaces as a Reconcile-style
  flag. added_dirs_genuinely_new flow through unchanged.
- Phase 5: new bold paragraph stating --batched mode is exhaustive
  per window — no subsampling. Wet-run subsampling was scope-control
  for testing, not skill design. Also: paperclip_version frontmatter
  is now live (builder strips and surfaces it); added a new final
  voice-rule bullet requiring concrete identifiers (flags, env vars,
  routes, field names) be copied verbatim from parent code.
- New Phase 5.5: run verify-edit.mjs on every touched file. Auto-merge
  edits roll back on any high-confidence unverified claim. PR-tier
  edits get a Verification Failed callout; batched-release PRs never
  auto-merge when any verification fails. Suspicious claims log but
  never block.
- Phase 7 PR body template: three structured sections — Drift,
  Verification Failures, Renames detected. Each omitted if empty,
  never silently dropped.
- Helper-script index added near top.

anchor-map.json:

- adapters watcher detect rewritten to reference detect-renames.mjs
  as the canonical rename check (no more inline heuristic prose).
- agent-skills, plugin-sdk, server-adapters: parenthetical pointers
  added to detect-renames where renames apply.
- Other watchers unchanged (rename signal doesn't cleanly apply).

Follow-ups flagged but not done in this commit:
- Special-cases "Renames on parent" still describes the manual
  fix-anchor-map workflow; would benefit from a one-line distinction
  between watcher-path renames (manual) vs single-dir renames within
  a watcher (automatic).
- file-path drift kind and verify-edit's file-path kind overlap —
  worth deduping a future pass so one missing file doesn't appear
  under both Drift and Verification Failures.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
2026-05-13 13:57:26 +02:00
Aron PrinsandClaude Opus 4.7 71fc30eed7 Add architecture diagrams to MAINTENANCE.md
Two mermaid diagrams under Sync workflow → Architecture:

- Branch model — parent's master HEAD + release tags vs docs' main
  (released) and nightly (draft) branches, with the diff base
  invariant (base_release_tag only bumps on release merge).
- Per-run flow — Preconditions → Phases 1, 1.5, 2, 3, 3.5, 4, 5, 6,
  7, 8. Color-coded: blue for deterministic helper scripts (no LLM
  tokens), yellow for data artifacts, red for human-review warnings.

GitHub renders mermaid inline so these display directly when viewing
MAINTENANCE.md on github.com. They live outside SKILL.md to avoid
adding ~80 lines to the runtime-loaded operational playbook.

Also cleaned up the Two modes section's incremental-diff wording —
it predated the cumulative-diff redesign and was still describing
`last_synced_sha → parent main HEAD`. Now correctly says
`base_release_tag → parent default-branch HEAD` and points at
compare-window.mjs as the implementation.

SKILL.md gets a one-line pointer to the diagrams so future readers
of the orchestrator playbook know where the picture lives.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
2026-05-13 12:25:50 +02:00
Aron PrinsandClaude Opus 4.7 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]>
2026-05-13 11:55:47 +02:00
Aron PrinsandClaude Opus 4.7 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]>
2026-05-13 11:40:41 +02:00
Aron PrinsandClaude Opus 4.7 5647ca941f Apply Tier 3 dry-run fixes: parent default branch, dead globs, bisection, watchers
A design-validation dry-run against the real 318→512 release gap
surfaced seven issues that would break or misbehave at runtime.
All addressed:

1. Parent default branch is `master`, not `main` (verified via
   gh api ... -q .default_branch). Resolved dynamically at runtime:
   new Preconditions step 5 calls the API once and stores the result
   as <parent-default>; all subsequent references use that variable.
   Resilient to future parent renames.

2. Two dead globs in anchor-map.json:
   - env-vars: `server/src/env.ts` → replaced with `server/src/config.ts`
   - schemas: dropped `server/src/db/schema/**` (dir doesn't exist;
     coverage already provided by `packages/**/schema/*.ts`).

3. Truncation fallback was insufficient. The 318→512 gap had 5 of 6
   tag-to-tag windows still hitting the 300-file cap. Rewrote Phase 2
   to use recursive midpoint bisection by commit SHA: detect via
   files.length==300 OR total_commits>commits.length, split at median
   SHA, recurse until every leaf is within caps. Also corrected the
   commits cap (250, not ~300) and added explicit status-merge rules
   for unioning leaf file lists (added+removed → drop, etc.).

4. Adapter watcher would over-trigger on renames within the *-local
   family. Extended detect rule to check for correlated add/remove
   of similarly-named dirs (rename heuristic) before assuming a new
   adapter — uses compare's status:'renamed' signal.

5. Four new watchers for previously unwatched busy surfaces:
   - web-ui (ui/src/**) — invalidates screenshots and end-user flows
   - server-services (server/src/services/**) — user-visible behaviour
   - server-adapters (server/src/adapters/**) — distinct from packages/
   - db-migrations (packages/db/migrations/**) — context-only

6. auto_merge_safety.max_lines_changed 20→50. The current gap shows
   real-world .env.example additions land in batches (a feature ships
   with 10–20 related env vars + comments) and were being needlessly
   demoted to PR tier.

7. New Preconditions step 6: preflight every concrete (non-glob)
   watcher path via gh api .../contents and warn on 404. Would have
   caught the server/src/env.ts dead glob immediately.

Watcher count: 7 → 11.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
2026-05-13 10:50:08 +02:00
Aron PrinsandClaude Opus 4.7 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 c0ff148 — never registered in content.json)
   that the user should triage separately.

C. Screenshot registry backfill — scripts/sync/backfill-screenshots.mjs
   scaffolds entries for every PNG under docs/user-guides/screenshots/.
   Run once; registry.json now ships with 274 scaffolded entries
   (137 light + 137 dark). Each has placeholder `depends_on: []` and
   a `_todo` field; humans fill in parent-repo path anchors over time
   to activate staleness detection per-entry. Re-runs preserve any
   entry with non-empty depends_on.

D. content.json edit serialisation — SKILL Phase 5 now requires
   subagents to return a structured `nav_addition` object alongside
   page content rather than editing site/content.json directly. The
   orchestrator merges all nav_additions on the main thread after
   subagents finish. Prevents the parallel-write race where two
   subagents creating new pages clobber each other's nav entries.

E. gh compare truncation — SKILL Phase 2 now explicitly documents
   the GitHub compare endpoint's ~300-file/~300-commit cap and the
   window-splitting fallback (walk intermediate release tags for
   release mode; binary-split commit list for nightly mode).

Also wires the scripts into package.json (sync:lint-links,
sync:verify-nav, sync:check, sync:backfill-screenshots) and into
SKILL Phase 7 (sync:check runs before commit; dangling/broken-link
failures abort the run).

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
2026-05-13 10:37:42 +02:00
Aron PrinsandClaude Opus 4.7 28af20be39 Fix correctness gotchas in /sync-docs design
Several issues found while reviewing the skill — most stemming from
the incremental-diff model that the first cut used. The fix is a
shift to cumulative diffs (always from the last release tag) plus
explicit reconciliation handling.

Changes:

- Cumulative diff window for BOTH modes. Nightly was using
  yesterday's SHA as the diff base; now it uses base_release_tag
  like release mode does. This makes reverts auto-cancel in the
  manifest (they're net-zero across the cumulative window) instead
  of needing message-regex filtering.

- Remove skip_patterns from anchor-map.json. They were dangerous:
  filtering ^revert by message would HIDE the undo signal when a
  previously-processed commit got reverted. The proper filter is
  surface impact via watcher path globs + cumulative-diff cancellation.

- Add Phase 3.5 (Reconciliation) to the SKILL. Catches the remaining
  case: a doc edit was committed in a prior run for a feature that
  has since been reverted. The manifest_hash + last_applied_manifest_hash
  comparison surfaces "disappeared" entries as ⚠ Reconcile flags for
  manual review — the skill never auto-undoes prose edits.

- auto_merge_safety: add explicit forbid_kinds: [removed, renamed].
  A 1-line rename is still a breaking change and was previously
  guarded only by SKILL prose.

- Rename state fields for clarity. synced_to/synced_sha were
  ambiguous between branches. New schema: branch_mode,
  base_release_tag, base_release_sha, last_seen_parent_sha,
  last_applied_manifest_hash. base_release_tag is the diff base
  and only bumps on a successful release merge — the invariant
  that makes cumulative diffs stable.

- Release mode is now self-sufficient (doesn't require nightly
  drafts to exist). Fixes the chicken-and-egg for the first run
  catching up multiple releases.

- PENDING.md is regenerated each run, not appended, so it never
  carries stale entries from reverted commits.

- New special-cases entries for parent renames, nightly-with-open-PRs
  at release time, and quarantine race conditions.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
2026-05-13 10:30:12 +02:00
Aron PrinsandClaude Opus 4.7 51c4c1d082 Add /sync-docs skill — keeps docs in lockstep with parent paperclip repo
A Claude Code skill that detects code-surface changes in
paperclipai/paperclip (CLI, env vars, REST routes, adapters, plugin
SDK, schemas) and produces doc updates in our friendly-tutorial voice.

Two modes:
- nightly — tracks parent main, targets the nightly branch, preview deploy
- release — on parent tag, merges nightly → main, tags, ships live

The branch model keeps end users on released paperclip from seeing docs
for unreleased features: main always reflects the latest released tag,
nightly accumulates drafts in between.

Skill files ship inside the repo at skills/sync-docs/ so the workflow
is versioned alongside the docs it produces. Install with a symlink
into ~/.claude/skills/.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
2026-05-13 10:18:54 +02:00