Changelog

Gerado a partir das anotações reais das tags de versão do repositório — não é um ficheiro mantido à parte.

v1.65.0 2026-07-25
Sprint 65: Scheduling & Timetabling

No Scheduling bounded context and no new Lesson/Timetable Entry model — the
architecture review found AttendanceSession (one class meeting day within an
intake, in place since Sprint 10) already the right aggregate to extend with
real time-of-day precision, location, and an explicit lifecycle state.
held_on stays untouched and still required; starts_at/ends_at add optional
precision within that same day, mirroring CourseIntake.starts_at/ends_at's
own coarse/precise coexistence with registration_opens_at/closes_at. Also
activates Organization.timezone, stored since Sprint 50 but never used in a
real date computation until now.

Added
- AttendanceSession gains starts_at/ends_at, location, and a persisted
  status (AttendanceSessionStatus: Scheduled/Cancelled); sessionStatus() is
  a separate derived 3-way read (scheduled/completed/cancelled)
- AttendanceSession::instructorConflicts()/locationConflicts() — derived
  only, never persisted; a Cancelled session never participates as either
  side of a conflict
- BatchCreateAttendanceSessions — generates real sessions from a
  weekday/date-range pattern; the pattern itself is never persisted
- App\Support\TimezoneFormatter — presentation-only; storage stays UTC
  everywhere
- Admin session edit + batch-create UI, conflict badges on the sessions
  index, and Admin\ReportController::schedule() — a live timetable
  defaulting to the current week
- GET /api/v1/courses/{course}/sessions/{session}/meetings[/{id}] (reuses
  courses:read, no new scope), named "meetings" to avoid colliding with
  Sprint 58's own "sessions" vocabulary for CourseIntake
- CourseIntake::nextSession() — the canonical "what's next" read, feeding
  the Portal enrollment card, the instructor dashboard's upcoming-sessions
  list, and Global Search's subtitle

Changed
- Enrollment::sessionsAttended()/totalSessions() and
  CourseIntake::totalAttendanceRecords()/attendedAttendanceRecords() now
  exclude Cancelled sessions from both sides of every attendance rate
- RecordSessionAttendance rejects recording against a Cancelled session
- AttendanceRecord::scopeAttended()'s status filter is now qualified with
  the table name, since attendance_sessions gained its own status column

Preserved, deliberately
- No persisted recurrence pattern, no per-session instructor attribution,
  no cancellation notifications, no new permission — all explicitly
  reconsidered and deferred
v1.64.0 2026-07-24
Sprint 64: Instructor Management & Teaching Allocation
v1.63.0 2026-07-24
Sprint 63: Learner Progress & Analytics
v1.62.0 2026-07-24
Sprint 62: Learning Content & Resources
v1.61.0 2026-07-24
Sprint 61: Assessments & Learner Evaluation
v1.60.0 2026-07-23
v1.60.0 — Sprint 60: Completion & Certification

Unlike Sprints 57-59, this sprint activated no dormant aggregate -
Completion and Certificate were already mature (issuance, verification,
QR, audit, notifications, and webhooks all existed since Sprint 12/15).
Sprint 60 polishes both: two small model refinements and the REST
surface's last real gap. This completes the Academy Platform's first
operational lifecycle: Course -> Application -> Review -> Enrollment ->
Capacity/Waitlist -> Training Session -> Completion -> Certificate.

Added
- Optional notes on Completion - administrative context for a
  completion decision, editable independently of the outcome itself
- CourseIntake::completionRate() - Completed / (Completed + Withdrawn)
  among an intake's confirmed enrollments; enrollments with no decided
  outcome yet are excluded from both sides, so an in-progress cohort
  never appears to have a poor rate
- GET /api/v1/certificates and /{certificate} - the certificates:read
  scope, reserved since Sprint 38, is now active; certificates:issue
  stays reserved, issuance stays admin-panel-only
- completion_outcome, completion_notes, completion_recorded_at on
  EnrolmentResource; completion_rate on CourseIntakeResource

Changed
- SetEnrollmentCompletion's save-gate now covers either outcome or
  notes changing, but EnrollmentCompleted only fires - and decided_at
  only moves - when the outcome itself changes. Editing just the note
  is administrative housekeeping, not a new completion decision

Preserved, deliberately
- No Workflow, no Scheduler automation, no new Search provider, no
  Health check, no Document Framework integration for certificate
  PDFs, no certificate reissue/revocation - all explicitly
  reconsidered and left as-is; Sprint 12's "certificates are
  permanent, no correction path" decision stands

Testing: 1504/1504 tests passing, clean frontend build, all nine
registry validators passing, manual verification against the real
PostgreSQL development environment including database-level
confirmation that editing a completion note alone never moves
decided_at or re-fires the completion event.
v1.59.0 2026-07-23
v1.59.0 — Sprint 59: Student Enrolment

Enrollment — already fed by the application-review lifecycle
(CourseApplication.status) and the completion outcome
(Completion.outcome) since Sprint 8/11 — gains real capacity
management: maximum_participants (dormant since Sprint 58) becomes
operational, with automatic waitlisting and promotion rather than a
new parallel lifecycle.

Added
- EnrollmentStatus enum (Confirmed/Waitlisted/Cancelled) — deliberately
  not a Workflow definition; the Applied->Approved->Enrolled->Withdrawn
  arc already exists across CourseApplication/Completion, so a third
  Workflow would have duplicated it rather than replaced ad hoc state
- CourseIntake::availableSeats()/capacityStatus() (available/full/
  waitlist) and Enrollment::waitlistPosition() — all derived, never
  persisted
- CreateEnrollment auto-waitlists instead of rejecting when an intake
  is full
- PromoteWaitlistedEnrollment and CancelEnrollment actions, plus a new
  PromoteWaitlistedEnrollmentsTaskHandler scheduled sweep (reuses the
  maintenance queue Sprint 58 activated) — oldest-enrolled-first, no
  configurable promotion policy
- EnrollmentPromoted domain event (audit, staff notification, webhook)
  and EnrollmentCancelled (audit, learner email — deliberately no
  webhook, an internal seat-administration decision)
- EnrollmentSearchProvider — Global Search's 10th group
- Dedicated Enrollment administration view, nested
  Course -> CourseIntake -> Enrollments, with Promote/Cancel actions

Changed
- A partial unique index (WHERE status <> 'cancelled') replaces the
  Sprint 8 full unique index on (user_id, course_intake_id) — a
  cancelled enrollment no longer permanently blocks legitimate
  re-enrollment for the same intake
- EnrollmentCreated's email/staff-notification both now branch on
  status — the mailable previously always claimed a confirmed seat,
  which stopped being true once a full intake could waitlist instead
- EnrolmentResource gains status/waitlist_position; CourseIntakeResource
  gains available_seats/capacity_status; GET /api/v1/enrolments gains
  an optional status filter — all additive, the existing flat REST
  contract is unchanged
- EnrollmentCompleted (dispatched since Sprint 11) gains
  staff-notification and webhook listeners

Fixed
- CreateEnrollment's own application-level duplicate check didn't
  exclude Cancelled rows, contradicting the new partial index's intent
  — caught by this sprint's own test suite before shipping

Testing: 1482/1482 tests passing, clean frontend build, all eight
registry validators passing, manual verification against the real
PostgreSQL development environment including the partial unique index
and re-enrollment-after-cancellation behavior.
v1.58.0 2026-07-23
v1.58.0 — Sprint 58: Training Sessions

CourseIntake — already the aggregate Enrollment/AttendanceSession point at
since Sprint 1 — becomes the platform's "Training Session": the Workflow
Framework's second real, active definition, proving it serves multiple
domains unmodified.

Added
- code, instructor_id (internal instructor FK), registration_opens_at/
  registration_closes_at on CourseIntake; instructorName() resolves
  internal-or-external instructor in one call, registrationStatus()
  (not_open/open/closed) is derived, never persisted
- The course_intake_lifecycle Workflow (draft -> scheduled ->
  open_for_registration -> in_progress -> completed, cancelled from any
  non-terminal state) — the engine's second active definition
- SweepCourseIntakeLifecycleTaskHandler, a new active Task Registry entry
  observing registration_opens_at/starts_at/ends_at and driving the
  matching Workflow transition — cancelled stays exclusively
  admin-triggered, never automated
- CourseIntakeRegistrationOpened/CourseIntakeCompleted/
  CourseIntakeCancelled domain events, each with a dedicated audit
  action, staff notification, and webhook event
  (course_intake.registration_opened/.completed/.cancelled)
- CourseIntakeSearchProvider — Global Search's 9th group
- Nested read-only REST API, GET /api/v1/courses/{course}/sessions[/{session}],
  reusing the existing courses:read scope; CourseIntakeResource exposes
  workflow_state, registration_status, registration_open, instructor_name
  as derived fields
- Admin instructor picker, registration-window fields, and a workflow
  status panel on the Turma edit page

Changed
- The maintenance queue category is now active — its first real consumer
  is the lifecycle sweep
- Automated background workflow transitions are attributed to the
  existing bootstrap administrator account as the acting user, rather
  than a new synthetic "System" identity

Fixed
- x-ui.select requires a plain array, not an Eloquent Collection — the
  instructor picker's pluck('name', 'id') needed ->all()

Testing: 1430/1430 tests passing, clean frontend build, all six registry
validators passing, manual verification against the real PostgreSQL
development environment.
v1.57.0 2026-07-23
Sprint 57: Course Management Enhancement

Opens Phase 2 - Academy Platform: extends the Course domain that has
existed since Sprint 1 rather than building a parallel one. Adds
CourseCategory (a new lightweight aggregate), code/language/price/
currency on Course, maximum_participants on CourseIntake, and activates
course_publication - the Workflow Framework's first real, active
definition (draft -> review -> {approved, draft} -> published ->
archived, only archived terminal). is_published stays as a derived
compatibility column, synced by a new SyncCoursePublicationState
listener, so Search/REST/the public catalogue/filters needed no
changes. Introduces <x-workflow.transition-actions>, a reusable
presentation-layer component for any future workflow, and course_image,
the Document Framework's first public-disk type. CourseArchived mirrors
the pre-existing CoursePublished with its own webhook. README gains a
Development Phases section.
v1.56.0 2026-07-23
Sprint 56: Security Hardening Framework

Extends the existing Health Framework with a new 'security' category
(15 checks total) instead of introducing a parallel SecurityService/
SecurityRegistry: session cookie security, debug mode, login/API rate
limiting, an explicit CORS policy, CSP status (reported degraded by
design pending real-browser validation), password policy structural
validation, and the active VirusScanner implementation. Adds
failed-login auditing (identity.login_failed) through the existing
Audit and Identity frameworks. LoginForm::MAX_ATTEMPTS and
SecurityHeaders::CSP_ENABLED are the two shared constants that keep
each checker and its real runtime logic from ever silently drifting
apart. Overall platform health now honestly reports degraded at
baseline, reflecting two real, accepted gaps (no CSP yet, no virus
scanner yet) rather than papering over them.
v1.55.0 2026-07-23
Sprint 55: Performance & Caching Framework

Registry-driven caching framework (App\Infrastructure\Cache): 8 profiles
declared, 2 shipped active (developer_portal, organization) — the only
two real, currently-uncached, read-heavy paths grounded during Step 1.
CacheService is the single abstraction over Cache::store(), using a
store-agnostic versioned-key-prefix scheme for flushProfile() instead of
native Laravel cache tags (this platform's real store, 'database', does
not implement TaggableStore). CacheHit/CacheMiss/CacheInvalidated are
observability-only domain events (new 'cache' category). Developer
Portal's PortalMetadataRepository and Organization's OrganizationService
both implement CacheableService; invalidation is explicit
(portal:build's own pipeline) or event-driven (OrganizationUpdated via
a new listener) per profile, never inline in a controller. New Admin
Operations > Cache page with a manual flush action, 404-gated by
CacheService::supports().
v1.54.0 2026-07-23
Sprint 54: Observability & Health Framework

Registry-driven health-check framework (App\Health): 7 active checks
(database, queue, scheduler, storage, mail, webhook, integrations).
queue/scheduler delegate to the existing OperationsDashboardService;
HealthResult is an ephemeral, non-persisted value object. Exposes an
Admin Operations > Saude sub-page (health.view permission) and a
read-only REST endpoint (GET /api/v1/health, /api/v1/health/{check},
health:read scope). Aggregated worst-status-wins overallStatus() feeds
both surfaces. health.failed is audited via a documented sentinel
(HealthResult has no persisted row to reference).
v1.53.0 2026-07-23
v1.53.0 - Integration & Webhook Framework

- Registry-driven Integration & Webhook Framework (config/integrations.php
  + IntegrationRegistry, IntegrationAdapter contract, IntegrationMessage,
  IntegrationService: supports/publish/retry)
- Only the 'webhook' integration is active, delegating wholesale to the
  pre-existing Sprint 34 WebhookService/WebhookDelivery pipeline — no
  duplicated HTTP client logic; moodle/salesforce/dynamics365/sap ship
  reserved, per the brief's own named examples
- New generic RunIntegrationDeliveryJob resolves adapters via the registry
- IntegrationQueued/Delivered/Failed are real dispatched domain events —
  closing a pre-existing gap where webhook/notification delivery had no
  event, only an audit row; retrofitted onto the existing DeliverWebhookJob
  additively, with no change to its own delivery logic
- Admin: new "Mensagens de Integração" item inside the existing
  "Integração" section (not a duplicate "Administration > Integrations")
- API: GET/POST /api/v1/integrations, .../{id}/retry
  (integration-messages:read/write scopes, distinct from the pre-existing
  reserved integrations:manage scope)
- 1264 tests passing
v1.52.0 2026-07-23
v1.52.0 - Scheduler & Background Task Framework

- Task Registry (config/tasks.php + TaskRegistry) makes scheduled work
  pluggable via a TaskHandler interface, modeled on ReportGenerator
- Extends Sprint 30/43's real scheduling engine (App\Scheduling\
  ScheduledTask/ScheduleService, JobRun) instead of duplicating it —
  a Step 1 architecture decision after finding ~85% overlap with
  existing infrastructure
- 3 existing export task types migrated onto the registry as active
  (real handlers wrapping the pre-existing Exporter classes); 4
  brief-named illustrative types (workflow_reminders, document_retention,
  notification_digest, report_cleanup) ship reserved
- New generic RunScheduledTaskJob replaces the old per-type hardcoded
  match() only in the scheduler's own dispatch path
- TaskStarted is the one genuinely new domain event; JobCompleted/
  JobFailed are reused directly for completion/failure
- Admin panel moved from the top-level, ungated admin.schedules.* to
  Administration > Scheduled Tasks, closing a real pre-existing gap
  with new tasks.view/tasks.manage permissions
- API: GET/POST /api/v1/tasks, .../{id}/run (tasks:read/tasks:write)
- ScheduleService::supports() lets callers check task availability
  without reaching into the registry directly
- 1228 tests passing
v1.51.0 2026-07-23
v1.51.0 - Workflow & Approval Engine

- Reusable, module-agnostic Workflow & Approval Framework
  (config/workflows.php + WorkflowRegistry, WorkflowInstance/Approval
  models, WorkflowService: supports/start/assign/transition/
  canTransition/currentState/history)
- Three illustrative workflow definitions (course_application,
  organization_change, document_publication) shipped reserved — no
  existing entity's real lifecycle logic is touched this sprint
- 5 domain events (WorkflowStarted/Assigned/Transitioned/Completed,
  ApprovalRecorded), new "workflow" domain event category
- Notifications reuse Sprint 46's framework directly (assignee +
  requester-at-completion), zero changes to the notification pipeline
- Admin panel: read-only "Workflows" section (instances, approvals,
  history, current assignee)
- API: GET/POST /api/v1/workflows, .../{id}/transition, .../{id}/assign
  (workflows:read/workflows:write scopes)
- Audit trail reused as WorkflowService::history()'s data source —
  no separate transition-log table
- 1201 tests passing
v1.50.0 2026-07-22
v1.50.0 - Organization Settings & Platform Configuration

- Configurable organization identity, branding, contact, localization,
  and platform settings (config/organization.php, 20 fields / 5 categories)
- OrganizationRegistry + validator + organization:validate console command
- Singleton Organization model (app/Organization/Domain/Models/Organization.php)
- OrganizationService: get/update, logo & favicon upload via the
  existing Document Framework (versioned via supersedesId)
- OrganizationUpdated domain event (sync) + audit log listener
- Admin panel: singleton "Organização" settings page
- API: first singleton-resource endpoint, GET/PATCH /api/v1/organization
  (organization:read/organization:write scopes)
- Retired the unused App\Identity\IdentityRegistry::organization()
  forward reference in favor of the real implementation
- 1164 tests passing
v1.49.0 2026-07-22
v1.49.0 - Search & Global Indexing Framework (Path A)

Highlights:
- Extended the existing, live Search Framework (App\Search\SearchService/SearchProvider, shipped since Sprint 22) rather than reintroducing a parallel one - discovered during architecture review that a mature, fully-tested global search page already existed
- Four new SearchProvider implementations closing real coverage gaps: Users (admin/staff), Documents, Reports, and identity Notifications - the pre-existing providers covered only academic/training data (CourseApplication, Student, Certificate, Course)
- Closed the platform's last remaining ungated admin page: admin.search.index now requires a real search.view permission instead of relying on bare "admin" middleware
- New GET /api/v1/search REST endpoint under a new search:read scope - a thin wrapper over the existing live SearchService, no new search mechanism
- SearchService::availableGroups() lets the Developer Portal list searchable resources without a parallel, hand-maintained registry
- Deliberately no persisted index, no new queue category, no new domain events, and no rebuild pipeline this sprint - scoped down from the original brief after finding the live-query mechanism already meets the platform's actual search needs; that heavier path stays available for a future sprint if real scale or full-text requirements materialize
v1.48.0 2026-07-22
v1.48.0 - Reporting & Export Framework

Highlights:
- Centralized Reporting & Export Framework (ReportRegistry, config/reports.php) - single entry point (ReportService) for requesting, generating, storing, downloading, and expiring reports
- 6 report types registered (User, Identity Audit, Notification, Document, Training, System Health) - User Report and Document Report have real generators, the other four are honest placeholders producing real, minimal output
- Two-axis generation pipeline: ReportGenerator (business data) x ReportFormatWriter (PDF/CSV serialization) - Excel/JSON/XML are future writer classes only, no generator changes needed
- Full lifecycle (Requested -> Generating -> Generated -> Stored -> Available -> Expired, plus a pragmatic Failed state) with real per-type expiry via expires_after_days
- Every generated report becomes a managed Document through Sprint 47's DocumentService (new storeGenerated() method for server-generated, non-uploaded files) - no raw file writes
- Reuses the existing, already-active "reports" queue category (Sprint 43) - no new queue category introduced
- Asynchronous-only REST API (GET/POST /api/v1/reports, GET .../download) - POST returns 202 Accepted immediately, generation always happens off the request
- Admin "Relatórios Gerados" browser under Administração - deliberately distinct naming and URL prefix from the pre-existing, live, unrelated top-level "Relatórios" institutional reports section, which stays completely untouched
- Full audit trail (report.requested/generation_started/generated/stored/downloaded/expired/generation_failed) via the existing AuditLog
- Caught and fixed a real bug during manual verification: the admin controller and views were wired to a stale route name from before the URL prefix was renamed to avoid the naming collision - every redirect would have 500'd in production
v1.47.0 2026-07-22
v1.47.0 - File & Document Management

Highlights:
- Centralized Document Framework (DocumentRegistry, config/documents.php) - single entry point (DocumentService) for storing, versioning, archiving, and deleting documents; business modules never touch storage paths/disks directly
- 6 document types (profile documents, employment letters, character references, training certificates, course documents, generated reports), each with its own size/MIME limits, retention policy, and storage directory - all mapped to the existing "local" disk, no new physical storage introduced
- File lifecycle (stored -> available -> archived -> deleted) with a real versioning mechanism - uploading a new version automatically supersedes and archives the prior one
- New "documents" queue category for background processing, starting with a placeholder virus-scan step behind a swappable VirusScanner contract (NullVirusScanner today, a real engine is a one-line binding change later)
- Full REST API (GET/POST/PATCH/DELETE /api/v1/documents, plus a dedicated download endpoint) under new documents:read/documents:write scopes - the API's first genuine multipart file-upload endpoint
- Admin Document Browser (Administracao > Documentos) with metadata, storage status, audit history, and retention display
- Full audit trail (document.stored/archived/deleted/downloaded/updated) via the existing AuditLog
- Fixed a real staleness bug found during manual verification: a queued listener reading a domain event's model snapshot instead of re-fetching by id could silently revert a document's status after it had already been archived - now consistent with how every other queued job in the codebase (DeliverWebhookJob, DeliverIdentityNotificationJob) re-fetches its target
v1.46.0 2026-07-22
v1.46.0 - Notification Framework

Highlights:
- Centralized identity-lifecycle notification framework (NotificationRegistry, config/identity_notifications.php)
- 5 notification types: invitation, activation, suspension, reactivation, archival - each mapped to a domain event and Mailable via the registry
- Queued delivery pipeline (Notify* listener -> NotificationRecord -> DeliverIdentityNotificationJob) with retry/backoff, reusing the existing "notifications" queue category
- Admin Notification Log (Administracao > Notificacoes) with delivery history, retry action for failed deliveries
- Read-only REST API (GET /api/v1/notifications, /{id}) under a new identity-notifications:read scope
- Full audit trail (identity_notification.created/queued/delivered/failed/retried) via the existing AuditLog
- Deliberately kept separate from the pre-existing ChannelNotifications (Slack/Teams) and App\Notifications (student-facing) systems - no naming collisions, no unification this sprint
v1.45.0 2026-07-22
Sprint 45: Identity & User Lifecycle

Highlights

• Introduced a complete admin/staff user lifecycle — draft, invited, active, suspended, archived — as the platform's first full end-to-end business workflow built on the infrastructure delivered in Sprints 39-44. Students are entirely unaffected, continuing through the existing, untouched course-application invitation flow.
• Added IdentityRegistry (config/identity.php) as the single source of truth for lifecycle states, invitation expiry, the bootstrap administrator, and — for the first time in this project — a real, configured password policy.
• Introduced a dedicated Invitation model with its own token, expiry, acceptance timestamp, and resend tracking, replacing ad hoc reuse of Laravel's password-reset mechanism for this flow.
• Published seven new domain events, the first real population of the "identity" category Sprint 44 introduced empty, with queued listeners reusing the exact QueueRegistry pattern already established for 18 other listeners.
• Reconciled with the existing Sprint 39 Users administration page rather than duplicating it — identity management and role assignment are now cleanly separated under their own permissions and routes.
• Exposed a full set of versioned REST endpoints for user lifecycle management under new users:read/users:write API scopes, automatically documented through the existing OpenAPI pipeline with no manual work.
• Bootstrap administrator creation is idempotent and reuses the existing RBAC seeder rather than introducing a new mechanism.
• Full regression suite (1015 tests) passing, plus manual verification of the complete lifecycle and REST API against the real PostgreSQL development database.
v1.44.0 2026-07-22
Sprint 44: Domain Events & Event Bus

Highlights

• Introduced DomainEventRegistry (config/domain_events.php) cataloguing the 11 real domain events already dispatched across the platform — no relocation, since Laravel's event system was already fully wired via auto-discovery before this sprint.
• Converted the 18 listeners responsible for webhook, channel, and mail fan-out to ShouldQueue, each resolving its queue via QueueRegistry (Sprint 43) — Record*Audit listeners stay synchronous deliberately, so the Audit Log keeps showing entries immediately.
• Added a read-only Event Monitor to the Admin Integration Console — registered events, their real listeners (discovered via reflection, never hand-typed), dispatch mode, and 7-day published/processed/failed counts.
• Added a new day-bucketed domain_event_counters table, fed by a single wildcard event listener plus the framework's own queue lifecycle events — no changes to any of the 11 event classes or 26 listener bodies.
• Added DomainEventRegistryValidator and `php artisan events:validate`, cross-checking the registry against real listener bindings via the same reflection-based discovery the Event Monitor itself uses.
• Found and fixed a real, non-obvious regression in two pre-existing tests (WebhookListenersTest, NotificationListenersTest), whose blanket Bus::fake() calls were silently preventing the now-queued listeners from running at all — traced to Laravel's own queued-job execution path.
• Full regression suite (949 tests) passing, plus manual verification against the real PostgreSQL development database.
v1.43.0 2026-07-22
Sprint 43: Background Jobs & Queue Infrastructure

Highlights

• Introduced QueueRegistry as the authoritative source of queue definitions, labels, lifecycle status, and operational metadata.
• Added registry-driven queue resolution, eliminating hard-coded queue names across dispatched jobs.
• Implemented infrastructure-focused queue orchestration while keeping controllers lightweight and business services fully queue-agnostic.
• Added a Queue Monitor to the Integration Console, providing live operational visibility into queued, processing, and failed jobs alongside the existing Job History.
• Integrated Laravel's existing jobs and failed_jobs tables for live operational metrics without introducing additional database schema or persistent counters.
• Preserved the active/reserved registry lifecycle model and introduced a new, standalone QueueRegistryValidator (`php artisan queues:validate`) to verify queue assignments and registry consistency.
• Maintained complete separation between queue infrastructure and application business logic — no job's business logic changed, only how each already-correct job resolves its queue name.
• Completed automated regression testing and manual verification against the real PostgreSQL development database with no regressions to the REST API, Developer Portal, Integration Console, RBAC, scheduling, or existing background processing.
v1.42.0 2026-07-22
Sprint 42: API Rate Limiting & Quotas

Highlights

• Introduced ApiRateLimitRegistry as the authoritative source of API quota policies, key strategies, lifecycle status, and documentation metadata.
• Added ApiRateLimit middleware using Laravel's RateLimiter for request accounting while emitting standards-compliant RateLimit and Retry-After response headers.
• Implemented explicit, registry-driven rate-limit policies for public API surfaces, authenticated API tokens, and Developer Portal resources — plus two reserved policies (health, webhook) registered for endpoints that don't exist yet.
• Adopted per-token quota enforcement for authenticated API requests, enabling independent limits for machine-to-machine integrations while preserving controller and service isolation.
• Extended the OpenAPI generation pipeline with centralized rate-limit metadata through OpenApiGenerator's own document-enrichment step (not a Scramble transformer hook, which this codebase doesn't use), eliminating duplicate controller annotations.
• Expanded the Developer Portal with a new Rate Limiting page (sourced from ApiRateLimitRegistry, the same pattern as the Errors/Scopes pages) and per-endpoint rate-limit badges on the REST API guide, drawn from the enriched OpenAPI artifact.
• Added a read-only Rate Limiting section to the Integration Console for operational visibility without introducing runtime configuration.
• Confirmed the existing build-time registry consistency checks continue to pass with the new 429 responses and rate-limit metadata.
• Expanded automated regression coverage and completed successful manual verification against the real PostgreSQL development database with no regressions to the REST API, RBAC, Developer Portal, administration, or authentication.
v1.41.0 2026-07-21
Sprint 41: API Versioning

Highlights

- Introduced ApiVersionRegistry as the authoritative source of API lifecycle metadata, including version status, support, and release information.
- Refactored API routing into version-specific route files with versioned route names while preserving existing API behavior and contracts.
- Added DeprecationHeaders middleware to provide standards-based lifecycle headers driven entirely by ApiVersionRegistry without introducing version awareness into controllers or services.
- Extended the OpenAPI generation pipeline, Developer Portal, search index, examples, and metadata generation to support multiple API versions through parameterized build components.
- Added version-aware navigation and documentation throughout the Developer Portal, including version-scoped OpenAPI artifacts and endpoint references.
- Strengthened build-time validation to ensure version registry consistency and generated artifact integrity.
- Expanded automated test coverage to 872 passing tests and completed successful manual verification against the real PostgreSQL development database with no regressions to the REST API, RBAC, administration, student portal, or authentication.
v1.40.0 2026-07-21
Sprint 40: Developer Portal

Highlights

- Introduced a public Developer Portal generated entirely from code-first OpenAPI documentation and registry metadata.
- Adopted attribute-based OpenAPI generation with build-time specification validation and cached runtime artifacts.
- Added DeveloperPortalBuilder with a deterministic documentation build pipeline including search index, examples, metadata, and portal asset generation.
- Introduced WebhookEventRegistry and ApiErrorRegistry as new registry-driven configuration sources, continuing the platform's single-source-of-truth architecture.
- Added an interactive API explorer using a maintained OpenAPI reference UI with direct browser-to-API communication via Bearer tokens.
- Generated language-specific integration examples, searchable API reference, and build-time changelog from annotated Git tags without introducing duplicate documentation sources.
- Implemented registry consistency validation to detect documentation and configuration drift before publication.
- Expanded automated test coverage to 845 passing tests and completed successful manual verification against the real PostgreSQL development database with no regressions to RBAC, REST API, or existing application functionality.
v1.39.0 2026-07-21
Sprint 39: Role-Based Access Control

Highlights

- Introduced a complete Role-Based Access Control (RBAC) system for authenticated users while preserving independent Sanctum API token authorization.
- Added a centralized permission registry with active and reserved permission definitions as the single source of truth.
- Implemented AuthorizationService as the sole permission-resolution engine with efficient per-request memoization.
- Added role, permission, role-permission, and user-role domain models with support for multiple roles per user.
- Introduced permission middleware for route-level authorization and updated existing Policy classes to delegate explicitly to AuthorizationService.
- Added a new Administration area for managing roles, permissions, and user role assignments.
- Implemented secure bootstrap of existing administrators through automatic Super Administrator assignment during deployment.
- Added comprehensive audit coverage for role lifecycle events and permission changes.
- Expanded automated test coverage to 798 passing tests (2,076 assertions) and completed successful manual verification against the real PostgreSQL development database.
v1.38.0 2026-07-21
v1.38.0 - Sprint 38: Scoped API Tokens

Highlights

• Introduced fine-grained API authorization using Laravel Sanctum's built-in abilities middleware.
• Added a central API scope registry supporting active and reserved scopes.
• Extended ApiTokenService to manage scoped token creation, rotation, and in-place scope updates while remaining the single lifecycle authority.
• Added scoped token management to both the Integration Management Console and Artisan commands.
• Preserved complete backward compatibility through existing wildcard token abilities.
• Added api_token.scope_updated audit events while preserving all existing API token audit event names.
• Protected implemented REST resources with route-level ability checks, leaving controllers unchanged.
• Added comprehensive automated test coverage for scope validation, middleware enforcement, service behaviour, audit, CLI, UI, and regression scenarios.
• Manually verified scoped authorization, wildcard compatibility, audit integrity, and Integration Console workflows against the real PostgreSQL development database.
v1.37.0 2026-07-21
v1.37.0 - Sprint 37: Integration Management Console

Highlights

• Introduced dedicated ApiTokenService, WebhookEndpointService, and NotificationChannelService as the single CRUD source of truth for both web administration and Artisan commands.
• Preserved WebhookService and NotificationService as event-driven delivery services with no behavioural changes.
• Added a web-based Integration Management Console for API Tokens, Webhooks, and Notification Channels using the existing Blade and Tailwind design system.
• Refactored existing Artisan commands to delegate lifecycle operations to the new service layer, eliminating duplicated CRUD logic.
• Centralized lifecycle audit generation within the new administration services while preserving existing API token audit events.
• Added lightweight delivery aggregation methods for management views, including success/failure counts and last delivery information.
• Added comprehensive automated test coverage for administration services, controllers, authorization, validation, audit, and regression scenarios.
• Manually verified the complete administration workflow against a real PostgreSQL database through Laravel's HTTP kernel, confirming CRUD operations, audit integrity, live aggregation data, authorization behaviour, and no regressions to existing platform functionality.
v1.36.0 2026-07-21
v1.36.0 - Sprint 36: Microsoft Teams & Slack Notifications

Highlights

• Introduced a dedicated NotificationService for enterprise collaboration platform notifications.
• Added independent NotificationChannel and NotificationDelivery models, separate from webhook infrastructure.
• Reused existing domain events without introducing new event classes.
• Implemented channel adapters for Microsoft Teams and Slack behind a common interface.
• Added a channel-neutral NotificationMessage value object with adapter-specific rendering.
• Implemented queued notification delivery with configurable retry and backoff.
• Added deterministic notification deduplication using stable event-derived identifiers.
• Added Artisan commands for notification channel management.
• Added audit logging for notification delivery outcomes.
• Added comprehensive automated test coverage for routing, rendering, delivery, retries, deduplication, and regression scenarios.
• Manually verified the framework end-to-end against a real PostgreSQL database with disposable local receivers standing in for Slack and Teams incoming webhooks, validating payload formats, retry behaviour, deduplication, audit integrity, and no regressions to existing platform functionality.
v1.35.0 2026-07-20
v1.35.0 - Sprint 35: API Token Management & Rotation

Highlights

• Introduced secure API token lifecycle management built on Laravel Sanctum.
• Added a lightweight ApiToken companion model for operational metadata without duplicating security-critical data.
• Implemented configurable default token expiry with optional per-token overrides.
• Added secure token rotation with immediate invalidation of replaced credentials.
• Added token revocation and administrative token listing commands.
• Preserved backward compatibility through a deprecated api:token alias.
• Added audit logging for token creation, rotation, and revocation.
• Ensured existing API tokens remain valid with no migration or expiry backfill.
• Added comprehensive automated test coverage for lifecycle operations, expiry handling, command behaviour, and regression scenarios.
• Manually verified the full token lifecycle against a real PostgreSQL database, confirming expiry behaviour, authentication, rotation, revocation, audit integrity, and no regressions to existing platform functionality.
v1.34.0 2026-07-20
v1.34.0 - Sprint 34: Outbound Webhooks

Highlights:
- New outbound webhook system: WebhookEndpoint/WebhookDelivery,
  managed via Artisan commands (webhook:endpoint:create/list/enable/
  disable), no admin UI.
- Seven event types supported (enrollment.created, certificate.issued,
  course.published, course_application.imported, export.completed,
  job.failed, alert.sent), reusing five existing domain events and
  adding two new ones (CoursePublished, AlertSent).
- HMAC-SHA256 signed deliveries with a stable envelope
  (id/type/version/occurred_at/payload), queued with automatic
  retry/backoff, audited on final outcome only.
- docs/webhooks.md documents the integration contract.
- 29 new tests (570 total passing); manually verified end-to-end
  against the real Postgres dev database with a live receiver.
v1.33.0 2026-07-20
v1.33.0 - Sprint 33: Public REST API

Highlights:
- New versioned, read-only REST API at /api/v1 (Students, Courses,
  Enrolments, Certificate verification), authenticated via Laravel
  Sanctum and restricted to administrator tokens.
- Token issuance via a new `api:token {email}` artisan command.
- Consistent filtering/sorting/pagination across all list endpoints,
  configurable rate limits (config/api.php), and an OpenAPI spec at
  docs/api/openapi.yaml.
- 34 new tests (541 total passing); manually verified end-to-end
  against the real Postgres dev database.
v1.32.0 2026-07-20
CATDI v1.32.0

Highlights

- Added AlertService for proactive operational alerting
- Reused OperationsDashboardService for all alert conditions, no duplicated queries
- Introduced the project's first queued Notification (OperationalAlertNotification)
- Added email delivery via the existing shared mail layout
- Implemented AuditLog-based cooldown keyed by condition, no new Alert entity
- Registered alert evaluation as a code-defined scheduler entry
- Made stale-queue and scheduler-inactivity thresholds configurable
- Fixed two real grace-period edge cases found during testing and manual verification
- Verified end-to-end against the real Laravel scheduler, queue worker, and PostgreSQL
v1.31.0 2026-07-20
CATDI v1.31.0

Highlights

• Introduced a dedicated Operations Dashboard for platform observability
• Added OperationsDashboardService to aggregate operational metrics from existing platform data
• Reused the existing Widget and DashboardService infrastructure for operational widgets
• Added operational summaries including job execution, scheduler activity and queue metrics
• Introduced operational health indicators distinguishing confirmed and inferred platform conditions
• Added recent job failure and upcoming schedule views linked to existing operational pages
• Enhanced Job History with status filtering and improved audit action labels
• Preserved the read-only observability model with no changes to execution or scheduling logic
• Expanded automated test coverage for operational aggregation, dashboard rendering and regression protection
• Completed end-to-end verification against a real PostgreSQL environment without introducing schema changes
v1.30.0 2026-07-20
CATDI v1.30.0

Highlights

• Introduced database-backed ScheduledTask management for recurring platform operations
• Added ScheduleService to register declarative schedules with Laravel's scheduler
• Implemented recurring CSV exports using the existing background execution framework
• Reused JobExecutionService and JobRun for scheduled executions without duplicating business logic
• Added administrator interface for creating, editing, enabling, disabling and manually running schedules
• Integrated scheduled executions with the existing Job History and Audit Log
• Added cron expression validation and resilient scheduler registration
• Expanded automated test coverage for scheduling, registration, validation and controller behaviour
• Completed end-to-end verification using Laravel Scheduler, the database queue driver and PostgreSQL
v1.29.0 2026-07-20
CATDI v1.29.0

Highlights

• Introduced a reusable JobExecutionService for synchronous and queued execution
• Added JobRun tracking as the application source of truth for background operations
• Implemented queued CSV imports and queued CSV exports using Laravel queues
• Preserved existing ImportService and ExportService business logic across both execution modes
• Added Job History with status tracking, retry support and completed export downloads
• Introduced background job audit logging for completed and failed executions
• Added manual pruning of completed JobRun records and generated export files
• Expanded automated test coverage for background execution, retry workflows and job history
• Completed manual verification against a real PostgreSQL database using the database queue driver
v1.28.0 2026-07-20
CATDI v1.28.0

Highlights

• Introduced a reusable ImportService and ExportService framework
• Added Course Application CSV import using the existing admissions workflow
• Added CSV export for Course Applications, Enrolments and Students
• Implemented preview and commit import workflow with temporary cached validation
• Added duplicate detection and idempotent import behaviour
• Introduced streamed CSV exports with deterministic ordering and UTF-8 BOM compatibility
• Added batch-level import auditing with a dedicated domain event and listener
• Expanded automated test coverage for import, export and controller workflows
• Completed manual verification against a real PostgreSQL database
v1.27.0 2026-07-20
CATDI v1.27.0

Highlights

• Introduced a dedicated StudentNotificationService for student-facing notifications
• Added an in-application notification centre with pagination and unread indicators
• Extended existing enrollment and certificate notifications to use Laravel's database channel
• Reused existing notification classes, events and listeners with no duplicate infrastructure
• Added idempotent mark-as-read and mark-all-as-read operations
• Enforced strict student ownership across notification access
• Adopted Laravel's standard notifications table without framework deviations
• Expanded automated test coverage for notification workflows and ownership isolation
• Completed manual verification against a real PostgreSQL database
v1.26.0 2026-07-20
CATDI v1.26.0

Highlights

• Introduced a dedicated student dashboard widget architecture
• Added StudentDashboardService for orchestrating student-facing widgets
• Implemented reusable StudentWidget interface with student-scoped authorization
• Added Welcome Panel, Quick Actions, Current Enrolments, Attendance Summary, Completed Enrolments and Applications widgets
• Preserved per-widget failure isolation for resilient dashboard rendering
• Maintained strict student data ownership across all dashboard queries
• Kept controllers thin and widget query logic fully encapsulated
• Expanded automated test coverage for student dashboard architecture and ownership isolation
• Completed manual verification against a real PostgreSQL database
v1.25.0 2026-07-20
CATDI v1.25.0

Highlights

• Introduced a modular widget-based dashboard architecture
• Added DashboardService for widget orchestration with zero entity-specific knowledge
• Implemented reusable Widget interface with authorization support
• Added KPI Cards, Recent Applications, Recent Audit Activity and Quick Actions widgets
• Added graceful per-widget failure isolation to preserve dashboard availability
• Reused existing audit timeline component and operational metrics
• Kept controllers thin and widget query logic fully encapsulated
• Expanded automated test coverage for dashboard architecture and failure isolation
• Completed manual verification against a real PostgreSQL database
v1.24.0 2026-07-20
CATDI v1.24.0

Highlights

• Added reusable bulk action infrastructure for administrative workflows
• Introduced BulkActionService for orchestrating safe bulk operations
• Added bulk Accept and Reject actions for Course Applications
• Reused existing application status workflow, domain events, notifications and audit logging
• Added page-scoped bulk selection with native confirmation dialogs
• Added detailed bulk operation summaries with Success, Skipped and Failed outcomes
• Preserved per-record transaction safety for partial success processing
• Expanded automated test coverage with bulk action and integration tests
• Completed manual verification against a real PostgreSQL database
v1.23.0 2026-07-20
CATDI v1.23.0

Highlights

• Introduced a reusable filtering architecture for administrative lists
• Added entity-specific Filter classes for Course Applications and Audit Logs
• Refactored report filtering into shared reusable helpers
• Added reusable sortable table header components
• Added filtering, sorting and reset functionality to Course Applications
• Added sorting support to the Audit Log
• Added consistent "Limpar" behaviour across filtered administration pages
• Improved deterministic ordering with secondary sort keys for stable pagination
• Expanded automated test coverage with new filtering, sorting and regression tests
• Completed manual verification against a real PostgreSQL database
v1.22.0 2026-07-20
CATDI v1.22.0

Highlights

• Added a reusable SearchProvider architecture for administration search
• Introduced a SearchService that orchestrates entity-specific search providers
• Added Global Search across Course Applications, Students, Certificates and Courses
• Added portable case-insensitive searching compatible with PostgreSQL and SQLite
• Added grouped search results with reusable result components
• Integrated Global Search into the administration portal
• Expanded automated test coverage with 31 new tests
• Completed automated and manual verification against a real PostgreSQL database
v1.21.0 2026-07-20
CATDI v1.21.0

Highlights

• Added immutable audit logging for key academic lifecycle events
• Introduced AuditLog model with append-only audit records
• Added event-driven audit listeners using the existing Domain Event architecture
• Added attendance and completion domain events with change detection guards
• Preserved historical actor identity using immutable actor snapshots
• Added a global Audit Log administration page with filtering and pagination
• Added a reusable entity timeline component, embedded on the application detail page
• Completed automated and manual verification of audit logging
v1.20.0 2026-07-20
CATDI v1.20.0

Highlights

• Added event-driven Enrollment Created notifications
• Added event-driven Certificate Issued notifications
• Introduced EnrollmentCreated and CertificateIssued domain events
• Added dedicated listeners and Laravel Notifications
• Added enrollment and certificate email templates using the shared email layout
• Student Portal is now the authoritative destination for enrollment details and certificate downloads
• Continued synchronous notification delivery
• Full automated and manual verification
v1.19.0 2026-07-19
CATDI v1.19.0

Highlights

• Introduced domain-event notification architecture
• Added event-driven application acceptance and rejection notifications
• Added notification listeners and Laravel Notifications
• Introduced shared email layout for migrated templates
• Preserved existing Mailables and email content
• Removed direct Mail:: calls from application status workflow
• Full automated and manual verification
v1.18.0 2026-07-19
CATDI v1.18.0

Highlights

- Introduced a real Admin Dashboard
- Dashboard KPI cards
- Six institutional reporting modules
- Admissions, Academic, Attendance, Completion, Certificates, and Student Progression reports
- Query-string report filtering
- Live aggregate reporting
- Updated admin sidebar navigation
- Protected by existing admin authentication
- Full automated and manual verification
v1.17.0 2026-07-19
CATDI v1.17.0

Highlights

- Academic transcript PDF generation
- Single transcript download from the Student Portal
- Aggregated academic history across completed enrollments
- Support for multiple completion outcomes
- Certificate number cross-referencing
- Portuguese transcript formatting
- Always-fresh generation (deliberately not cached, unlike certificates,
  since a student's history can change between downloads)
- Dedicated transcript templates
- Built on the existing secure PDF generation pipeline
v1.16.0 2026-07-19
CATDI v1.16.0

Highlights

- Professional PDF certificate generation
- Authenticated student certificate downloads
- Embedded QR code linking to public verification
- Generate-and-cache PDF architecture
- Dedicated certificate templates
- Secure ownership enforcement (403 for unauthorized access)
- Prepared foundation for transcripts and additional certificate templates
- Full automated and manual verification
v1.15.0 2026-07-19
CATDI v1.15.0

Highlights

- Introduced opaque verification tokens for certificates
- Added public certificate verification page
- Separated human-facing certificate numbers from technical verification identity
- Preserved existing certificate numbering
- Strengthened public verification security
- Prepared the platform for PDF generation and QR codes
- No changes to Student Portal architecture
- Full automated test coverage
v1.14.0 2026-07-19
CATDI v1.14.0

Highlights

- Enhanced Student Portal
- Current and completed course grouping
- Attendance summaries
- Completion status
- Certificate information
- Improved student academic visibility
- Maintained single-page portal architecture
- Preserved existing security model
- No new routes or database changes
- Full automated test coverage
v1.13.1 2026-07-19
CATDI v1.13.1 — patch

Fixes an already-authenticated user (admin or student) landing on the
generic /dashboard instead of their real home area when navigating
directly to /login, bypassing Sprint 13's role-based redirect.
v1.13.0 2026-07-19
CATDI v1.13.0

Highlights

- Role-based authentication redirects
- Persistent admin sidebar
- Complete Course Management UI
- Course CRUD
- Immutable automatic course slugs
- Breadcrumb navigation
- Removal of public registration
- Shared student-role detection
- Improved admin navigation architecture
- Published/unpublished routing fix
- 190 passing automated tests
v1.12.0 2026-07-19
Sprint 12: Certificate Management
v1.11.0 2026-07-19
Sprint 11: Course Completion
v1.10.0 2026-07-19
v1.10.0: Sprint 10 - Attendance Management

- New Attendance Session / Attendance Record concepts, nested under
  Course Intake
- AttendanceStatus backed enum as the single source of truth for the
  Present/Absent/Late/Excused vocabulary
- Bulk recording + in-place correction per session, with recorded_by/
  updated_by audit fields
- Student Portal unaffected (verified by regression test)
v1.9.0 2026-07-19
v1.9.0: Sprint 9 - Course Delivery Information

- CourseIntake gains delivery_mode, location, instructor, notes
- No new aggregate, no lifecycle - descriptive fields only
- New domain-oriented training config section in config/catdi.php
- Student Portal unaffected (verified by regression test)
v1.8.0 2026-07-19
v1.8.0: Sprint 8 - Course Intake Management

- New CourseIntake model - a scheduled delivery of a Course
- Enrollment re-scoped from (student, course) to (student, intake)
- Minimal admin CRUD for intakes (create, list, edit)
- Student Portal unaffected in substance - query path only
v1.7.0 2026-07-19
v1.7.0: Sprint 7 - Student Portal (Enrollments)

- New Os Meus Cursos section shows student enrollments
- Acceptance and enrollment shown as independent, non-collapsed facts
- Portal\CourseApplicationController renamed to Portal\DashboardController
- Strictly student-facing - no admin changes
v1.6.0 2026-07-18
v1.6.0: Sprint 6 - Enrollment Foundation

- Dedicated Enrollment model/table, distinct bounded context from
  CourseApplication (admissions record)
- Enrollment created only via deliberate staff action, never automatic
- (student, course) scope only - no Intake/Session modeling yet
- Student Portal deliberately unchanged this sprint
v1.5.0 2026-07-18
v1.5.0: Sprint 5 - Student Portal Academic Overview

- Dashboard sections accepted applications separately from full history
- Deliberately no enrollment, course dates, certificates, or curriculum
  surfaced yet - all correctly deferred to future sprints
v1.4.0 2026-07-18
v1.4.0: Sprint 4 - Student Authentication

- Apply -> Accept -> Invite account creation workflow
- course_applications.user_id links applications to student accounts
- MustVerifyEmail enabled for student-facing routes (Admin exempted)
- Student Portal dashboard (/portal): a student's own applications and
  status only, never reviewer identity or internal notes
v1.3.0 2026-07-18
v1.3.0: Sprint 1-3 - Training Catalogue, Course Applications, Admissions Management

- Sprint 1: Training Catalogue Foundation (database-backed courses, migrated from config)
- Sprint 2: Course Applications (public application form, spam protection, rate limiting, dual email notifications)
- Sprint 3: Admissions Management (staff-only /admin area, application review workflow, status transitions, accept/reject notifications)
v1.0.0 2026-07-18
CATDI Foundation Release