Applied Module 12 · AI-Accelerated Government Development

Register a Module Everywhere

What you'll learn

~25 min
  • Identify the 5 registration points every new module must touch
  • Use a single prompt to update all 5 files consistently
  • Diagnose a silently broken module by checking registration completeness

The registration problem

You scaffolded 7 files in the previous lesson. The table migration is ready, the API route compiles, the page renders in isolation. You deploy to the dev environment, click the nav link — and nothing happens. Or worse: the page loads but every API call returns 403. Or the module appears for admins but not for the operational role that actually needs it.

The module exists but the platform does not know it exists. That is the registration problem.

Every new module must be registered in 5 places. Miss one and the failure mode is silent — no error in the console, no build failure, no test red. Just a feature that does not work for the people who need it.


The 5 registration points

1. RBAC types file (src/types/rbac.ts)

Add the module’s permission key to the ModulePermission union type:

// Before
type ModulePermission = 'dashboard' | 'user-admin' | 'case-tracker' | ...;
// After
type ModulePermission = 'dashboard' | 'user-admin' | 'case-tracker' | 'vehicle-fleet' | ...;

If the module name is not in the union, withPermission('vehicle-fleet', method) in your API route will reject every request with a type error at build time — or silently return 403 at runtime if the types are not strict enough.

2. Permission matrix (src/config/permissions.ts)

Map each role to the CRUD operations it can perform on the new module:

'vehicle-fleet': {
admin: ['read', 'create', 'update', 'delete'],
manager: ['read', 'create', 'update'],
superadmin: ['read', 'create', 'update', 'delete'],
operator: ['read', 'create', 'update'],
viewer: ['read'],
}

This is where you decide who can do what. A missing entry means the role gets zero permissions on the module — withPermission() denies everything.

3. Nav routes config (src/config/nav-routes.ts)

Add the module to the sidebar navigation:

{
label: 'Vehicle Fleet',
path: '/vehicle-fleet',
icon: 'DirectionsCar',
requiredPermission: 'vehicle-fleet:read',
group: 'operations',
}

If you forget this, the page is accessible by direct URL but invisible in the sidebar. Users will never find it.

4. Provenance map (src/config/provenance-map.ts)

Register which tables the module owns:

'vehicle-fleet': {
tables: ['vehicle_fleet'],
dataSourceDefault: 'manual',
}

The provenance system uses this to track data lineage. Without it, the vehicle_fleet table is orphaned — no module claims ownership, and data audits cannot trace who populated it or how.

5. Module status enum (src/config/module-status.ts)

Add the module to the status registry:

'vehicle-fleet': {
status: 'development', // 'development' | 'staging' | 'production' | 'deprecated'
version: '1.0.0',
owner: 'your-team-name',
}

This controls feature flags and the admin module dashboard. A module in 'development' status is hidden from production users unless they have the admin role.

The silent failure modes

Here is what happens when you miss each registration point:

  • RBAC types: build error or runtime 403 on every API call
  • Permission matrix: 403 for all roles — even admin
  • Nav routes: module works but is invisible in navigation
  • Provenance map: data audit reports the table as unowned
  • Module status: module may be hidden in production or visible before it is ready

The prompt

This prompt updates all 5 registration points for the vehicle-fleet module you scaffolded in Lesson 5:

Register the "vehicle-fleet" module in the DS platform. Update these 5 files:
1. src/types/rbac.ts
- Add 'vehicle-fleet' to the ModulePermission union type
- Do NOT remove any existing entries
2. src/config/permissions.ts
- Add vehicle-fleet permissions:
superadmin: ['read', 'create', 'update', 'delete']
admin: ['read', 'create', 'update', 'delete']
manager: ['read', 'create', 'update']
operator: ['read', 'create', 'update']
viewer: ['read']
3. src/config/nav-routes.ts
- Add to the 'operations' group:
label: 'Vehicle Fleet'
path: '/vehicle-fleet'
icon: 'DirectionsCar'
requiredPermission: 'vehicle-fleet:read'
- Place it after the last existing operations item
4. src/config/provenance-map.ts
- Add: 'vehicle-fleet': { tables: ['vehicle_fleet'], dataSourceDefault: 'manual' }
5. src/config/module-status.ts
- Add: 'vehicle-fleet': { status: 'development', version: '1.0.0', owner: 'ds-core' }
IMPORTANT:
- Do NOT modify any existing entries in any file
- Only ADD the new vehicle-fleet entries
- Preserve the existing formatting and sort order
- Show me the diff for each file so I can review before committing
💡Why 'Do NOT remove any existing entries'

AI tools sometimes “clean up” files they are editing — removing comments, reordering imports, or consolidating entries. In a shared config file, that creates merge conflicts and breaks other modules. The explicit instruction to only add prevents this.


Watch it work

Claude Code — Registering module in 5 files
/home/user $ claude
/home/user $

The registration audit

After the AI updates the files, verify registration completeness. This is a manual check you do once — but it catches the bugs that automated tests miss.

Quick verification script you can run in your AI CLI:

Search the codebase for every occurrence of 'vehicle-fleet' and confirm it
appears in exactly these files:
- src/types/rbac.ts (in the ModulePermission type)
- src/config/permissions.ts (permission mapping)
- src/config/nav-routes.ts (nav entry)
- src/config/provenance-map.ts (table ownership)
- src/config/module-status.ts (status entry)
- src/app/api/vehicle-fleet/route.ts (API route)
- src/hooks/useVehicleFleet.ts (SWR hook)
- src/app/(protected)/vehicle-fleet/page.tsx (page)
- src/types/vehicle-fleet.ts (Zod schemas)
- src/tests/vehicle-fleet.test.ts (tests)
If 'vehicle-fleet' is missing from any of the first 5 files, that is a
registration gap. Report which files are missing.
Why not automate this?

You could write a script that checks registration completeness. Some teams do. But on a platform where new modules ship quarterly, a 30-second manual check after the AI runs is pragmatic. The cost of the script exceeds the cost of the check for the volume of new modules you actually ship.


Common registration mistakes

MistakeSymptomFix
Module name typo in one file403 on API calls even though nav link worksSearch for the module name across all 5 files. One of them has a different spelling.
Permission matrix has role but no operationsUser sees the nav link, clicks it, gets “Access Denied” on data loadCheck that the role has at least ['read'] in the permission matrix. An empty array means no access.
Nav route uses wrong requiredPermissionLink visible to wrong roles, or hidden from correct rolesThe requiredPermission must match the format 'module-name:operation'. Confirm it matches the RBAC types.
Provenance map points to wrong table nameData audit reports an orphaned tableThe table name in the provenance map must match the actual SQL table name in the migration (snake_case).
Module status set to 'production' prematurelyUnfinished module visible to all usersSet status to 'development' until the module passes QA. Only promote to 'production' when it is ready.

KNOWLEDGE CHECK

You register a new module in all 5 files and deploy. Users with the 'operator' role can see the module in the sidebar and load the page, but clicking 'Add New' returns a 403 error. Users with the 'manager' role can add records without issue. What is the most likely cause?


What’s next

Your module is scaffolded and registered. In the next lesson, you will build out the API route handler with the platform’s response helpers, safe error handling, pagination, and Zod validation — the patterns that make every route consistent and auditable.