Tell one canonical wiring story across the docs - #183
Conversation
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Comment |
|
Coordination note: #179 (sibling branch) adds |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 4deb76db1b
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
Important
The jiti claim is wrong in two places: leadtype init never installs it, and a scaffold without it throws on first render. A reader who trusts the docs over the adjacent bun add jiti gets a broken app.
Reviewed changes — full read of the 17-file docs diff, with every API claim on the new reference page traced back to packages/leadtype/src.
- New
docs/reference/project.mdx— canonical reference forcreateDocsProject()/resolveProject(): discovery order, options tables,baseUrlprecedence, the diagnostics-vs-throws split,ProjectDiagnosticids, andnavigationOrigin. Registered in the reference nav and present inpaths.lock.json. - Canonical entry point flipped — quickstart step 2,
build-a-docs-site,use-the-source-primitive,architecture,how-it-worksandindexnow lead with zero-argcreateDocsProject();createDocsSource()is relabelled an escape hatch rather than "most cases". --base-urldropped from documented commands — everyleadtype generateinvocation now relies on the config'sbaseUrl, which #182 put in place.- Legacy shapes confined, not deleted —
groupsmoved under a Legacy callout in the source reference, repeated--docs-dirflags kept in a labelled callout inconfigure-sources. - Naming sweep — root-config examples switched to
defineLeadtypeConfig,prefix→routePrefix, plus a newgitSource()multi-repo section.
I verified the load-bearing accuracy claims rather than assuming them. All of the following check out against this branch: every createDocsProject/resolveProject option name, type and default (project/index.ts:88-130); all seven diagnostic ids verbatim; the explicit | inherited | groups | inferred union; baseUrl precedence arg > config > env (generate.ts:1796, internal/docs-url.ts:179-207), so dropping --base-url is safe; --name/--summary falling back to config product (generate.ts:580-589); the inheritConfig whitelist excluding baseUrl (config/inherit.ts:44-56), making the "never inherited from the source" comment true; and every rewritten snippet's imports and call shapes, including createLoadPageData's internal slug ?? [] which makes dropping the old ?? [] correct. The three new internal anchors all resolve.
ℹ️ Nitpicks
docs/quickstart.mdx:118— "the exactlib/source.tsthatinitscaffolds" holds for Next only. Nuxt scaffolds a lazily-memoizedgetSource()(init-templates.ts:338-350), and Astro/SvelteKit write tosrc/lib/source.ts. Since step 3 is framed as "the only step that varies by framework", a Nuxt reader hits a contradiction one step early.docs/reference/project.mdx:62— the env fallback list skips two real steps:PORTLESS_URLand the browser'slocation.originboth sit ahead ofhttp://localhost:<PORT>(internal/docs-url.ts:179-207).
Claude Opus | 𝕏
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 14b6ed0288
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
ℹ️ No blocking issues in the new commit — all three prior threads are genuinely fixed. One inline correction and one sweep straggler below.
Reviewed changes — the delta since the prior pullfrog review at 4deb76db, i.e. commit 14b6ed02.
- Corrected the
jitiownership claim in both places —docs/quickstart.mdx:120anddocs/reference/project.mdx:26now sayinitreminds you to install it rather than installs it. This matches the code:jitiis indepsfor every framework plan (init-templates.ts:145/244/334/439), soinit.ts:342-344does print "Ensure these are installed: … jiti", whileinit.ts:321-326still patches onlyscripts["docs:generate"]. - Added the missing
typeTableBasePathto the c15tgitSource()snippet —docs/pipeline/configure-sources.mdx:289now carries the top-leveltypeTableBasePath: "content-fixtures/c15t"with its clone-root comment, matchingapps/c15t-example/leadtype.config.ts:69field-for-field. Copied as written, the type tables thesparse: ["docs", "packages"]comment promises now actually resolve. - Extended the
baseUrlsweep tolocalize-docs— thedefineDocsConfigexample gainedbaseUrl, and itsleadtype generatecommand dropped--base-url, bringing the page in line with quickstart and configure-sources. - Reworded the
resolveProject()scope claim — no longer asserts flatly that it is "the one pipeline behindgenerate"; see the inline note, the replacement overshoots in the other direction.
ℹ️ The --base-url sweep stops short of a page this PR edits
docs/pipeline/sync-docs-across-repos.mdx:86 still reads "docs:generate": "leadtype generate --src . --out public --base-url https://docs.example.com --sync" — the same docs:generate script that build-a-docs-site.mdx:81 just had --base-url removed from, describing the same pinned-source production shape. Nothing breaks (the flag is still a valid override), but two pages this PR touches now tell different stories about where the base URL lives, which is the exact drift the PR exists to close.
Technical details
# `--base-url` still restated in a `docs:generate` script on a page this PR edits
## Affected sites
- `docs/pipeline/sync-docs-across-repos.mdx:86` — `docs:generate` script keeps `--base-url https://docs.example.com`, while the parallel script at `docs/pipeline/build-a-docs-site.mdx:81` was changed to `leadtype generate --src . --out public --sync` in this PR. Both pages document the pinned remote-collection shape.
## Required outcome
Every `leadtype generate` invocation on a page this PR touches reads its base URL from the config, matching the story quickstart, configure-sources, localize-docs and build-a-docs-site now tell.
## Open questions for the human
The same straggler exists on pages outside this PR's stated scope — `deploy-generated-artifacts.mdx:36`, `generate-static-artifacts.mdx:51/91/104/148/158`, `search/add-search.mdx:21`, `reference/openapi.mdx:52`, `generate-rss-atom-feeds.mdx:51`, `aeo/optimize-docs-for-agents.mdx:41`. Worth deciding whether they are a follow-up PR or belong here, since a reader who lands on any of them gets the pre-#182 story.Claude Opus | 𝕏
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 29ca04e111
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
Important
The new multi-collection generateStaticParams snippet emits params that include the route prefix, so under the catch-all location every other Next example in these docs uses it renders /docs/docs/quickstart. The paragraph's diagnosis is right; the one-liner it prescribes is not usable as written.
Reviewed changes — the delta since the prior pullfrog review at 14b6ed02, i.e. commit 29ca04e1, which touches docs/reference/project.mdx and its paths.lock.json hash only.
- Narrowed the
resolveProject()/generateclaim — the sentence now saysgenerate"performs the same resolution steps" and the unlanded sync-consolidation reference is gone. That matches the branch:generate.tscomposesconfig/load+inherit+normalize+inferfrom the same modules without importingresolveProject. This retires the one open thread from the last review. - Rewrote the
configPath/configDiroption rows — now documents that the two are alternatives and thatconfigDirwins as the project root when both are passed. Traced and accurate:createDocsProjectmapsconfigDirontoresolveProject'scwd(project/index.ts:243),cwdbeats the config-path-derived root (config/project.ts:309-311), and the fallback content root becomes<configDir>/docs(config/project.ts:316,457-461) — whilenormalizeDocsConfigstill receivesconfigDir: path.dirname(options.configPath)(config/project.ts:325-327), so relative paths do stay anchored toconfigPath's directory. - Added multi-collection static-params guidance to "What it returns" — a new paragraph plus snippet steering readers away from
createGenerateStaticParamsfor projects with several route prefixes. See the inline comment.
The facts behind the new paragraph all check out: createGenerateStaticParams maps page.slug verbatim (next/index.ts:321), listPages() spreads each source's page through unchanged (project/index.ts:376), and slug is derived from the relative path with no mount applied while urlPath is mount-aware (source/index.ts:300-309) — so two collection index pages really do both yield { slug: [] }. It is only the prescribed replacement that doesn't land.
Claude Opus | 𝕏
There was a problem hiding this comment.
Important
The prior thread is genuinely fixed — both layouts are now correct and the route topology is stated. But the "single-collection projects are fine" carve-out that frames them is still too broad: a single collection with mounts fails identically, and that is the shape this repo's own docs/docs.config.ts uses.
Reviewed changes — the delta since the prior pullfrog review at 29ca04e1, i.e. commit 7456896f, which touches docs/reference/project.mdx, docs/pipeline/use-the-source-primitive.mdx, docs/pipeline/configure-sources.mdx, and the paths.lock.json hashes.
- Replaced the multi-collection static-params snippet with two named layouts — a per-collection catch-all fed by
project.getSource(key), and a merged site-root catch-all derived fromurlPath, each with its route location stated in the fence comment. The follow-up paragraph now spells out the prefixed-route misrouting (/docs/changelog/1-0) and the optional-catch-all collision with a staticapp/page.tsx. This resolves the thread from the last review. - Widened the adapter enumeration — the paragraph now names all five static-params helpers rather than Next's alone. Verified:
createGenerateStaticParams(next/index.ts:316-323) mapspage.slugdirectly, andcreateGetStaticPaths,createPrerenderRoutes,createEntries,createStaticParamsall route throughlistJoinedSlugs(internal/framework.ts:77-82), which does the same.leadtype/fumadocsbuilds a synchronousfileslist rather than a route-enumeration helper, so its absence from the list is correct. - Documented
resolveIncludeas the exception to the merged view — accurate:project/index.ts:489-500binds it togetSource(project.collections[0]?.key), andsource/index.ts:798-809defaultsfromDirto that collection's own content root.config/project.ts:371iteratesObject.entries(declared), so "first-declared" holds. - Qualified the adapter claim in
use-the-source-primitive.mdxand pointed it at the reference — the second site the last review asked to reach. - Pinned
baseUrlownership to the docs UI repo inconfigure-sources.mdx— the ownership bullet now names it and the multi-repo example carriesbaseUrl: "https://acme.dev". Both claims check out:config/inherit.ts:44-57has nobaseUrlin the inherit whitelist (andSourceOwnedConfigFieldsdoesn't declare one), and the fallback chain ininternal/docs-url.ts:179-207really does terminate athttp://localhost:<PORT>after the deployment-URL env vars.
Claude Opus | 𝕏
31fa731 to
e5857db
Compare
7456896 to
6c680f1
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 6c680f1bda
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
e5857db to
f100b97
Compare
6c680f1 to
5d1a2ab
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 5d1a2ab615
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
The quickstart taught createDocsSource({ contentDir, baseUrl }) while
claiming to show the same wiring init generates — but init scaffolds a
zero-arg createDocsProject() reading everything from the config. The
same drift ran through the path-picker table (raw createDocsSource
labelled "most cases", the project never mentioned), the source
reference leading with legacy groups, and snippets restating baseUrl,
contentDir, or defineDocsConfig in root leadtype.config contexts.
Now every page tells the init story: config owns identity, baseUrl,
and structure; createDocsProject() is the documented runtime entry
(with a new reference page covering discovery, baseUrl precedence,
the diagnostics-vs-throws contract, ProjectDiagnostic, and
navigationOrigin); createDocsSource() is the labelled escape hatch;
and gitSource() collections replace repeated --docs-dir flags as the
multi-folder answer. Legacy shapes stay documented — groups under an
explicit legacy callout, --docs-dir as a labelled legacy note — they
just stop appearing as the recommended path.
…s to traced behavior
… resolveInclude ownership
The adapter maps collection-local slugs and reads meta.json from the primary contentDir, so a merged multi-collection project is not a routable Fumadocs source.
The throw names project.rootDir and the two filename globs, not the resolved search paths.
There was a problem hiding this comment.
Important
The new Fumadocs scoping is a real correction — the merged-project failure mode is stated accurately and the per-collection recipe is right. But the single-collection carve-out that replaces it is still one condition short: a single collection with mounts fails the same way, and that is the shape apps/fumadocs-example ships.
Reviewed changes — the delta since the prior pullfrog review at 7456896f. A git range-diff against 6c680f1 shows commits 2–4 unchanged and commit 1 replayed onto a new base tip with no content delta of its own, so the reviewable surface is one commit, 5d1a2ab "Scope fumadocsSource to one collection at a time" (3 files, +20/−4). Everything else in the 18-file diff is base-branch absorption from dx/base-url-config.
- Scoped the Fumadocs project wiring to a single collection —
integrate-with-fumadocs.mdx:25now says a config-described project is aDocsSource"for a single-collection project", and the new paragraph at:38explains why a merged project isn't:fumadocsSource()maps each page to its collection-localslugandrelativePath. Verified againstfumadocs/index.ts:122-132, which buildsslugs: meta.slugverbatim, and:134, which readsmeta.jsononly fromleadtype.contentDir— whichproject/index.ts:520-523binds tocollections[0]. The prefix-drop and index-page-collision claims both follow. - Added a per-collection recipe —
lib/changelog-source.tsfeedingproject.getSource("changelog")into its ownfumadocsSource()/loader({ baseUrl: "/changelog" }). Correct, andgetSourceis genuinely synchronous (project/index.ts:380-388), so the un-awaited call in the snippet is right. One nit on the fence directive, inline. - Carved
leadtype/fumadocsout of the adapter enumeration inbuild-a-docs-site.mdx:100— accurate that it is not interchangeable with the five static-params adapters, since it builds a synchronousfileslist rather than enumerating routes. It carries the same over-broad single-collection clause, covered inline. - Refreshed the two
docs/paths.lock.jsonhashes for the touched pages. No path changes.
I ran the docs lint against the locally built CLI to check the new snippets: node packages/leadtype/dist/cli.js lint docs --error-unknown --max-warnings 0 → "All 54 files pass."
Claude Opus | 𝕏
| ``` | ||
|
|
||
| If your docs are described by a Leadtype config, pass a project instead of a content directory. The adapter accepts any `DocsSource`, and a project is one — so the content root, navigation, mounts, and OpenAPI overlay come from the config rather than being restated here: | ||
| If your docs are described by a Leadtype config, pass a `DocsSource` instead of a content directory. A single-collection project is one — so the content root, navigation, mounts, and OpenAPI overlay come from the config rather than being restated here: |
There was a problem hiding this comment.
mounts don't survive this path, so the single-collection carve-out is too broad. fumadocsSource() hands fumadocs slugs: meta.slug (fumadocs/index.ts:122-132), and slug is derived from the relative path with no mount applied (source/index.ts:305) while urlPath is mount-aware (source/index.ts:306-314). A single collection with mounts: [{ pathPrefix: "changelog", urlPrefix: "/changelog" }] therefore yields ['changelog','1-0'] under loader({ baseUrl: "/docs" }) — served at /docs/changelog/1-0, while the generated sitemap and llms.txt advertise /changelog/1-0. That is docs/docs.config.ts:289 and apps/fumadocs-example/lib/source.ts, which is why apps/fumadocs-example/app/changelog/[[...slug]]/page.tsx:72-77 hand-writes generateStaticParams instead of using the wiring this page describes.
Technical details
# The single-collection carve-out omits `mounts` on the Fumadocs surface
## Affected sites
- `docs/integrations/integrate-with-fumadocs.mdx:25` — "A single-collection project is one — so the content root, navigation, `mounts`, and OpenAPI overlay come from the config rather than being restated here". `mounts` is named explicitly as something that carries over, which is the inverse of what happens.
- `docs/pipeline/build-a-docs-site.mdx:100` — "Pass `project.getSource(key)` into one `fumadocsSource()` / `loader()` per collection, or use it with a single-collection project." Same condition, same gap.
## Evidence
- `packages/leadtype/src/fumadocs/index.ts:122-132` builds each page file as `slugs: meta.slug`. Nothing mount-aware reaches fumadocs.
- `packages/leadtype/src/source/index.ts:305` derives `slug` from `outputRelativePath` with no mounts argument; `:306-314` computes `urlPath` through `toDocsUrlPath(relativePath, mounts)`. A mount splits the two exactly the way a `routePrefix` does.
- `docs/docs.config.ts:289` — this repo's own single-source config carries `mounts: [{ pathPrefix: "changelog", urlPrefix: "/changelog" }]`.
- `apps/fumadocs-example/lib/source.ts` is the wiring at `:27-36` verbatim (`fumadocsSource({ source: project })` → `loader({ baseUrl: "/docs" })`), and `apps/fumadocs-example/app/changelog/[[...slug]]/page.tsx:72-77` filters `page.urlPath.startsWith("/changelog/")` and emits `page.slug.slice(1)` by hand — the concrete cost of the gap.
- `docs/pipeline/configure-sources.mdx:17` lists "One docs folder with mounted subtrees" as a first-class shape, so this is a documented common case, not an edge.
- PR #184 (`740699f8`) does not close this: it adds `DocsSource.routePrefix` and reworks the five static-params adapters, but `packages/leadtype/src/fumadocs/index.ts` is not among its 13 files and the `meta.slug` derivation in `source/index.ts` is untouched. The deferral that resolved the earlier `use-the-source-primitive.mdx` thread doesn't extend to this surface.
## Required outcome
A reader with one collection can tell from the sentence whether their project is in the safe case. The condition is "a single-collection project whose pages' `urlPath` matches the loader's `baseUrl`" — i.e. no mounts moving a subtree off the route's own prefix — not "a single-collection project".
## Suggested approach (optional)
State the condition in terms of `urlPath` rather than collection count, and note that a mounted subtree needs its own `loader()` at the mount's `urlPrefix` the same way a second collection does. The per-collection recipe added just below generalizes to mounts with a `listPages()` filter on the prefix, which is what `apps/fumadocs-example` ships.| `fumadocsSource()` maps each page to its collection-local `slug` and `relativePath`, and reads `meta.json` only from that source's `contentDir`. A merged multi-collection project therefore drops every `routePrefix` and collides on identical local pages. Give each collection its own loader via `project.getSource(key)`: | ||
|
|
||
| ```ts title="lib/changelog-source.ts" | ||
| // @noErrors |
There was a problem hiding this comment.
nit: this // @noErrors isn't needed, and it opts a brand-new snippet out of the repo's own snippet:types check while the near-identical sibling at :27-36 stays checked. Deleting the line leaves the lint at "All 54 files pass", and a control substitution of project.getSource(123) does fail it — × [error snippet:types] ts snippet fails typechecking: Argument of type 'number' is not assignable to parameter of type 'string'. (line 46) — so the fence really is verified against the built leadtype types once the directive is gone.
5d1a2ab to
06e0291
Compare
There was a problem hiding this comment.
ℹ️ No new issues — the corrected missing-config sentence matches the throw the code actually produces. One wording nit inline.
Reviewed changes — the delta since the prior pullfrog review at 5d1a2ab: one commit, 06e0291 "Document the missing-config error createDocsProject actually throws", touching one sentence in docs/reference/project.mdx plus that page's paths.lock.json hash.
- Corrected the missing-config error description —
docs/reference/project.mdx:37no longer promises a list of searched paths. Traced end-to-end and accurate:config.missingis pushed atlevel: "warn"(config/project.ts:614-618), so the blockinglevel: "error"check atproject/index.ts:276-286never fires on this path and execution reaches:289-292, which throwsno docs config found from "${project.rootDir}". Pass `config`, or add a leadtype.config.* / docs.config.* file.— one root directory plus the two globs, exactly as the sentence now says. It holds on both branches into that throw: with no content roots at allresolveProjectreturnsemptyProject(rootDir, diagnostics)(config/project.ts:626-627), and with adocs/tree but no config fileresolveConfiglessProjectstill leavesconfignull — either waycreateDocsProjectlands on the same message. This closes the Codex thread the author replied to. - Refreshed the
/docs/reference/projectlockfile hash — the only other change in the delta; no path additions or removals.
Claude Opus | 𝕏
|
|
||
| The config file's location fixes the project root: a `docs.config.*` sits *inside* the docs directory, so the root is its parent; a `leadtype.config.*` sits at the root already. An explicit `configPath` follows the same basename rule. | ||
|
|
||
| If neither file exists, `createDocsProject` throws naming `project.rootDir` and the two filename globs (`leadtype.config.*` / `docs.config.*`); `resolveProject` reports a `config.missing` diagnostic whose fix is `leadtype init`. |
There was a problem hiding this comment.
nit: rootDir is a real field on ResolvedProject (config/project.ts:121), but it isn't part of what this page documents — the resolveProject() return snippet at :135-140 lists configPath, collections, sources, inference and diagnostics, and there's no rootDir row in either options table. A reader chasing project.rootDir finds no referent, while the error itself just interpolates the resolved project root, which reads plainer stated as such.
| If neither file exists, `createDocsProject` throws naming `project.rootDir` and the two filename globs (`leadtype.config.*` / `docs.config.*`); `resolveProject` reports a `config.missing` diagnostic whose fix is `leadtype init`. | |
| If neither file exists, `createDocsProject` throws naming the project root it searched from and the two filename globs (`leadtype.config.*` / `docs.config.*`); `resolveProject` reports a `config.missing` diagnostic whose fix is `leadtype init`. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 06e0291f56
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| export const leadtypeSource = fumaSource.leadtype; | ||
| ``` | ||
|
|
||
| `fumadocsSource()` maps each page to its collection-local `slug` and `relativePath`, and reads `meta.json` only from that source's `contentDir`. A merged multi-collection project therefore drops every `routePrefix` and collides on identical local pages. Give each collection its own loader via `project.getSource(key)`: |
There was a problem hiding this comment.
Include mounted routes in the Fumadocs limitation
The fresh case not covered by the multi-collection workaround is a single Fumadocs collection with mounts: for example, docs/changelog/v1.mdx mounted at /changelog is still mapped here to the collection-local slug changelog/v1, so loader({ baseUrl: "/docs" }) routes it as /docs/changelog/v1 while urlPath and generated artifacts advertise /changelog/v1. This contradicts the preceding promise that mounts come through for a single-collection project; document that this adapter does not preserve mounts or make its page mapping mount-aware.
Useful? React with 👍 / 👎.

Stacked on #182 → #167. The docs consistency pass from the roadmap audit (#157, Principle 2): existing configs keep working, and one canonical format is the documented path everywhere.
The contradiction
The quickstart's step 2 taught the manual source as
createDocsSource({ contentDir: "./docs", baseUrl: "https://example.com" })— while claiming the manual steps show "the same wiring"initgenerated. Butinitscaffoldslib/source.tsas a zero-argcreateDocsProject()that reads content root, navigation, and (since #182)baseUrlfrom the config. A reader following the manual path built a different, drift-prone integration from the one the scaffold gives them, on the page whose whole point is that they are the same thing.The same drift ran wider: the build-a-docs-site path picker labelled raw
createDocsSource()"Most cases" and never mentionedcreateDocsProjector the adapters; the source reference led with legacygroups:; several pages restated--base-url/contentDirin snippets the config now owns; and a few rootleadtype.config.tsexamples useddefineDocsConfigwhere the config-model ownership table saysdefineLeadtypeConfig.Page by page
baseUrl(the same two fieldsinitwrites); step 2 is the exact scaffolded zero-argcreateDocsProject()with the jiti note; step 3 uses theleadtype/nextadapter helpersinitwires; steps 4–5 drop the restated--base-url. The manual path now genuinely is the wiringinitgenerates.createDocsProject()andleadtype generate;createDocsSource()is the labelled escape hatch. Root config example switched todefineLeadtypeConfigwith a site-ownedbaseUrl; thedocs:generatescript drops--base-url.createDocsProject()andresolveProject(): zero-arg discovery rule (rootleadtype.config.*, elsedocs.config.*inside the docs dir, and the project-root-from-basename rule), the options tables,baseUrlprecedence (argument > config > deployment-URL env fallbacks), the diagnostics-vs-throws contract, theProjectDiagnosticshape with the stable ids, cache-only remote collections namingleadtype sync, andnavigationOrigin. Every claim checked againstconfig/project.ts/project/index.tson this branch — one correction from the audit note: there is no"mixed"origin value in the code; the type isexplicit | inherited | groups | inferred, and that is what the page documents. Registered in the reference nav indocs/docs.config.ts.createDocsProject(); lead example now uses canonicalnav;groupsmoved under an explicit Legacy callout;navadded to the options table.gitSource()+sparse+ nested collections shape (mirroringapps/c15t-example/leadtype.config.ts); the "multiple local folders" answer is now config collections, with repeated--docs-dirflags kept as a labelled legacy callout; root config examples switched todefineLeadtypeConfig; generate commands stop restating--base-url.createDocsProject()first, primitive as the fallback for content no config describes. The recently-added jiti note and all framework recipes kept.createDocsProject/resolveProjectas the primary runtime entry; "source primitive" redefined as the lower-level function the project is built on.initoffered up front as the scaffold for steps 2–4,doctoradded to the verify step; the config gainsbaseUrland the generate command stops restating it.createDocsProject()/defineLeadtypeConfig, canonicalroutePrefixinstead of legacyprefix), localize-docs (per-locale sources viacreateDocsProject({ locale })instead of re-handing the config back), sync-docs-across-repos and reference/markdown root configs todefineLeadtypeConfig, collections.mdx lead example likewise.What stayed documented as legacy, and why
Per Principle 2, nothing was deleted — legacy shapes are confined, not erased.
groupskeeps a full explanation under an explicit Legacy callout in the source reference (and its rename story stays in the config-model table); repeated--docs-dirflags keep a labelled legacy callout in configure-sources with their exact semantics; deprecated-name behavior (prefix,sourceConfig,schema) remains documented in concepts/config-model, untouched here. They just no longer appear anywhere as the recommended path.Deliberately untouched for conflict avoidance:
docs/reference/doctor.mdx(#181) anddocs/concepts/config-model.mdx/ the CLI flag rows ofdocs/reference/cli.mdx(#182, in this base).Verification
bun x leadtype lint docs --format github --error-unknown --max-warnings 0(the exact CI invocation): 54 files scanned, 0 errors, 0 warnings — including snippet typechecking against the builtdist/.bun x leadtype doctor --src . --docs-dir docs: exit 0.bun run --filter leadtype test: 843 pass (56 files);bun run --filter leadtype-evals test: 46 pass. The pre-commit hook re-ran the full suite: 889 pass.docs/paths.lock.jsonregenerated. Entries updated for every page this PR edits, plusintegrate-with-fumadocs: its entry was one of the three stale-at-Resolve the project once instead of in every command #167 hashes Make baseUrl a config field instead of a repeated knob #182 left alone, and the package test suite regenerates the lockfile against the current tree, so its refreshed hash is committed here — the other two stale entries (collections,use-the-source-primitive) belong to pages this PR edits anyway. The lockfile is now fully consistent with the tree.Need help on this PR? Tag
@codesmith-botwith what you need. Autofix is disabled.