Skip to content

feat(docs): require every published doc slug to be recorded in the Fern lock - #515

Merged
Tim Beyer (TimBeyer) merged 2 commits into
mainfrom
docs/enforce-fern-slug-lock
Sep 21, 2026
Merged

Tim Beyer (TimBeyer) merged 2 commits into
mainfrom
docs/enforce-fern-slug-lock

Conversation

@TimBeyer

@TimBeyer Tim Beyer (TimBeyer) commented Sep 21, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

The v1-to-v2 migration guide merged in #514 without an entry in documentation/fern-slugs.lock.json. That was allowed by design — reconcileLock supplies an entry in memory, so the page publishes either way — but it silently shipped without slug protection:

  • the existing check can only compare a slug against one the lock already names, so an unrecorded page has nothing to compare against
  • the sync workflow runs pnpm docs:fern without --update-lock, so an entry an author never commits never appears

That page's first slug reword would have moved a live URL with no redirect — the exact failure the lock exists to prevent. It was the first new page since the lock landed in #442, so the gap had never been reachable before. The release sync has since run, so migrate-optimization-sdk-v1-to-v2 is a live URL and the recorded entry matches what actually shipped.

Enforcement

pnpm fern:check now reports an unrecorded slug the same way it reports a changed one, naming the fix:

documentation/guides/…-from-v1-to-v2.md:1: fern.slug "migrate-optimization-sdk-v1-to-v2" is not
recorded in documentation/fern-slugs.lock.json; rerun `pnpm docs:fern -- --update-lock` to record it

The missing entry is recorded here, so the check passes on main. The slug-change path is unchanged, and --update-lock still records new pages and redirects as before.

fern:check is also wired into the documentation-integrity Stop hook, which ran knowledge:check and guides:check but not the one check that decides whether an edit reaches the public site. The publishing contract breaks silently, so it needs a trigger that does not depend on someone thinking to look. Watched paths gain documentation/concepts, the slug lock, and scripts/fern.

Documentation

ADR 0002 and the documentation/AGENTS.md publishing section already own most of this contract, so the new docs-site-publishing skill points at both rather than becoming a third copy. It keeps only what neither records:

  • a document absent from its group README children: is silently not published — the exporter reads that list and never scans the filesystem
  • children: order is global across the group, so a page's index in the one guides/ list decides its position within its fern.section
  • a new slug must be recorded and committed with the page (the ADR covers only changing one)

documentation/AGENTS.md gains those first and third points. optimization-guide-authoring gains a publication step routing to the skill, and checklist section G asks only what a check cannot judge — intent: whether the children: index is the intended position, whether a slug change is deliberate, and whether the lock diff shipped with the guide.

Validation

  • pnpm fern:check, pnpm guides:check, pnpm knowledge:check — all pass
  • pnpm lint, pnpm typecheck — pass (typecheck needed a pnpm install first; unrelated stale deps after chore(repo): release packages for main #503)
  • ESLint on both changed scripts — pnpm lint covers only lib packages, not scripts/
  • bash -n on the hook; targeted Prettier; git diff --check clean
  • verified the new check fires on an unrecorded page and that a changed slug is still reported as needing a redirect
  • exercised the hook both ways: silent with exit 0 on a clean tree, and reporting the unrecorded-slug problem with its fix command after removing the lock entry

🤖 Generated with Claude Code

…rn lock

`documentation/guides/migrating-optimization-sdk-packages-from-v1-to-v2.md`
merged in #514 without a `documentation/fern-slugs.lock.json` entry, which was
allowed: the exporter reconciles an unrecorded document in memory, so the page
publishes either way. What it silently lost was slug protection. The existing
check can only compare a slug against one the lock already names, and the sync
workflow runs `pnpm docs:fern` without `--update-lock`, so an entry an author
never commits never appears. That page's first slug reword would have moved a
live URL with no redirect — the exact failure the lock exists to prevent.

It was the first new page since the lock landed in #442, so the gap had never
been reachable before.

`pnpm fern:check` now reports an unrecorded slug the same way it reports a
changed one, naming the command that fixes it, and records the missing v1-to-v2
entry so the check passes on `main`. The slug-change path is unchanged.

Because a check only catches what reaches CI, the contract is also written down
where an author meets it: a new `docs-site-publishing` skill owns the four
artifacts that decide whether a page ships (group README `children:`, the
`fern:` block, the slug lock, MDX safety), the group-global sidebar ordering
rule, and the release-gated sync. `optimization-guide-authoring` now routes to
it from a publication step in its workflow, and the self-review checklist gains
a mechanical section G. `documentation/AGENTS.md` documented only how to change
a slug, never how to record one, and never that `children:` is what publishes a
document at all; both are now stated.

Validation: `pnpm fern:check`, `pnpm guides:check`, `pnpm knowledge:check`,
`pnpm lint`, `pnpm typecheck`, ESLint on both changed scripts, targeted Prettier
on every changed file, `git diff --check`. Verified by hand that the new check
fires on an unrecorded page and that a changed slug is still reported.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@bito-code-review

bito-code-review Bot commented Sep 21, 2026 •

Copy link
Copy Markdown

Code Review Agent Run #8ec28c

Actionable Suggestions - 0
Review Details
  • Files reviewed - 8 · Commit Range: 443a302..443a302
    • documentation/AGENTS.md
    • documentation/fern-slugs.lock.json
    • scripts/fern/build.ts
    • scripts/validate-fern-export.ts
    • skills/docs-site-publishing/SKILL.md
    • skills/docs-site-publishing/package.json
    • skills/optimization-guide-authoring/SKILL.md
    • skills/optimization-guide-authoring/references/authoring-checklist.md
  • Files skipped - 0
  • Tools
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful
    • GITHUB_ADVISORY (Security Vulnerability) - ✔︎ Successful
    • OWASP (Security Vulnerability) - ✔︎ Successful

Bito Usage Guide

Commands

Type the following command in the pull request comment and save the comment.

  • /review - Manually triggers an incremental AI Review.

  • /review full - Manually triggers a full AI Review.

  • /pause - Pauses automatic reviews on this pull request.

  • /resume - Resumes automatic reviews.

  • /resolve - Marks all Bito-posted review comments as resolved.

  • /abort - Cancels all in-progress reviews.

Refer to the documentation for additional commands.

Configuration

This repository uses Default Agent You can customize the agent settings here or contact your Bito workspace admin at jared.jolton@contentful.com.

Documentation & Help

AI Code Review powered by Bito Logo

@bito-code-review

bito-code-review Bot commented Sep 21, 2026 •

Copy link
Copy Markdown

Impact Analysis by Bito

Cross-Repository Impact Analysis
What Changed Impact of Change Suggested Review Actions
fern:check now rejects any published document whose slug is absent from the slug lock, and the repository adds a dedicated documentation publishing skill. - intra-repo: documentation publishing workflow: The changed validator, hook, documentation guidance, README manifest, and skill all govern how this repository produces the generated Fern bundle. - Verify that every currently published document already has an entry in documentation/fern-slugs.lock.json; otherwise fern:check will fail repository-wide.
- Add automated coverage for both a new document with no lock entry and a new document with a recorded lock entry.
- Confirm the generated nav-block ordering after inserting the migration guide into the README children list.
The repository adds a reusable docs-site-publishing skill package and updates optimization-guide-authoring to delegate publication wiring to it. - verify-needed: external skill consumers: The new package is intended for agent/tooling consumption, but the indexed organization-wide search found no docs-site-publishing references and no concrete external consumer call site was available for verification. - Verify how skill packages are discovered, published, or installed by the consuming agent framework before relying on package.json alone.
- Add a packaging or discovery test proving that SKILL.md is included and the paths frontmatter is honored.
- Confirm whether the package name and version need registration in a central skill manifest or workspace configuration.
Code Paths Analyzed

Impact:
The change makes slug locking mandatory for every published page, extends the documentation integrity hook to run Fern validation, records the new migration guide slug, and introduces explicit authoring guidance for publication wiring. The main behavioral risk is that existing or newly discovered documents missing lock entries will now fail fern:check.

Flow:
A documentation change triggers scripts/documentation-integrity-hook.sh, which runs knowledge:check, guides:check, and fern:check. fern:check calls buildBundle with acceptSlugChanges=false. buildBundle loads documents from README children manifests, transforms them, reads documentation/fern-slugs.lock.json, rejects missing or changed slug entries, resolves redirects, and reports validation problems. The publishing workflow uses the same build pipeline with lock updates enabled when explicitly requested.

Direct Changes (Diff Files):
• documentation/AGENTS.md [30-50] — Documents that README children controls publication and clarifies that both new and changed slugs must be recorded in the lock.
• documentation/fern-slugs.lock.json [32-40] — Adds the slug lock entry for the Optimization SDK v1-to-v2 migration guide.
• scripts/documentation-integrity-hook.sh [12-80] — Includes concepts, the slug lock, Fern scripts, and fern:check in the documentation integrity trigger and failure report.
• scripts/fern/build.ts [87-119] — Changes slug validation so an absent lock entry is an error rather than an accepted new-page state.
• scripts/validate-fern-export.ts [4-13] — Updates validator documentation to describe enforcement for both new and changed slugs.
• skills/docs-site-publishing/SKILL.md [1-64] — Adds a reusable skill describing README manifests, sidebar ordering, slug locking, validation, release gating, and reporting.
• skills/docs-site-publishing/package.json [1-9] — Adds package metadata for distributing the new documentation publishing skill.
• skills/optimization-guide-authoring/SKILL.md [67-68,114-118,170-181] — Separates content authoring from publication wiring and adds Fern validation and publication-status reporting to the workflow.
• skills/optimization-guide-authoring/references/authoring-checklist.md [2,291-305] — Adds checklist items for section-relative ordering, deliberate slug changes, and committing the slug lock with the guide.

Repository Impact:
• Fern export and validation: buildBundle now reports missing lock entries and can fail validation for pages that previously passed.
• Documentation integrity hook: Documentation edits now execute an additional validator and include its output in the capped failure report.
• Documentation navigation: The new migration guide is inserted into the global guides children list, which affects generated sidebar ordering.
• Agent authoring workflows: The guide-authoring skill now depends conceptually on docs-site-publishing for publication wiring and reporting.

Cross-Repository Dependencies:
• Potential external consumers of the new skill package: The package is named and versioned for reuse, but no indexed consumer reference was found.

Database/Caching Impact:
• None

API Contract Violations:
• The Fern validation contract becomes stricter: a published document without a documentation/fern-slugs.lock.json entry is now invalid.
• The README children list is treated as a publication manifest; adding a file without adding it to children will silently omit it from the generated site.
• No HTTP, RPC, database, message, or exported runtime API contract changes are shown.

Infrastructure Dependencies:
• The documentation integrity Stop hook now requires pnpm fern:check in addition to knowledge:check and guides:check.
• The release-gated documentation sync must consume a committed slug lock entry because it runs without --update-lock.
• The generated Fern nav-block and redirect artifacts should be reviewed after changes to README children ordering or slug history.

Additional Insights:
• Validation failure behavior: The hook runs all validators and caps the combined output at 40 lines, so Fern failures may be truncated when multiple documentation checks fail.
• Repository-wide migration risk: The new missing-entry check iterates over every loaded published document, so an incomplete historical lock can break unrelated documentation changes.
• Tooling availability: pnpm was not available in the review environment, so the actual fern:check result could not be executed locally.

Testing Recommendations

Frontend Impact:
• Generate the Fern nav-block and verify that migrating-optimization-sdk-packages-from-v1-to-v2 appears under the intended Migration guides position rather than merely appearing in the filesystem.

Service Integration:
• Run the documentation integrity hook with a changed guide and confirm knowledge:check, guides:check, and fern:check all execute and their failures are combined correctly.
• Run the release/export workflow without --update-lock and confirm a committed slug entry is preserved in the generated bundle.

Data Serialization:
• Test loading documentation/fern-slugs.lock.json with the new migration guide entry and verify reconcileLock and resolveRedirects produce the expected lock and redirect output.
• Test malformed or incomplete lock data, including a missing slugs object, to ensure the existing fallback behavior remains safe.

Privacy Compliance:
• No privacy-sensitive data path is changed; no additional privacy-specific test is required beyond normal documentation validation.

Backward Compatibility:
• Run fern:check against the complete current documentation tree and enumerate every page reported as missing from the lock.
• Test a new document with no lock entry and assert that validation fails with the documented rerun command.
• Test a new document with a recorded lock entry and assert that validation passes.
• Test an existing document whose slug changes and verify that validation still requires --update-lock and emits a redirect.

OAuth Functionality:
• None

Cross-Service Communication:
• If the publishing sync consumes generated nav-block or redirect artifacts from this repository, run an end-to-end sync fixture and verify both page inclusion and redirect generation.
• Verify that the skill packaging pipeline discovers skills/docs-site-publishing/SKILL.md from the new package metadata.

Reliability Testing:
• None

Additional Insights:
• Because pnpm was unavailable in the review environment, execute pnpm fern:check, pnpm guides:check, pnpm knowledge:check, and the relevant package/discovery checks in CI or a Node/pnpm-enabled environment.
• Add unit tests around buildBundle for missing lock entries so the stricter behavior is protected against regression.
• Check the 40-line failure cap when all three validators fail, ensuring the Fern-specific remediation command remains visible.

Analysis based on known dependency patterns and edges. Actual impact may vary.

@bito-code-review

Copy link
Copy Markdown

✅ Review Settings Overridden

Status: Overridden Successfully

Guidelines:

  • Accepted:

    • General : Review Posture, Repo Truth And Boundaries, Domain Invariants

    • Language : typescript= TypeScript Review Evidence

Note: Extra guidelines beyond 3 general purpose guidelines and 1 language specific guideline per language are not processed. Guidelines are fetched from the source branch.

bito-code-review[bot]
bito-code-review Bot previously approved these changes Sep 21, 2026
@TimBeyer
Tim Beyer (TimBeyer) marked this pull request as draft September 21, 2026 12:42
…en down

The first draft restated ADR 0002 and the `documentation/AGENTS.md` publishing
section at length. Both already own this material, so the skill was mostly a
third copy that could drift from them. It now points at those two as sources of
truth and keeps only what neither records: that a document absent from its group
README `children:` is silently not published, that `children:` order is global
across the group so an index decides position *within* a section, and that a new
slug has to be recorded and committed with the page.

Same cut in the self-review checklist. `pnpm fern:check` mechanically enforces
the `fern:` block shape, slug format, link and anchor resolution, MDX safety, and
slug recording, so restating those as human checkboxes added nothing. Section G
now asks only what a check cannot judge: intent — whether the `children:` index
is the intended position, whether a slug change is deliberate, and whether the
lock diff shipped with the guide.

Descriptions are matched, not read, so the skill's own description is now a
short purpose plus its trigger conditions rather than a summary of its contents.

Wire `fern:check` into the documentation-integrity Stop hook, which ran
`knowledge:check` and `guides:check` but not the check that decides whether an
edit reaches the public site at all. This is the part that actually fires
unprompted: the publishing contract breaks silently, so it needs a trigger that
does not depend on anyone thinking to look. Watched paths gain
`documentation/concepts`, the slug lock, and the fern scripts.

Validation: `pnpm fern:check`, `pnpm guides:check`, `pnpm knowledge:check`,
`bash -n` on the hook, targeted Prettier, `git diff --check`. Exercised the hook
both ways — silent and exit 0 on a clean tree, and reporting the unrecorded-slug
problem with its fix command after removing the lock entry.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@TimBeyer
Tim Beyer (TimBeyer) merged commit e5d6dc7 into main Sep 21, 2026
43 checks passed
@TimBeyer
Tim Beyer (TimBeyer) deleted the docs/enforce-fern-slug-lock branch September 21, 2026 15:12

@bito-code-review bito-code-review Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review Agent Run #876303

Actionable Suggestions - 1
  • skills/docs-site-publishing/SKILL.md - 1
    • Missing migration-knowledge-authoring in scope list · Line 62-64
Additional Suggestions - 1
  • skills/docs-site-publishing/SKILL.md - 1
    • Incorrect hook claim · Line 46-48
      The claim that `fern:check` "runs from the `Stop` hook" is incorrect: no `Stop` hook exists in `.husky/` (only `commit-msg`, `pre-commit`, `pre-push`, `prepare-commit-msg`), and `pre-commit` runs only lint-staged (prettier on md). `fern:check` actually runs in CI (`main-pipeline.yaml:255`, `sync-fern-docs.yaml:113`). Reword so an agent doesn't skip a local run expecting a hook.
Review Details
  • Files reviewed - 4 · Commit Range: 443a302..7058dac
    • scripts/documentation-integrity-hook.sh
    • skills/docs-site-publishing/SKILL.md
    • skills/optimization-guide-authoring/SKILL.md
    • skills/optimization-guide-authoring/references/authoring-checklist.md
  • Files skipped - 0
  • Tools
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful

Bito Usage Guide

Commands

Type the following command in the pull request comment and save the comment.

  • /review - Manually triggers an incremental AI Review.

  • /review full - Manually triggers a full AI Review.

  • /pause - Pauses automatic reviews on this pull request.

  • /resume - Resumes automatic reviews.

  • /resolve - Marks all Bito-posted review comments as resolved.

  • /abort - Cancels all in-progress reviews.

Refer to the documentation for additional commands.

Configuration

This repository uses Default Agent You can customize the agent settings here or contact your Bito workspace admin at jared.jolton@contentful.com.

Documentation & Help

AI Code Review powered by Bito Logo

Comment on lines +62 to +64
Guide prose and structure (`optimization-guide-authoring`), migration routing
(`migration-guide-authoring`), fact derivation (`sdk-knowledge-authoring`), and the
`contentful-docs` repository's own conventions.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing migration-knowledge-authoring in scope list

The Not in scope section now lists only sdk-knowledge-authoring for fact derivation, but the previous version also referenced migration-knowledge-authoring for migration fact derivation. If that skill still exists, readers with migration fact questions may be directed to the wrong skill.

Code suggestion
Check the AI-generated fix before applying
Suggested change
Guide prose and structure (`optimization-guide-authoring`), migration routing
(`migration-guide-authoring`), fact derivation (`sdk-knowledge-authoring`), and the
`contentful-docs` repository's own conventions.
Guide prose and structure (`optimization-guide-authoring`), migration routing
(`migration-guide-authoring`), SDK or migration fact derivation (`sdk-knowledge-authoring`,
`migration-knowledge-authoring`), and the
`contentful-docs` repository's own conventions.
Suggested based on your custom review guideline "Repo Truth And Boundaries"

Code Review Run #876303


Should Bito avoid suggestions like this for future reviews? (Manage Rules)

  • Yes, avoid them

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.

2 participants