Skip to content

Add Module Constitution and trim CLAUDE.md - #15

Merged
antosubash merged 14 commits into
mainfrom
claude/serene-wilson
Mar 27, 2026
Merged

antosubash merged 14 commits into
mainfrom
claude/serene-wilson

Conversation

@antosubash

Copy link
Copy Markdown
Owner

Summary

  • Add docs/CONSTITUTION.md — the authoritative rules document for module development and framework contribution, covering 12 sections: founding principles, module boundaries, dependencies, data ownership, communication, endpoints, frontend, permissions, settings, testing, compiler-enforced rules (28 SM diagnostics), and framework contributor guidelines
  • Trim CLAUDE.md — removed ~200 lines of architectural rules now covered by the Constitution, added a reference link, fixed incorrect "Inline dynamic imports" claim in Key Constraints
  • Designed for a small team with a single deployment unit and shared database — shared DB is framed as a deliberate design choice, not a limitation

Test plan

  • Verify docs/CONSTITUTION.md renders correctly on GitHub
  • Verify the docs/CONSTITUTION.md link in CLAUDE.md resolves correctly
  • Confirm no build/test regressions (documentation-only change)

🤖 Generated with Claude Code

@antosubash
antosubash force-pushed the claude/serene-wilson branch 2 times, most recently from d5894c8 to ef9b44f Compare March 27, 2026 21:19
Key findings: 3 modules violate contracts pattern by referencing
Users implementation directly, unified HostDbContext creates tight
database coupling, event bus lacks background dispatch, and Admin
module has mixed responsibilities.
1. Extract ApplicationUser/ApplicationRole to Users.Contracts
   - Moved Identity entities from SimpleModule.Users.Entities to
     SimpleModule.Users.Contracts namespace
   - Admin, OpenIddict, Permissions now reference Users.Contracts
     instead of Users implementation (fixes SM0011 violations)
   - Added GetRoleIdsByNamesAsync to IUserContracts to eliminate
     OpenIddict's direct UsersDbContext dependency
   - Updated all migration snapshots for new entity namespace

2. Add duplicate view page name detection (SM0015)
   - Source generator now detects when two IViewEndpoint classes
     produce the same page name, preventing silent client-side 404s

3. Add background event dispatch to IEventBus
   - New PublishInBackground<T> method for fire-and-forget events
   - BackgroundEventChannel + BackgroundEventDispatcher hosted service
   - Eliminates need for modules to reinvent the pattern (as AuditLogs did)
   - AuditingEventBus updated to forward background dispatch
The TypeScript page registry validation cannot be done at compile time
by the Roslyn generator (it has no access to .ts files). This check is
already handled by `npm run validate-pages`.
- Remove dead null check in EventBus.PublishInBackground (BackgroundEventChannel is now required)
- Make BackgroundEventDispatcher dispatch events concurrently to avoid head-of-line blocking
- Cache GetUserIdAsync result in AuthorizationEndpoint to avoid duplicate async call
- Use ICollection pattern check in UserService.GetRoleIdsByNamesAsync to avoid unnecessary ToList
- Remove unnecessary comments from BackgroundEventChannel
Aligns with the convention established by AuditWriterService. IServiceScopeFactory
is the recommended injection point for BackgroundService scope creation.
New compile-time diagnostics:
- SM0040 (Error): Duplicate module name — prevents route prefix conflicts,
  database schema collisions, and ambiguous TypeScript module grouping
- SM0041 (Warning): View page prefix mismatch — catches pages whose name
  doesn't start with the owning module's name (e.g. "Orders/Browse" in
  the Products module), which breaks React page resolution
- SM0042 (Error): Module with IViewEndpoint but no ViewPrefix — view
  endpoints without a ViewPrefix won't be routed correctly

Also adds tests for:
- SM0015 (duplicate view page name) — positive and negative cases
- SM0040 — two and three duplicate modules, plus valid unique names
- SM0041 — matching prefix passes, mismatch warns
- SM0042 — missing ViewPrefix errors, present ViewPrefix passes,
  API-only module without ViewPrefix passes
Admin, OpenIddict, and Permissions modules were migrated to reference
SimpleModule.Users.Contracts instead of SimpleModule.Users, so the
SM0011 (illegal implementation reference) suppression is no longer needed.
All constant classes in Contracts assemblies are `static class` (excluded
from DTO discovery). Permission classes are excluded via IModulePermissions
check. Vogen value objects are excluded via IsVogenValueObject check. No
DbContext types exist in any Contracts assembly. The suppression was
preemptive and never needed.
Framework core:
- Add Result<T> type, ModuleHealthStatus enum, and module lifecycle hooks
  (OnStartAsync/OnStopAsync/CheckHealthAsync on IModule)
- Add ModuleLifecycleHostedService to invoke lifecycle hooks at startup/shutdown
- Add IModuleDbContext marker interface for future per-module DB isolation
- Add PermissionMatcher with wildcard support and use in authorization handler
- Add [ViewPage] attribute for compile-time page name validation

Source generator:
- Add SM0040-SM0042 diagnostics (empty modules, missing contracts, missing ViewPrefix)
- Fix SM0043 to not flag modules with DbContexts as empty
- Fix endpoint-to-module matching to use namespace-based discovery
- Scan contracts assemblies for permission classes (fixes SM0035 false positives)
- Register IModule singletons and lifecycle service in generated AddModules()

Module decoupling:
- Decouple Orders.Contracts from Vogen types (UserId→string, ProductId→int)
- Extract IUserAdminContracts/IRoleAdminContracts from Users module
- Refactor Admin module to use contracts instead of direct UserManager/RoleManager
- Add Dashboard.Contracts project with IDashboardContracts placeholder

Route fixes:
- Add ViewPrefix to Admin, Dashboard, OpenIddict, PageBuilder, Users modules
- Fix route doubling by making all view endpoint routes relative to ViewPrefix
- Fix SM0015 duplicate page name (PageBuilder/ViewerDraft)

Tests updated across all affected modules and framework.
- Differentiate SM0031 vs SM0034 diagnostic descriptions
- Add PublishInBackground shutdown/cancellation behavior note
- Expand Settings Types to a proper table with descriptions
- Clarify permission constants assembly placement
- Improve SM0043 description accuracy
The extract-ts-types.mjs script was writing types.ts to the old
module paths (e.g., modules/Admin/src/Admin/) instead of the renamed
paths (modules/Admin/src/SimpleModule.Admin/). Updated the script to
prefix "SimpleModule." to the module directory name and removed the
9 stale type files in the old locations. Also fixed a Biome formatting
issue in PageBuilder Manage.tsx.
@antosubash
antosubash force-pushed the claude/serene-wilson branch from ef9b44f to f078c5f Compare March 27, 2026 21:20
@antosubash
antosubash merged commit f9a01ec into main Mar 27, 2026
@antosubash
antosubash deleted the claude/serene-wilson branch March 27, 2026 21:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant