mirror of
https://github.com/paperclipai/paperclip-docs.git
synced 2026-11-09 12:21:27 -10:00
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]>
58 lines
2.2 KiB
JavaScript
58 lines
2.2 KiB
JavaScript
#!/usr/bin/env node
|
|
// One-shot scaffolder for docs/user-guides/screenshots/registry.json.
|
|
//
|
|
// Walks every PNG under docs/user-guides/screenshots/{light,dark} and produces a registry entry
|
|
// with placeholder `depends_on: []` and a `_todo` note. Humans fill in the depends_on parent paths
|
|
// over time — until that happens, staleness detection for that entry is a no-op.
|
|
//
|
|
// Re-runs are safe: existing entries with non-empty depends_on are preserved verbatim.
|
|
//
|
|
// Usage: node scripts/sync/backfill-screenshots.mjs
|
|
|
|
import { readdirSync, readFileSync, writeFileSync, statSync } from "node:fs";
|
|
import { join, resolve, relative } from "node:path";
|
|
|
|
const ROOT = resolve(new URL("../..", import.meta.url).pathname);
|
|
const SHOT_DIR = join(ROOT, "docs/user-guides/screenshots");
|
|
const REGISTRY = join(SHOT_DIR, "registry.json");
|
|
|
|
function walk(dir, acc = []) {
|
|
for (const entry of readdirSync(dir)) {
|
|
const p = join(dir, entry);
|
|
const s = statSync(p);
|
|
if (s.isDirectory()) walk(p, acc);
|
|
else if (entry.endsWith(".png")) acc.push(p);
|
|
}
|
|
return acc;
|
|
}
|
|
|
|
const existing = JSON.parse(readFileSync(REGISTRY, "utf8"));
|
|
const preserve = new Map();
|
|
for (const e of existing.entries ?? []) {
|
|
if (Array.isArray(e.depends_on) && e.depends_on.length > 0) preserve.set(e.file, e);
|
|
}
|
|
|
|
const files = walk(SHOT_DIR).map((p) => relative(SHOT_DIR, p)).sort();
|
|
|
|
const entries = files.map((file) => {
|
|
if (preserve.has(file)) return preserve.get(file);
|
|
const theme = file.startsWith("dark/") ? "dark" : "light";
|
|
return {
|
|
file,
|
|
theme,
|
|
viewport: "1440x900",
|
|
route: null,
|
|
captured_against: null,
|
|
captured_sha: null,
|
|
depends_on: [],
|
|
_todo: "Fill in `route` (URL path captured) and `depends_on` (parent-repo paths whose changes invalidate this screenshot). Until depends_on is populated, staleness detection is a no-op for this entry."
|
|
};
|
|
});
|
|
|
|
const out = { ...existing, entries };
|
|
writeFileSync(REGISTRY, JSON.stringify(out, null, 2) + "\n");
|
|
|
|
const scaffolded = entries.filter((e) => !preserve.has(e.file)).length;
|
|
const preserved = entries.length - scaffolded;
|
|
console.log(`Wrote ${entries.length} entries to ${relative(ROOT, REGISTRY)} (${scaffolded} scaffolded, ${preserved} preserved).`);
|