What β when two modules ship a guide with the same filename slug, the public docs API serves an arbitrary winner and the nav lists a duplicate entry. A consumer that adds its own 00-welcome or 01-quickstart collides with the sample guides shipped in modules/home, and has no way to resolve it from its own code.
Why β the module already detects the collision but the only remedy it offers is impossible to apply for the party who hits it. That pushes every consumer into a per-project config workaround, and that workaround does not load in the test environment, so it ends up patched in a shared test file instead.
Scope β modules/public/services/public.docs.service.js, modules/public/helpers/public.docs.tree.js.
Today compute() warns and picks the last writer:
if (bySlug.has(entry.slug)) {
logger.warn(`[public/docs] duplicate guide slug "${entry.slug}" β later guide wins; rename one to avoid the collision`);
}
bySlug.set(entry.slug, entry);
Two problems:
- "rename one" is not actionable for the colliding party. A consumer cannot rename a guide that ships with the framework. The only escape is
config.docs.excludeModules, which is discoverable solely by reading config/index.js.
- "later wins" is file-scan order, not a policy.
loadGuideEntries sorts by numeric prefix then slug, and buildDocsTree keeps BOTH entries β so /api/public/docs lists a duplicate while /api/public/docs/:slug.md returns whichever landed last. The two endpoints can disagree.
The knock-on effect is what makes this worth fixing rather than documenting: the config-level workaround is not loaded under NODE_ENV=test, so consumers compensate inside modules/public/tests/public.docs.integration.tests.js β a shared file. That is a local edit on framework-owned test code, which conflicts on every subsequent upstream sync.
Suggested direction (either, or both):
- Don't serve the sample guides by default. The guides under
modules/home are demo content; a real deployment does not want them in its public docs. Flipping the default to excluded, with explicit opt-in for the demo, removes the collision for everyone and needs no per-consumer config.
- Give the module a precedence policy. Carry the originating module on each entry and resolve collisions deterministically β e.g. application-level guides override framework-provided ones β so the outcome does not depend on scan order, and
bySlug and the tree always agree.
Whichever is chosen, buildDocsTree and bySlug should be made consistent so the listing and the fetch endpoint cannot disagree, and the resolution should be reachable without the consumer having to discover a config key.
What β when two modules ship a guide with the same filename slug, the public docs API serves an arbitrary winner and the nav lists a duplicate entry. A consumer that adds its own
00-welcomeor01-quickstartcollides with the sample guides shipped inmodules/home, and has no way to resolve it from its own code.Why β the module already detects the collision but the only remedy it offers is impossible to apply for the party who hits it. That pushes every consumer into a per-project config workaround, and that workaround does not load in the test environment, so it ends up patched in a shared test file instead.
Scope β
modules/public/services/public.docs.service.js,modules/public/helpers/public.docs.tree.js.Today
compute()warns and picks the last writer:Two problems:
config.docs.excludeModules, which is discoverable solely by readingconfig/index.js.loadGuideEntriessorts by numeric prefix then slug, andbuildDocsTreekeeps BOTH entries β so/api/public/docslists a duplicate while/api/public/docs/:slug.mdreturns whichever landed last. The two endpoints can disagree.The knock-on effect is what makes this worth fixing rather than documenting: the config-level workaround is not loaded under
NODE_ENV=test, so consumers compensate insidemodules/public/tests/public.docs.integration.tests.jsβ a shared file. That is a local edit on framework-owned test code, which conflicts on every subsequent upstream sync.Suggested direction (either, or both):
modules/homeare demo content; a real deployment does not want them in its public docs. Flipping the default to excluded, with explicit opt-in for the demo, removes the collision for everyone and needs no per-consumer config.bySlugand the tree always agree.Whichever is chosen,
buildDocsTreeandbySlugshould be made consistent so the listing and the fetch endpoint cannot disagree, and the resolution should be reachable without the consumer having to discover a config key.