Skip to content

fix(modules): warn on config.modules.* keys that match no registered module - #4529

Merged
PierreBrisorgueil merged 5 commits into
masterfrom
fix/4480-module-active-unknown-key-warn
Aug 3, 2026
Merged

fix(modules): warn on config.modules.* keys that match no registered module#4529
PierreBrisorgueil merged 5 commits into
masterfrom
fix/4480-module-active-unknown-key-warn

Conversation

@PierreBrisorgueil

@PierreBrisorgueil PierreBrisorgueil commented Aug 3, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • What changed: isModuleActive() fails open — any config.modules.{name} entry that isn't exactly { activated: false } resolves to active. Adds a dev-mode-only warnUnknownModuleKeys() that scans config.modules once at router boot and warns per offending key: a key that matches no registered module (wrong case / typo), or a key that matches a module but has no activated property (e.g. display set instead). No-op in production; isModuleActive itself is unchanged.
  • Why: a mis-cased key, typo'd module name, or wrong property silently leaves an optional module (and its routes) active with no signal the deactivation attempt did nothing.
  • Related issues: Closes 🐛 isModuleActive fails open — mis-cased/typo'd modules.* key silently keeps an optional module active + URL-reachable #4480

Chosen shape (a) only — dev-mode warn. Rejected: (b) flipping the tasks reference module's shipped default to inactive, and (c) fail-closed template routes — both change shipped defaults and would need downstream migration notes; no activation-semantics change was in scope.

Scope

  • Modules impacted: src/lib/helpers/modules.js (new warnUnknownModuleKeys), src/modules/app/app.router.js (one call site in getRouter())
  • Cross-module impact: noneisModuleActive behavior is byte-identical; this only adds a diagnostic
  • Risk level: low

Validation

  • npm run lint
  • npm run test:unit (150 files / 2547 tests green)
  • npm run build
  • Manual checks done — tests cover wrong-case key, wrong prop (display without activated), typo'd name, correct activated: false, production no-op, once-per-load, and the core-module exemption (activated is always inert on a core module, so it's skipped rather than mis-guided)

Guardrails check

  • No secrets or credentials introduced
  • No risky rename/move of core stack paths
  • Changes remain merge-friendly for downstream projects
  • Tests added or updated when behavior changed

Notes for reviewers

  • Security considerations: none — dev-mode-only console.warn, no-op in production, no change to route mounting or auth.
  • Mergeability considerations: none.
  • Follow-up tasks (optional): config.modules.{name} is a dual-purpose namespace (also read by useCoreStore.refreshNav for a per-route nav-display override) — both warning messages name that pattern explicitly so intentional display-only usage isn't misread as broken config, rather than trying to fully disambiguate it.

Summary by CodeRabbit

  • Bug Fixes
    • Added development-time warnings for unknown, misspelled, incorrectly cased, or malformed module configuration keys.
    • Added validation for optional, administrative, account, organization, and analytics module settings.
    • Warnings are suppressed in production and limited to one per module load.

…module

isModuleActive() fails open: any config.modules.{name} entry that isn't
exactly { activated: false } resolves to active. A mis-cased key, a
typo'd module name, or the wrong property (e.g. display instead of
activated) silently leaves the module - and its routes - active with no
signal that the deactivation attempt did nothing.

Adds a dev-mode-only warnUnknownModuleKeys() that scans config.modules
once at router boot and warns per offending key: unknown key (wrong
case/typo) or a known key with no activated property. No-op in
production, no activation-semantics change (isModuleActive is
untouched).

Rejected: flipping the tasks module's shipped default to inactive, and
fail-closed template routes - both change shipped defaults and would
need downstream migration notes.

Closes #4480
…arg build

- registeredModuleNames was built only from optionalModules, missing
  invitations (gated via adminChildModules/accountChildModules) — would
  have false-positive warned on a legitimately deactivated module. Now
  unions all four isModuleActive-gated registries.
- replace the manual hasWarned flag with lodash-es once() (already a
  repo dependency).
- pass registeredModuleNames as a thunk so the list is only built on
  the (single) call that actually runs, not on every getRouter() call.
- add a regression test asserting invitations is present in the names
  passed to warnUnknownModuleKeys.
…mistake

Address Phase-0 review findings on the config.modules.* warn:
- config.modules.{name} is dual-purpose — useCoreStore.refreshNav reads
  config.modules[routeName].display to hide a nav item without touching
  activation. Both warning messages now name that pattern explicitly so
  intentional display-only usage isn't misread as broken config.
- activated is always inert on a CORE module (isModuleActive hardcodes
  true regardless of config) — skip the "no activated property" check
  entirely for core module keys instead of telling the developer to add
  a property that would do nothing.
- document the nonRoutedModuleNames allowlist as intentionally narrow
  (stack-owned, not downstream-extensible) rather than a silent gap.
@coderabbitai

coderabbitai Bot commented Aug 3, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@PierreBrisorgueil, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 23 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 31313adb-4292-448e-b183-823108169a0b

📥 Commits

Reviewing files that changed from the base of the PR and between cbf4947 and 622a5d1.

📒 Files selected for processing (7)
  • MIGRATIONS.md
  • src/config/defaults/development.config.js
  • src/config/defaults/test.config.js
  • src/lib/helpers/modules.js
  • src/lib/helpers/tests/modules.unit.tests.js
  • src/modules/app/app.router.js
  • src/modules/app/tests/app.router.unit.tests.js

Walkthrough

The change adds development-time validation for module configuration keys. The app router supplies registered names from route-gated and configuration-only modules. Tests cover warning conditions, production suppression, scan deduplication, core modules, and router integration.

Changes

Module key validation

Layer / File(s) Summary
Module warning helper and coverage
src/lib/helpers/modules.js, src/lib/helpers/tests/modules.unit.tests.js
Adds warnUnknownModuleKeys, exposes it from the helper, and tests invalid keys, production suppression, duplicate prevention, core modules, missing configuration, and valid activation entries.
Router registry integration
src/modules/app/app.router.js, src/modules/app/tests/app.router.unit.tests.js
The router passes optional, admin-child, account-child, organization-child, and analytics module names to the warning helper. Router tests verify the call and reset the mock between tests.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant AppRouter
  participant ModuleWarningHelper
  participant ModuleConfig
  participant Console
  AppRouter->>ModuleWarningHelper: pass registered route and analytics module names
  ModuleWarningHelper->>ModuleConfig: inspect configured module keys
  ModuleWarningHelper->>Console: warn for unknown or malformed keys in development
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: warning for unmatched module configuration keys.
Description check ✅ Passed The description covers the required summary, scope, validation, guardrails, and reviewer notes with issue linkage.
Linked Issues check ✅ Passed The implementation satisfies issue #4480 by adding development-only warnings without changing activation semantics or shipped defaults.
Out of Scope Changes check ✅ Passed The changes remain focused on module configuration diagnostics, router integration, and related tests.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/4480-module-active-unknown-key-warn

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@codecov

codecov Bot commented Aug 3, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 99.57%. Comparing base (cbf4947) to head (622a5d1).

Additional details and impacted files
@@           Coverage Diff           @@
##           master    #4529   +/-   ##
=======================================
  Coverage   99.57%   99.57%           
=======================================
  Files          36       36           
  Lines        1411     1427   +16     
  Branches      439      448    +9     
=======================================
+ Hits         1405     1421   +16     
  Misses          6        6           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@PierreBrisorgueil
PierreBrisorgueil marked this pull request as ready for review August 3, 2026 10:29
@PierreBrisorgueil

Copy link
Copy Markdown
Collaborator Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Aug 3, 2026

Copy link
Copy Markdown
✅ Action performed

Full review finished.

@coderabbitai coderabbitai 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.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/modules/app/app.router.js`:
- Around line 131-139: Remove the hard-coded 'analytics' entry from
nonRoutedModuleNames in the core router. Add a registry or configuration seam
that optional modules use to register non-routed configuration names, and have
the router consume that registered set while preserving the existing dev-mode
unknown-module validation.
- Around line 143-149: Document each changed arrow function with a one-line
JSDoc description plus required `@param` and `@returns` tags: add the lazy
module-name provider documentation in src/modules/app/app.router.js lines
143-149, and document the mock delegation functions in
src/modules/app/tests/app.router.unit.tests.js lines 12-15, 73-76, and 641-644.
Describe each function’s inputs and returned iterable or delegated result.

In `@src/modules/app/tests/app.router.unit.tests.js`:
- Around line 606-614: Add an expectation for the configuration-only module name
`analytics` in the module-name assertions within the relevant router unit test,
alongside the existing optional module checks. Keep the regression guard
covering the child-module registry entries unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: b6635c96-63d0-4f0e-8109-bd949c86c177

📥 Commits

Reviewing files that changed from the base of the PR and between cbf4947 and 3406094.

📒 Files selected for processing (4)
  • src/lib/helpers/modules.js
  • src/lib/helpers/tests/modules.unit.tests.js
  • src/modules/app/app.router.js
  • src/modules/app/tests/app.router.unit.tests.js

Comment thread src/modules/app/app.router.js Outdated
Comment thread src/modules/app/app.router.js Outdated
Comment thread src/modules/app/tests/app.router.unit.tests.js
…sting it

CodeRabbit flagged app.router.js (core module) hardcoding the optional
module name 'analytics' in a nonRoutedModuleNames allowlist, violating
the "no optional module names in core module code" rule (CLAUDE.md).

config.modules.analytics.activated was never read anywhere in the
codebase (confirmed repo-wide) — vestigial config predating this PR.
Removing it from both default config files eliminates the false-
positive source at the root instead of special-casing it, so core
router code no longer references any optional module name at all.

Also names + documents the lazy registeredModuleNames() provider
(previously an anonymous inline arrow) per CodeRabbit review.
…-hide intent

Adversarial review before merge found the unknown-key warn treated
config.modules.* as a single module-name namespace, but
useCoreStore.refreshNav separately keys the same object by Vue Router
ROUTE name (PascalCase, e.g. 'Tasks') for nav-hiding — a legitimate,
unrelated pattern. Warning "typo" on every real display-only override
would have been a false positive on adoption.

Now discriminates by intent:
- entry has "activated" → activation intent → must match a registered
  MODULE name, else warn (still catches the original #4480 bug: a
  mis-cased/typo'd module name with activated:false).
- entry has no "activated" → nav-hide intent → must match a mounted
  ROUTE name (the same list refreshNav consults, threaded from
  app.router.js's getRouter()) OR a module name (coincidental-match
  case), else warn.

Also softens both messages to cover dead leftover config, not just
typos (downstream repos may still carry the vestigial `analytics` key
this PR removed stack-side), and updates MIGRATIONS.md.
@PierreBrisorgueil

Copy link
Copy Markdown
Collaborator Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Aug 3, 2026

Copy link
Copy Markdown
✅ Action performed

Full review finished.


Your included review limit is currently reached under our Fair Usage Limits Policy. This review may still proceed through usage-based billing if eligible. Your next included review will be available in 23 minutes.

@PierreBrisorgueil
PierreBrisorgueil merged commit 1ab89c4 into master Aug 3, 2026
7 checks passed
@PierreBrisorgueil
PierreBrisorgueil deleted the fix/4480-module-active-unknown-key-warn branch August 3, 2026 11:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

🐛 isModuleActive fails open — mis-cased/typo'd modules.* key silently keeps an optional module active + URL-reachable

1 participant