LMS / Learning platform / 2026
Bringing lesson-grounded AI into an LMS.
A Laravel platform with reviewed course revisions, progress checked on the server, and AI help drawn from lessons each learner can access.
- ROLE
- Software engineering
- STATE OF THE BUILD
- In progress
- RECORD UPDATED
- PUBLIC WEBSITE
- Visit website
THE WORK
The project.
LMS gives learners AI help with the course they're actually taking. They can ask questions about a lesson and use generated study guides in the same application. Laravel retrieves permitted lesson material for generation, while the linked LMS AI Core service reranks passages and checks generated claims. Permissions, source versions, provider calls and the final learner response stay in Laravel.
That connection to the lesson continues after generation. A saved guide must match the current material and generation settings before it can be shown. A compatible ready version can remain available when an older generation record says unavailable; an outdated guide is withheld.
The wider platform covers course authoring, review, enrollment and lesson progress. Instructors prepare separate revisions, and publication locks the course and revision inside one transaction. The lesson player reports progress, but the server decides what to accept using media-time limits and recorded reading-page dwell.
ATTEMPTS & CORRECTIONS
What changed along the way.
Extending the self-moderation rule to delete actions
- At first
- Reply, approval and status actions already checked whether an instructor was acting on a thread from their own learner account. The two delete actions stopped after checking that the record belonged to one of the instructor's courses.
- What needed to change
- An instructor could therefore delete their own learner thread or a reply inside it. The first regression run stopped before the controller because the test application key was missing. With a test key in place, the requests returned 200 and reproduced the defect.
- The change
- Both delete paths now apply the existing self-moderation guard after resolving the thread or reply. The regressions require a 422 response and an undeleted record, so returning an error while still deleting the data cannot pass.
- The result
- The May record reports the focused Q&A tests passing with nine assertions and environment warnings. The repair extends an existing action rule to the missing paths; it doesn't add another role or permission system.
Applying the sanitizer to older stored HTML
- At first
- New announcements and newsletters used the shared sanitizer when saved. The player, editor preview and mail templates then rendered the stored body directly.
- What needed to change
- Older or imported HTML hadn't necessarily passed through that save handler. The newsletter preheader also stripped tags from an unsanitized stored value. Checking new saves didn't cover either route into the output.
- The change
- The existing sanitizer now runs in those announcement and newsletter views, including the preheader source. Tests bypass the save handler by writing malicious HTML straight into records, then check that useful text remains while scripts, event handlers and scriptable URLs are removed.
- The result
- The May record reports passing content feature tests and sanitizer unit tests, with environment warnings in the feature group. Those checks cover the identified rendering paths. They don't establish that every rich-text output was audited, and the change doesn't alter upload storage or provider behavior.
Making reading and resource completion a server decision
- At first
- Reading and resource progress handlers accepted a completion-ready flag from the browser. Reading used one start time to calculate dwell, and the resource timer could start inside the progress request itself.
- What needed to change
- The submitted flag could say ready without the server having recorded resource access or enough time on each reading page. A client update was participating in the decision that authorized completion.
- The change
- The handlers no longer use that browser flag for reading and resources. Lesson access records the start, accepted reading pages get server timestamps, and every page must meet the configured dwell time. A resource needs recorded access, nonzero progress and its own dwell check before Laravel sets completion_ready_at.
- The result
- The regressions submit completion_ready=true too early and expect readiness to stay false and completion to return 422. They then advance the clock and check the permitted path. The May hardening report records the broader non-AI suite passing with environment warnings. These checks establish completion eligibility, not understanding.
IMPLEMENTATION
Design choices.
- Proposed course changes live beside the approved course until review is complete. The publication service locks both records and copies an explicit set of fields and relationships in one transaction. That keeps unfinished edits out of the approved content, although recreating lesson IDs leaves progress-history continuity as separate work.
- Playback position and accepted progress are different values. Laravel constrains media increases, advances reading pages in sequence and checks dwell before setting completion readiness. Those rules make completion a server decision; they don't prove attention or understanding.
- The AI service has a narrow job. Python ranks supplied passages and checks claims, while Laravel retains enrollment, source history, provider calls and response policy. Bounded HTTP calls and local fallback keep the integration explicit, with the cost of maintaining two verification implementations and another possible failure path.
- Rich text is checked when written and at selected points where stored content is rendered. The same rules can therefore cover old or imported bodies as well as new editor output. If the parser fails, escaped text replaces formatted HTML: the fallback preserves the text but gives up its formatting.
- Guide readiness comes from a compatible saved version, not only the latest generation artifact. Provider, model, policy and source-fingerprint checks let an eligible guide survive an older failed attempt without showing stale content. Historical versions remain records, not automatic candidates for learner display.
HOW IT FITS TOGETHER
Architecture.
- Instructors organize media, documents and reading material into courses, chapters and lessons through Laravel controllers and Blade/JavaScript interfaces. CourseRevisionWorkflowService gives proposed edits their own records. Publication locks the course and revision, checks that they belong together, and applies the reviewed content in one transaction.
- Access follows the course records, not just a role name. Learner requests resolve an enabled enrollment, then the chapter and lesson inside that course. Instructor requests use the primary or assigned instructor relationship, followed by action checks such as refusing moderation of the same person's learner thread.
- The player sends position and progress updates to Laravel. The server caps media increases against elapsed time, configured playback allowances and duration. Reading advances page by page, with server-recorded dwell required before completion, so a browser-supplied completion flag doesn't settle the result.
- Laravel turns lesson material into versioned sources and retrieves context within the learner's permitted courses. Provider interfaces handle generation. The LMS AI Core gateway can call Python for ranking and claim checks, while Laravel interprets the result, filters citations and decides whether to return an answer, review suggestions or an unavailable state.
Explore the architecture map9 components · 8 connections
Course publishing, learner progress and AI study support
Laravel manages course content, access and progress, and connects permitted lesson material to the AI workflow.
Application entry points
Platform services and state
Review and content boundaries
Scroll or drag the background to move. Use the zoom buttons to resize.Arrow keys move between components. Enter selects.
Choose a component to explore
Select a numbered component on the map or use the component menu. Its details and connections will appear here.
No component selected.
All connections (8)
- 01 · Authoring controllers → 04 · Course revision serviceResolve editable revision · Direct call
- 04 · Course revision service → 07 · Reviewed publication gateRequest reviewed publication · Direct call
- 07 · Reviewed publication gate → 05 · Course, enrollment and progress recordsApply transactional curriculum writes · Direct call
- 02 · Learner controller → 05 · Course, enrollment and progress recordsCheck access and accept progress · Direct call
- 05 · Course, enrollment and progress records → 08 · Rich-content sanitizerRender stored rich content · Direct call
- 03 · Administrative workflows → 09 · Role and action permissionsAuthorize requested operation · Direct call
- 09 · Role and action permissions → 05 · Course, enrollment and progress recordsPermit scoped mutations · Direct call
- 02 · Learner controller → 06 · Linked LMS AI Core buildRequest permitted AI features · Direct call
MADE WITH
PHP · Laravel · Blade · JavaScript · Eloquent
ACCESS & INPUT
Security controls.
- Administrative CRUD permissions, role management and course access answer different questions. Role assignment stays Super Admin only. An instructor assigned to a course must still pass the self-moderation check before acting on a thread created through their learner identity.
- The rich-text sanitizer keeps allowed formatting while restricting tags, attributes, styles and URL schemes. It runs on new content and selected announcement and newsletter output paths, so older stored HTML doesn't depend on having passed the current editor.
- Uploads get generated filenames, executable-type restrictions and raster re-encoding. Controller downloads also check enrollment, the downloadable flag and managed paths. A direct public-disk URL doesn't pass through those controller checks and remains a separate storage limitation.
- Named rate limits, session regeneration, configured cookie settings and baseline response headers are present in the implementation. Their current deployed values haven't been checked, so the source alone doesn't establish the settings of a running installation.
- The separate, uncommitted Enrollment Manager work lets staff create learners and assign courses without granting broader access. Requests without access-management permission cannot set role, access_roles or audience_ids, even if those fields are submitted outside the UI.
- AI query logs and saved learner messages share sensitive-text redaction but use different length limits. Portable evaluation reports omit selected full responses, payloads and errors. These controls reduce retained text; they don't establish complete anonymity.
Explore the security map8 components · 7 connections
Checking who can change a record and what HTML can render
Role, course membership and action checks control writes. A separate sanitizer checks stored rich text at selected outputs.
Requested operation
Authority checks
Mutation and rendering
Scroll or drag the background to move. Use the zoom buttons to resize.Arrow keys move between components. Enter selects.
Choose a component to explore
Select a numbered component on the map or use the component menu. Its details and connections will appear here.
No component selected.
All connections (7)
- 01 · Signed-in learner, instructor or administrator → 03 · Role and permission boundaryCheck requested permission · Direct call
- 02 · Requested course, chapter, lesson or thread → 04 · Relationship-scoped accessResolve authorized relationship · Direct call
- 03 · Role and permission boundary → 04 · Relationship-scoped accessContinue with permitted operation · Direct call
- 04 · Relationship-scoped access → 05 · Action-specific guardCheck action-specific conflict · Direct call
- 05 · Action-specific guard → 06 · Permitted data mutationAllow guarded mutation · Direct call
- 07 · Stored author-created HTML → 08 · Shared output sanitizerTreat stored markup as untrusted · Direct call
- 06 · Permitted data mutation → 07 · Stored author-created HTMLPersist authored content · Direct call
SELECTED DEVELOPMENT RECORD
Engineering changes.
Implementation dates come from project records. “Recorded” is the date this portfolio entry was written.
Checking older HTML when it reaches the pagefixedRecorded
Announcement and newsletter output now uses the shared sanitizer, including content that bypassed the current save handler.
Source observedChanged
Closing the missing check in discussion deletesfixedRecorded
Thread and reply deletion now apply the existing self-moderation rule. The regressions check both the refusal and that the record remains undeleted.
Source observedChanged
Preparing course changes in a separate revisionaddedRecorded
Proposed edits now stay separate until reviewed publication applies them together. Publication still recreates lesson rows, so references to their old IDs need separate handling.
Source observedChanged
CHECKS & RESULTS
What was checked.
A source review shows what the code does. It is not a fresh test of the running app.
Portfolio source evidence refreshedReviewed
All 21 allowlisted implementation and test files match the earlier source snapshot. The case study covers course structure, progress, revisions and study-guide readiness within that scope. Tests were not run and live availability was not verified.
- Scope of this check
- Source and test-definition review; no runtime checks
Learning architecture and study-guide state inspectedReviewed
The inspected implementation includes structured learning content, progress handling, and version-aware study-guide readiness. The private evidence inventory maps these statements to exact source locations. This observation is not runtime verification.
- Scope of this check
- Repository source inspection
Readiness test definitions inspectedReviewed
A focused case constructs an unavailable older artifact and a current ready version, then expects an available learner response. Other cases describe missing, queued and older-policy content. No fresh passing result is claimed.
- Scope of this check
- Test source inspection; tests not run
Code and development records reviewedReviewed
The code, tests and available development records were checked. Reported test results keep their original scope. No fresh app or live deployment check was run for this write-up.
- Scope of this check
- Code review, not a fresh run of the app
NEXT
Still to work through.
Course publication is now one transaction, but it still recreates chapter and lesson IDs. Progress continuity across those replacements hasn't been established. Public-disk media also needs a separate storage decision, and the restricted Enrollment Manager role is still local and unreleased. The test results described here come from dated development records, not a fresh check of the running LMS.