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]>
The wet-run found the plugin-sdk watcher's docs_paths
(docs/reference/skills.md, docs/how-to/write-a-company-skill.md)
target agent skill bundles, not the plugin SDK. Separate concerns.
- plugin-sdk now targets docs/reference/plugins/sdk.md (to be created
by the skill on first hit) and docs/administration/plugins.md.
- New agent-skills watcher targets the skills/ surface in parent and
routes to docs/reference/skills.md + docs/how-to/write-a-company-skill.md.
Watcher count: 11 → 12.
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
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]>
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]>
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]>
Scaffolds the data layer for the /sync-docs skill:
- scripts/sync/anchor-map.json — declares which parent-repo paths each
watcher tracks, where they route in our docs, and whether changes
are auto-merge (mechanical, schema-bound) or PR tier (judgment calls).
- scripts/sync/state.example.json — template for .sync-state.json.
- .sync-state.json — per-branch sync state, seeded for an initial
release-mode run from v2026.318.0.
- docs/user-guides/screenshots/registry.json — screenshot dependency
tracking so the skill can flag stale captures.
The skill itself follows in the next commit.
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Deploys the current branch into a subfolder of the gh-pages branch so
previews can sit alongside the main site without replacing it.
./scripts/publish-gh-pages-preview.sh v2 # deploy to /v2/
./scripts/publish-gh-pages-preview.sh v2 --remove # take it down
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
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]>