Skip to content

Add link snapshot contract + CI enforcement#98

Open
tuliren wants to merge 15 commits into
mainfrom
link-snapshot-contract
Open

Add link snapshot contract + CI enforcement#98
tuliren wants to merge 15 commits into
mainfrom
link-snapshot-contract

Conversation

@tuliren
Copy link
Copy Markdown
Collaborator

@tuliren tuliren commented May 16, 2026

Summary

  • Introduces an append-only link snapshot (link-snapshot.yaml, 161 URLs) recording every URL the docs site has ever served — nav pages from docs.json, literal redirect sources, and on-disk .mdx/.md files under lfm/, leap/, examples/, deployment/.
  • Adds scripts/generateLinkSnapshot.ts (--update / --check) plus a husky pre-commit hook and a GitHub workflow (check-link-snapshot.yaml) that fails any PR which makes a snapshotted URL stop resolving.
  • Brings in minimal Node/TypeScript scripting scaffolding (package.json, tsconfig.json, scripts/runScriptWithEnv.sh) modeled after app-monorepo-template/apps/web. The existing scripts/generate_snippets.py and check-docs.yaml workflow are untouched.

When the check flags a URL, contributors pick one of three remediations: (1) add a redirects entry in docs.json, (2) keep the .mdx on disk but drop it from navigation, or (3) move the URL to deleted: in link-snapshot.yaml with a reason. Option 3 is described in the README as the last-resort path.

Test plan

  • npm install wires the husky pre-commit hook (run once per clone).
  • npm run snapshot:check exits 0 on a clean tree (161 URLs verified).
  • Delete an existing entry from docs.json navigation and its .mdx file without adding a redirect → npm run snapshot:check fails naming the URL and listing the three remediation paths.
  • Add a redirects entry for that URL → check passes.
  • Alternatively, leave the .mdx on disk but remove from nav → check passes (deprecate-but-keep path).
  • Move the URL from active to deleted: with a reason, run npm run snapshot:update, → URL drops from active and check passes.
  • Stage a .mdx edit and git commit → pre-commit hook regenerates link-snapshot.yaml and re-stages it.

🤖 Generated with Claude Code

Resolve https://linear.app/liquid-ai/issue/DOC-26/add-ci-to-ensure-old-links-still-work-after-changes-introduced-by-a-pr

tuliren and others added 5 commits May 15, 2026 19:45
Mirrors the `npm run script scripts/<file>.ts` convention from
app-monorepo-template/apps/web so future repo automation has a uniform
entry point. The existing Python script under scripts/ is left as-is.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
`link-snapshot.yaml` is an append-only record of every URL the docs
site has ever served (nav pages + literal redirect sources + on-disk
.mdx/.md files under lfm/, leap/, examples/, deployment/). The
companion script derives URLs from `docs.json`, supports `--update`
and `--check`, and verifies that each `active` URL still resolves via
the current nav, disk pages, or redirect chain. Entries under
`deleted:` are intentionally retired and skipped by the check.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Regenerates link-snapshot.yaml and re-stages it whenever docs.json or
any .mdx/.md file is in the commit. Installed automatically by
"npm install" via the husky prepare script.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Runs `npm run snapshot:check` on every PR against main and on pushes
to main. The check fails the build if any URL recorded in
`link-snapshot.yaml#active` no longer resolves under the current
`docs.json` + on-disk pages, prompting the contributor to add a
redirect, deprecate the page in place, or move the URL to `deleted:`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Explains the one-time `npm install` to wire the pre-commit hook, the
three remediation paths when CI flags a missing URL (redirect / keep
as deprecated / move to `deleted:`), and the manual commands to
regenerate or verify the snapshot.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@tuliren tuliren requested a review from Paulescu as a code owner May 16, 2026 02:47
@mintlify
Copy link
Copy Markdown

mintlify Bot commented May 16, 2026

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated (UTC)
liquid-docs 🟢 Ready View Preview May 16, 2026, 2:47 AM

💡 Tip: Enable Workflows to automatically generate PRs for you.

tuliren and others added 2 commits May 15, 2026 20:10
The previous contract-violation message told contributors to "move the
URL to deleted with a reason" but did not show the YAML shape, so it
was unclear whether reason was a quoted string, required, or how to
add a retired_at field. Two changes:

1. Both the contract-violation message and a new "malformed deleted
   entry" check now print a fenced YAML example.
2. Each deleted entry is now validated to have a non-empty reason
   field; bare URLs under deleted are rejected at check time with the
   same example.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
tuliren and others added 4 commits May 15, 2026 20:16
README now carries a one-paragraph orientation pointing readers at
CLAUDE.md for the full mechanics. CLAUDE.md gains a new "Link
Snapshot Contract" section covering the file format, the resolution
algorithm CI uses to decide whether an `active` URL is satisfied,
the three remediation paths in priority order, and the common
pitfalls (forgetting redirects on path renames, skipping `npm
install`, manually editing `active`).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The header on link-snapshot.yaml already explained that contributors
move URLs from active to deleted; it did not show what either form
looks like. Two commented examples now ride along with the file so
that someone editing it directly does not need to cross-reference
CLAUDE.md or trigger a CI failure to discover the shape.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The runScriptWithEnv.sh wrapper was a single exec call with no
env-file handling — invoking tsx straight from package.json is one
fewer indirection and removes the asymmetry with how scripts are
typically run elsewhere.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
When the examples sat at the top of the file they were easy to skim
past — by the time a contributor scrolled down to `deleted:` they
had to remember a YAML shape from screens earlier. Splitting the
serializer so it emits `active:` first, then the example block,
then `deleted:` keeps the examples adjacent to the field they
describe.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Compressed from 60+ lines to ~17. Cut the file-shape YAML block (the
snapshot file now carries its own example comments), the tooling
table (folded into prose), and the common-pitfalls section. Kept
the resolution algorithm and the three remediation paths since
those are what a contributor needs to act on a CI failure.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
tuliren and others added 2 commits May 15, 2026 20:42
The previous one-paragraph orientation said what the snapshot was
but not how the append-only invariant survives the pre-commit hook
regenerating the file on every commit. Four bullets now make the
mechanism explicit so contributors do not have to follow the link
to CLAUDE.md to understand why a missing redirect breaks CI.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
A leading bullet now names `active:` and `deleted:` as the two
sections of link-snapshot.yaml so the append-only invariant in the
following bullets reads as "the active field stays append-only,"
not "active is append-only" without antecedent. Also pulled the
hand-edit clue into the retire-a-URL bullet.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant