Tell one canonical wiring story across the docs - #183
Conversation
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.
|
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 | 𝕏
…s to traced behavior
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 | 𝕏
… resolveInclude ownership
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 | 𝕏

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.