Skip to content

πŸ”§ config.docs.excludeModules β€” decouple doc contribution from module runtime activationΒ #3923

Description

@PierreBrisorgueil

Problem

The public OpenAPI spec (GET /api/spec.json) and the public docs guide tree (GET /api/public/docs) are assembled by globbing every module's doc/*.yml + doc/guides/*.md (config/assets.js). That resolved file list is then filtered only by module runtime activation (config/index.js β†’ lib/helpers/config.js filterByActivation, which reads config[moduleName].activated).

Two coupling defects fall out of this:

  1. Activation conflates runtime with documentation. A module's activated flag gates BOTH its routes/models AND its doc contribution. There is no way to keep a module running while dropping only its sample docs.
  2. Core modules bypass the filter entirely. CORE_MODULES = { core, auth, users, home } are never filtered, because they carry infra-critical endpoints β€” home serves /api/health (liveness) and /api/admin/readiness. So a core module's sample doc/guide content (home/doc/guides/00-welcome.md β†’ slug welcome, 01-quickstart.md β†’ slug quickstart) is always globbed into the tree, with no opt-out.

Impact

A consumer that ships its own onboarding guides under the natural slugs welcome / quickstart collides with the home samples β†’ the docs tree builder rejects duplicate slugs. The only workaround is to physically delete the sample guides shipped by the framework β€” which conflicts with keeping framework files byte-identical across upgrades and so recurs on every sync.

(The sample OpenAPI doc tasks/doc/tasks.yml is not affected β€” tasks is non-core, so config.tasks.activated = false already strips it via the existing mechanism. This issue is specifically the core-module doc case that activation cannot reach.)

Proposed fix

Add an independent doc-exclusion config: config.docs.excludeModules: string[] (default []). A new helper filterByDocExclusion(files, config) drops doc/*.yml (spec) + doc/guides/*.md (guides) for any module in the list β€” regardless of activation / core status β€” applied as a second filter pass over the openapi + guides file keys, after filterByActivation. Default empty keeps the sample guides as a working tutorial.

This decouples documentation contribution from runtime activation (doc β‰  runtime), is granular per-module, and works for core and non-core modules alike.

Scope

  • lib/helpers/config.js: add filterByDocExclusion(files, config) (reuse extractModuleName; no CORE_MODULES bypass; missing/empty list β†’ no-op).
  • config/index.js: second filter pass over ['openapi', 'guides'] after the filterByActivation loop. Runtime file keys (routes/models/policies/configs/preRoutes) untouched.
  • config/defaults/development.config.js: docs.excludeModules: [] + comment (doc-only exclusion, independent of activation, works on core modules; default empty keeps samples).
  • Unit tests: excludes a listed module's doc/*.yml + doc/guides/*.md; keeps non-listed modules; works for a core module (the motivating regression); default [] / missing = no-op; runtime file keys unaffected by excludeModules.
  • MIGRATIONS.md entry: new config.docs.excludeModules knob + when a consumer would set it (a runtime-active module whose sample docs collide with the consumer's own).

Out of scope

  • tasks/doc/tasks.yml (already handled by config.tasks.activated = false).
  • Any tree-builder dedup/precedence change.

Created via /dev:issue

Metadata

Metadata

Labels

No labels
No labels

Type

No type

Fields

No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions