Version 0
Host App Anatomy
Where MortelOS host app behavior belongs on disk.
mortelos/starter starts as a runnable Laravel host. Portal work grows it by adding host-owned actions, aggregates, events, projectors, resolvers, policies and package decisions.
Boot baseline
| Path | Ships | Purpose |
|---|---|---|
app/Actions/Auth/ResolvePostLoginRedirect.php |
Yes | Returns /dashboard by default. |
app/Actions/Invitations/ |
Yes | InviteUser, RevokeInvitation and AcceptInvitation: the invitation writes, each returning an ActionResult. |
app/Aggregates/InvitationAggregate.php |
Yes | Holds the invitation guards and records the events. |
app/Events/Invitations/ |
Yes | InvitationCreated, InvitationRevoked and InvitationAccepted, each with a *.v1.schema.json next to it. |
app/Projectors/InvitationProjector.php |
Yes | The only writer of the invitations table. |
app/Models/Invitation.php |
Yes | Read model of an invitation; change it through the Actions, never on the model. |
app/Access/StarterGovernanceGate.php |
Yes | Deny-by-default gate over role and policy data; SCOPES lists the policy scope of each host ability. |
app/Console/Commands/StarterDoctor.php |
Yes | Checks boot baseline wiring. |
app/Http/Controllers/Auth/PasswordLoginController.php |
Yes | Working email and password login. |
app/Http/Controllers/Auth/PasskeyAuthenticatedController.php |
Yes | Replace when passkeys are specified. |
app/Http/Controllers/Auth/AcceptInvitationController.php |
Yes | Serves /invite/{token}: shows the form, then calls AcceptInvitation and logs the new user in. |
app/Support/StarterUsersResolver.php |
Yes | Lists members of the configured tenant and the open invitations. |
app/Support/StarterUsersAccessResolver.php |
Yes | Guards user inspection actions. |
app/Support/StarterTenantInvitationService.php |
Yes | Implements the framework TenantInvitationService contract by calling InviteUser. |
config/starter.php |
Yes | Host contract surface with safe defaults. |
config/mortel.php |
Yes | Host overrides for the framework; registers App\Events\ as an event schema namespace. |
config/spine.php |
Yes | Governed models and the classes allowed to write them. |
config/event-sourcing.php |
Yes | event_class_map for host events and the framework projector list. |
config/app-standards.php |
Optional | Published overrides for mortelos/app-standards. |
routes/starter.php |
Yes | Starter route bridge, with Route::livewire('/users', 'pages::users.index') style routes. |
routes/web.php |
Yes | Requires routes/starter.php. |
tests/Feature/BootSmokeTest.php |
Yes | Confirms login and dashboard baseline. |
tests/Feature/ConfigShapeTest.php |
Yes | Confirms required config shape. |
tests/Feature/ArchGuardTest.php |
Yes | Fails on writes outside Actions, events outside aggregates and Livewire outside the Livewire 4 paths. |
tests/Feature/InvitationsTest.php |
Yes | Covers the invitation Actions, the undo and the projection replay. |
What lives where
| Concern | Lives in | Notes |
|---|---|---|
| Layout shell | resources/views/layouts/app.blade.php and guest.blade.php |
Used as layouts::app and layouts::guest. Keep the starter layout instead of replacing it. |
| Pages | resources/views/pages/<domain>/⚡<name>.blade.php |
Routed as pages::<domain>.<name>. |
| Auth pages | resources/views/pages/auth/⚡login.blade.php |
Livewire 4 single-file component, routed as pages::auth.login. |
| Auth logic | app/Http/Controllers/Auth/ |
Password login and invitation acceptance work; replace the passkey stub when it is specified. |
| Post-login redirect | app/Actions/Auth/ResolvePostLoginRedirect.php |
Make role-aware per portal when needed. |
| Tenant resolution | app/Support/SingleTenantResolver.php |
The starter resolves one tenant; the host picks its own tenancy strategy and rebinds TenantResolver. |
| Navigation tree | Host-owned resolver class | Bind through navigation.sidebar_resolver in config/starter.php; null by default. |
| Universal search | Host-owned resolver class | Set layout.universal_search_component to shared.universal-search and bind the resolver through navigation.universal_search_resolver in config/starter.php; both null by default. Troubleshooting lists the resolver methods. |
| Governance access | app/Access/StarterGovernanceGate.php |
Bound to the GovernanceGate contract in app/Providers/StarterServiceProvider.php. |
| User management | app/Support/StarterUsersResolver.php |
Bind through users.resolver; scope reads and inspection to explicit tenant_user membership. |
| Dashboard widgets | resources/views/components/<group>/⚡<name>.blade.php or a package |
Decide with package governance. |
| Policy abilities | StarterGovernanceGate::SCOPES in app/Access/StarterGovernanceGate.php |
Host abilities use PolicyScope::Action. Policies are framework rows created through CreatePolicy; deny by default. |
| Host facts | app/Aggregates/, app/Events/<Domain>/, app/Projectors/ |
A fact the host must audit or undo: aggregate, stored event, projector. See TALL conventions. |
| Portal docs | docs/portals/<slug>/ |
Capability map, build plan and progress. |
| Package decisions | .mortelos/package-decisions.md |
One entry per surface or package boundary. |
| Agent rules | AGENTS.md |
Host-specific prose plus generated package rules from mortelos/agent-standards and package-owned rules. |
| MCP mount | routes/ai.php |
Host mounts the framework MCP server when operate mode is enabled. |
Extend a package from a host service provider through its declared outlets: a contract, registry, event, config file or view outlet. Run php artisan mortelos:outlets:list <package> to find them; Outlets explains how to use them and when to open a package pull request. OutletsGuard checks that your app uses these routes instead of patching or copying package code.
Expected portal slice output
A complete first vertical slice usually adds:
docs/portals/<slug>/capability-map.md.- A package decision in
.mortelos/package-decisions.md. - Actions for write paths, each returning an
ActionResult. - Aggregates and stored events for auditable facts.
- A projector and read model for the surface.
- A surface such as dashboard widget, package route, page widget, chat widget or inbox item.
- Deny-by-default policy abilities.
- Focused Pest feature tests.
- Verification evidence from doctor, Pest and manual smoke.
Test account
The template seeds one local account:
| Role | Password | |
|---|---|---|
| Admin | admin@example.test |
password |
Add account-manager, customer or reviewer accounts in the host seeder only after the portal's role model is specified.