feat(enrolment): apply wedge spec via manifest + ADR-031 lifecycle handler (closes #23) - #31
Merged
Merged
Conversation
rubenvdlinde
force-pushed
the
feature/wedge/enrolment
branch
from
May 11, 2026 21:53
69f463c to
1e642c5
Compare
…ndler (closes #23) - Add Enrolment schema to scholiq_register.json: lifecycle (pending → active → completed | withdrawn | failed), x-openregister-relations (learner + course), x-openregister-calculations (isOverdue, daysRemaining, ragStatus), x-openregister-notifications (welcomeOnActivate, completionOnComplete, reminderT30/T7/T1 with idempotencyKey, managerAlertOnOverdue with HR-group fallback) - Create lib/Lifecycle/XapiCompletionHandler.php: single-method IEventListener; fires complete transition on active Enrolment when xAPI verb ∈ {completed,passed} AND lesson.mandatoryTraining=true AND lesson is final published lesson of course - Register XapiCompletionHandler in Application.php via IRegistrationContext::registerEventListener on XapiStatementReceivedEvent (ADR-031 legitimate exception) - Extend src/manifest.json with EnrolmentDetail (type=detail) and BulkEnrol (type=custom, component=BulkEnrolModal) pages - Create src/views/BulkEnrolModal.vue: 3-step modal — audience picker (NC groups OCS API + CSV), section+config (Course picker, mandatory toggle, dueDate), confirm+submit (POSTs directly to OR batch endpoint, polls progress via bulkJobId) - Register BulkEnrolModal in src/main.js via customComponents on CnAppRoot - No EnrolmentController, EnrolmentService, BulkEnrolmentService, EnrolmentDueReminderJob, enrolmentStore.js, or custom router entries
rubenvdlinde
force-pushed
the
feature/wedge/enrolment
branch
from
May 11, 2026 21:53
1e642c5 to
a746261
Compare
Contributor
Quality Report — ConductionNL/scholiq @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ⏭️ | ||||
| phpcs | ⏭️ | ||||
| phpmd | ❌ | ||||
| psalm | ❌ | ||||
| phpstan | ❌ | ||||
| phpmetrics | ⏭️ | ||||
| eslint | ❌ | ||||
| stylelint | ⏭️ | ||||
| composer | ❌ | ⏭️ | |||
| npm | ❌ | ❌ | |||
| PHPUnit | ❌ | ||||
| Newman | ❌ | ||||
| Playwright | ❌ |
Quality workflow — 2026-05-11 21:54 UTC
Download the full PDF report from the workflow artifacts.
Contributor
Quality Report — ConductionNL/scholiq @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ❌ | ||||
| stylelint | ❌ | ||||
| composer | ✅ | ✅ 100/100 | |||
| npm | ❌ | ❌ | |||
| PHPUnit | ⏭️ | ||||
| Newman | ⏭️ | ||||
| Playwright | ⏭️ |
Quality workflow — 2026-05-11 21:55 UTC
Download the full PDF report from the workflow artifacts.
rubenvdlinde
added a commit
that referenced
this pull request
May 12, 2026
…m manifest to nc-vue v1.4.0; drop dead scaffold files (#40) ## Root cause All 5 Wave-2 worktrees were branched off the same pre-Wave-2 base, so the sub-agents worked blind to each other. The dashboard agent wrote intentional STUB Enrolment + Regulation schemas ("declare a stub if it doesn't exist yet"). When the dashboard PR was cherry-picked LAST onto a development that already had #31's full Enrolment and #33's full Regulation, git's line-based JSON merge kept the stub (the schemas were declared at different file positions, so no textual conflict — git appended both and `json.load` resolved the duplicate key to the stub). The merge-resolver script was only re-run on manifest.json, not scholiq_register.json. ## Fixes - **Restore full Enrolment schema** (10 props + x-openregister + lifecycle pending→active→completed|withdrawn|failed + relations learner/course + calculations isOverdue/daysRemaining/ragStatus + notifications welcomeOnActivate/completionOnComplete/reminderT30/T7/T1/managerAlertOnOverdue) and merge in the dashboard's myMandatoryTraining widget. - **Restore full Regulation schema** (12 props + x-openregister + lifecycle draft→published→archived + aggregations mandatoryEnrolledCount/mandatoryCompletedCount/attestationCount/validCredentialCount + calculations coveragePercent/ragStatus + notifications officerAlertOnCoverageDrop/onPublished) and merge widgets coverageGrid+boardProof+attestationCount. - **Move `appendOnly: true` to top level** on Credential, XapiStatement, Attestation — OR's Schema::hydrate() only reads top-level `appendOnly` (it calls setAppendOnly()); when nested inside `x-openregister` the flag was silently dropped, so these schemas were NOT actually append-only. - **Conform src/manifest.json to nc-vue manifest schema v1.4.0**: removed `visibleIf` from pages, `public` from CredentialVerify, fixed dashboard widgets to {id,title,type:'custom'}, stripped non-schema keys (href, visibleIf) from the viewInMydash action. `tests/validate-manifest.js` now PASSes (was FAIL with 14 errors). - **Delete dead scaffold files**: src/router/index.js, src/views/Dashboard.vue, src/navigation/MainMenu.vue (none imported anywhere), lib/Controller/DashboardController.php (no route). Follow-ups: nc-vue needs `visibleIf` page predicates, `public` pages, and `widget-ref` page-content (umbrella #200) before the dashboard can be fully declarative; until then the dashboard pages are conformant-but-minimal.
This was referenced May 12, 2026
Closed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
lib/Settings/scholiq_register.json: full lifecycle (pending → active → completed | withdrawn | failed), relations toLearnerProfile+Course, calculations (isOverdue,daysRemaining,ragStatus), and declarative notifications (welcomeOnActivate,completionOnComplete,reminderT30/T7/T1withidempotencyKey,managerAlertOnOverduewith HR-group fallback). NoEnrolmentController,EnrolmentService,EnrolmentDueReminderJob, or app-local notification service — all replaced byx-openregister-*extensions per ADR-022 + ADR-031.lib/Lifecycle/XapiCompletionHandler.php— the one ADR-031-legitimate PHP file: singlehandle()method; listens onXapiStatementReceivedEvent(openregister.audit.xapi.statement.received); guards on verb ∈ {completed, passed} +lesson.mandatoryTraining=true+ lesson is the final published lesson of its course; callsObjectService::transition('Enrolment', $enrolmentId, 'complete'). OR then emits the audit entry andcompletionOnCompletenotification automatically.lib/AppInfo/Application.phpwiresXapiCompletionHandlerviaIRegistrationContext::registerEventListeneronXapiStatementReceivedEvent.src/manifest.jsonextended withEnrolmentDetail(type=detail, register=scholiq, schema=Enrolment) andBulkEnrol(type=custom, component=BulkEnrolModal).src/views/BulkEnrolModal.vue— 3-step modal: (1) audience picker via NC OCS/ocs/v2.php/cloud/groupsor CSV upload, (2) Course picker + mandatory toggle + dueDate, (3) confirm then POST directly toPOST /api/openregister/scholiq/Enrolment/batchwith browser-generatedbulkJobId; polls progress viaGET /api/openregister/scholiq/Enrolment?bulkJobId=<uuid>.src/main.jsregistersBulkEnrolModalincustomComponentsonCnAppRoot.ADR-031 legitimacy
XapiCompletionHandlerqualifies as a lifecycle guard exception: it is a cross-system bridge (xAPI LRS event → OR Enrolment transition) with conditional logic that cannot be expressed as a schema calculation. It has one method and delegates everything else to OR.Test plan
scholiq_register.jsonandmanifest.json(python3 -c "import json; json.load(open(...))")composer lint— all files passcompletedstatement to OR and verify the matching active Enrolment transitions tocompleted/enrolments/bulkin the app shell and walk through all 3 modal stepsdueDate = today+30, trigger OR's notification tick, assertreminderT30fires exactly once (idempotencyKey)dueDate = yesterday, assertisOverdue = trueandmanagerAlertOnOverduefires🤖 Generated with Claude Code