LST-67 (2.5) front. Completes the Mail module.
- New frontend/modules/mail/ layer (auto-detected): /mail page (3 columns),
7 components, mail service + DTO, mail store (folders/messages/unread polling).
- sanitizeMailHtml util and useSystemFolderLabel composable stay global;
AdminMailTab stays in /admin (service import repointed).
- Consumers repointed: AdminMailTab and PM TaskModal -> ~/modules/mail/...;
the store is auto-imported (Pinia storesDirs) so the layout badge/polling is
unchanged.
- /mail gated by the mail module: sidebar.php item with module=mail (so
SidebarFilter disables /mail when the module is off); the layout filters /mail
from the API sections to avoid a visual duplicate. ROLE_CLIENT exclusion kept.
- i18n key sidebar.general.mail added.
nuxt build passes; /mail and all other routes preserved.
LST-58 (2.4) front. Completes the Directory module.
- New frontend/modules/directory/ layer (auto-detected): /directory page with
Clients and Prospects tabs.
- Client front moved into the layer (clients service + client DTO +
ClientDrawer). New prospects service, prospect DTO and ProspectDrawer (with
a "Convert to client" action calling POST /prospects/{id}/convert).
- Consumers repointed to ~/modules/directory/... (admin client tab, PM project
drawer + project pages + project DTO, time-tracking page + export drawer).
- Sidebar admin item /directory gated by the directory module; /directory
protected by the admin middleware. i18n keys added (directory.*, prospects.*).
nuxt build passes; routes preserved.
Adds the 2.4 plan doc.
LST-58 (2.4), part 1/2 — Client move. Prospect + repertoire front are pending
the product spec and will be added on this branch afterward.
- Client entity moved to src/Module/Directory/Domain/Entity; repository split
into Domain/Repository/ClientRepositoryInterface + Doctrine impl (bound in
services.yaml). 5 client MCP tools moved to Infrastructure/Mcp/Tool, now
injecting the interface.
- resolve_target_entities ClientInterface repointed to Directory\Client;
Directory mapping added; DirectoryModule registered (id directory, 2 RBAC
perms). Client.projects relation now uses ProjectInterface -> Directory no
longer depends on ProjectManagement.
- ProjectManagement Create/UpdateProjectTool inject Directory's
ClientRepositoryInterface; Serializer and fixtures repointed.
- Garde-fous: #[Auditable] + Timestampable/Blamable on Client (additive
migration: created_at/updated_at + created_by/updated_by FK ON DELETE SET
NULL + COMMENT).
161 tests green, mapping valid, no API route regression, cs-fixer clean.
LST-66 (2.3) front. Companion to the backend module migration.
- Move pages (absences, team-absences), 8 components, the absences service +
DTO and the useAbsenceHelpers composable into frontend/modules/absence/
(auto-detected layer; composable now auto-imported).
- Rewrite consumers: AdminAbsencePolicyTab and the time-tracking calendar
(getPublicHolidays) point to ~/modules/absence/...
- Middlewares (employee/admin) and shared services (clients, users,
user-data DTO) stay at the root. i18n stays global.
- Routes /absences and /team-absences preserved.
nuxt build passes; routes confirmed.
LST-66 (2.3) backend. Behaviour-preserving move of the absences domain into
src/Module/Absence/. API operations, securities, routes and the 10 MCP tool
names are unchanged.
- 3 entities + 3 enums moved to Domain/{Entity,Enum}; user relations stay on
UserInterface. 3 repositories split into Domain/Repository interfaces +
Doctrine impls (bound in services.yaml); find() kept off interfaces
(findById instead).
- Pure services (AbsenceDayCalculator, PublicHolidayProvider) -> Domain/Service;
AbsenceBalanceService -> Application/Service; State (5), controllers (5),
10 MCP tools and AccrueLeaveCommand -> Infrastructure/.
- New LeaveProfileInterface contract (Shared) exposes the HR getters used by
AbsenceBalanceService/AccrueLeaveCommand; User implements it -> Absence no
longer imports the concrete Core User. MCP tools/command inject
UserRepositoryInterface (findById) instead of the concrete repository.
- Timestampable/Blamable added to AbsenceBalance and AbsencePolicy (additive
migration: created_at/updated_at + created_by/updated_by FK ON DELETE SET
NULL + COMMENT). AbsenceRequest untouched (already has createdAt/reviewedAt).
- AbsenceModule registered (id absence, 4 RBAC perms, not re-wired); doctrine
mapping added; team-absences sidebar item gated by the module.
161 tests green, mapping valid, no API route regression, cs-fixer clean.
Tranche 3 of LST-65. Task and Project adopt TimestampableBlamableTrait.
- Additive migration on task and project: created_at/updated_at (nullable),
created_by/updated_by (nullable INT, FK to "user" ON DELETE SET NULL) +
indexes + COMMENT ON COLUMN. down() drops only the added objects.
- Trait fields stay out of the existing API groups (trait carries its own).
- Functional test (TaskTimestampableTest) confirms created_at on persist and
updated_at refresh on update.
161 tests green, no destructive migration.
Tranche 2 of LST-65. Mechanical, behaviour-preserving move of the core
business domain into src/Module/ProjectManagement/. API operations,
securities, uriTemplates and the 38 MCP tool names are all unchanged.
- 10 entities + 2 enums moved to Domain/{Entity,Enum}; intra-module
relations stay concrete, cross-module relations go through contracts
(Project.client -> ClientInterface, Task/TaskDocument users ->
UserInterface).
- 9 repositories split into Domain/Repository interfaces + Doctrine impls,
bound in services.yaml; consumers inject the interfaces. find() kept off
the interfaces (ServiceEntityRepository ?object compat) -> findById().
- State (7), MCP tools (38), controller, CalDavService/RecurrenceCalculator,
3 Doctrine listeners and SwitchWorkflowOutput moved under Infrastructure/.
- doctrine.yaml: ProjectManagement mapping + resolve_target_entities of the
3 module contracts repointed to the module (ClientInterface stays legacy).
- ProjectManagementModule registered (id project-management, 4 RBAC perms,
not re-wired); sidebar my-tasks/projects gated by the module.
- Legacy not-yet-modularised consumers (Mail/Gitea/BookStack, Serializer,
fixtures, tests) swapped to the module FQCN — transitional coupling to be
cleaned in 2.4/2.5/2.6.
159 tests green, mapping valid, no API route regression, cs-fixer clean.
Tranche 1 of LST-65 (ProjectManagement module migration). Decouples the
TimeTracking module from the core-business entities before they move, with
no entity relocation yet — keeps the diff minimal and the risk isolated.
- New read contracts in Shared/Domain/Contract (minimal surface, aligned on
the entities' real nullable signatures): ProjectInterface (id/code/name),
TaskInterface (id/number/title), TaskTagInterface (id/label/color),
ClientInterface (id/name).
- Project/Task/TaskTag/Client implement their contract (entities stay in
src/Entity for now). Project.client typed as ClientInterface.
- TimeEntry (TimeTracking) now references ProjectInterface/TaskInterface/
TaskTagInterface instead of the concrete entities; repository + DQL
untouched in behaviour.
- resolve_target_entities maps the 4 contracts to the legacy entities (will
be repointed to the module in tranche 2).
- Adds the migration plan doc.
159 tests green, mapping valid, cs-fixer clean.
Companion to the backend module migration (LST-64). The Nuxt layer is
auto-detected from frontend/modules/* — no nuxt.config change needed.
- Move page, timer store, time-entries service + DTO and the 6 time-tracking
components into frontend/modules/time-tracking/.
- Rewrite explicit service/DTO imports to ~/modules/time-tracking/* (store and
components stay auto-imported); update the dashboard (index.vue) consumer.
- Route /time-tracking preserved; i18n keys kept in the global locale file.
nuxt build passes; /time-tracking routed.
First business module of Phase 2 (LST-64, rodage). Strangler-style,
additive move — no behavioural change to the public API or MCP tools.
- New module App\Module\TimeTracking (TimeTrackingModule, id "time-tracking",
declares time-tracking.entries.view/export permissions in the RBAC catalog;
operation security left on ROLE_USER, not re-wired here).
- Move TimeEntry entity, repository (now interface + Doctrine impl bound in
services.yaml), ActiveTimeEntryProvider, export service/controller and the
4 MCP TimeEntry tools into the module. #[ApiResource] (operations, security,
uriTemplates /time_entries/*), filters and serialization groups preserved.
- Doctrine mapping "TimeTracking" added; table time_entry unchanged.
- Sidebar item gated with module "time-tracking" (SidebarFilter disables the
route when the module is inactive).
- Timestampable/Blamable adopted (first adopter): additive migration adds
created_at/updated_at/created_by/updated_by (nullable, FK SET NULL) +
COMMENT ON COLUMN. Functional test confirms created_at on persist and
updated_at refresh on update — the suspected preUpdate recompute issue does
not occur (Doctrine ORM 3.6.2 recomputes change sets after preUpdate).
159 tests green, schema mapping valid, php-cs-fixer clean.