Skip to main content
The live known-issues table lives at the top of LMS_Frontend/major_code_updates.md (last reviewed: 07/10/2026 05 PM). This page groups those rows — plus the open “TO BE FIXED” items from the update entries — by priority. The #N markers are the row numbers in that table, so the two stay easy to cross-reference. This page was re-checked on 09/10/2026 against the 08/10/2026 clean-up (dead code deleted, every package bumped) and Updates 90–94, and on 19/10/2026 for Update 123 (rows #34 and #35 added, the chapter-quiz backfill closed) and Update 124 (row #36 added), and on 20/10/2026 for Update 129 (rows #40–#42 added; one more row is tracked privately in the frontend log), and on 01/11/2026 for Update 184 (rows #50 and #51, the R2 bucket folders, closed: see Recently resolved) and Update 185 (row #5 narrowed, row #55 added), and on 04/11/2026 for Updates 223–224 (row #7 narrowed, row #63 added; one more row is tracked privately in the frontend log). Notes marked 09/10 record what changed since the table was last reviewed; the table itself has not been updated yet.

🔴 Critical — security & data exposure

The endpoint checks neither that student_id is the caller nor that the training / exam was completed (CertificationPolicy::create returns true for everyone). The “finished” gate exists only in the Flutter UI — a logged-in user can issue a certification for an unfinished course, or for another user, by calling the API directly. Fix: server-side ownership + completion check in CertificationsController::store.
SendUnifiedNotificationRequest::authorize() returns true and the controller checks no role: any signed-in user, a student included, can notify any user, role, or the whole company. Fix: allow admins / instructors only, and limit an instructor to the learners of their own classrooms.
POST /classroom/create, PUT /classroom/update/{id}, archive / unarchive and DELETE /classroom/{id}/force_delete check no role (a student creating a classroom answers 201). update and force_delete also skip the company filter, so another company’s classroom can be changed or deleted. Update 73 only fixed the read endpoints. Fix: staff only (instructor limited to own classrooms) + where('company_id', …).
GET /training/{id} (TrainingPolicy::view is return true) still returns every lesson’s content and every quiz answer key, and GET /quiz/{id} is reachable because students hold QUIZZES_VIEW by default. The marketplace no longer calls them (Update 44), but anyone who types the URL still gets the data. Fix: make both staff / owner-only now that no student screen needs them.
Flagged by the knowledge-graph refresh (Update 89, source authed_user_and_attempts.md) and not yet re-checked against the code. Trace the endpoint through the providers and screens; if confirmed, promote it into the table above.

🚩 High — wrong behavior users can hit

StudentProvider.fetchPaginatedTrainings, the createCertificateApi call sites, their notification refresh and QuizProvider.uploadFile() all send company_id: 1 — wrong for any tenant other than company 1. Fix: read the company from the signed-in user (and grep for the remaining hardcoded instances — they’re latent multi-tenant bugs). 04/11: the notifications page now sends the signed-in user’s company_id (Update 223); the other call sites above are unchanged.
profile_side_widget.dart calls studentNavigationShell.goBranch(5) — the cart branch, not Settings (6). Fix: navigate like the app-bar avatar does (setSelectedIndex(6) + goBranch(6) + the settings sub-branch). 09/10: the file was deleted on 08/10/2026 by the dead-code clean-up (Section D — nothing imported it), so the button no longer exists anywhere in the app. The source table still carries the row; it can be closed.
myTrainings is built at login from orders[].trainings, whose relation deliberately drops the archived scope — so an archived training still shows on My Trainings, the dashboard cards and the overview counts (Update 82 only fixed the Certificates “In progress” list), and the two data sources disagree until a refetch. Fix: decide the product rule, then filter archivedAt == null where myTrainings is built — or scope the login resource.
The “congratulations” notification is inserted outside the transaction; if it throws, the app shows “Fail Creation Certificate” for a certificate that exists (a retry returns it with already_exists: true). Fix: guard the notification (try/catch or queue).
DELETE /classroom/{id}/force_delete hides archived classrooms in its lookup, so it answers 404 — yet the admin table only offers Delete on archived classrooms. Fix: look up with withArchived() (plus the #21 company filter).
When GET /classroom/{id} fails (403 for a non-member, 404, network), getClassroomById shows the error dialog and returns null — the page keeps its loading spinner forever behind it. Fix: an error state with a back button.
The Archivable scope cannot return active + archived together, so the frontend builds the “All” view from two paginated lists: 2–3 requests per page and active rows always sorted before archived ones. Fix: accept archive_status=active|archived|all and return archived_at in every row (see Suggested Changes).

⚠️ Medium — gaps and performance costs with workarounds

The catalog glass cards hide the quizzes stat rather than under-count it. Adding total_quizzes_count (chapter quizzes + final quiz) lights it up with no Flutter change.
Every rebuild / tab switch recreates the five tab bodies: CertificatesScreen refetches /certifications/by-user and the My Trainings search field loses its text. Fix: stable keys (or none).01/11/2026 (Update 185): My Trainings and My Exam Simulator now have stable keys (their search, category filter and groups survive rebuilds). Only Certificates and Exam Attempts still use UniqueKey().
The training preview’s lesson dialog, question explanations (question_explanation_view.dart) and the admin add-training preview render HTML outside the shared renderer — a YouTube iframe inside a question explanation would show “Error 153” on mobile. LessonContentScreen is a fourth, unused copy. Fix: migrate them; delete LessonContentScreen. 09/10: LessonContentScreen was deleted on 08/10/2026 (dead-code Section D); the three live renderers still need the migration.
The card leaves out the template’s “This week” pill and “Level – Tier” line because the API has neither a weekly window nor a level. Don’t fake them client-side (see Suggested Changes).
The stats card shows “Avg. N% complete” but cannot show ”· 2 touched this week”. Fix: add the pivot created_at to the user’s lessons.
With the keyboard open the page stops ~90px above it (bottom bar + system inset counted twice, cause not yet isolated). With a tall keyboard on a 320px display the search field is hidden while typing. Fix: find which scaffold applies the keyboard inset under the bottom bar and drop one of the two.
CertificateImageService draws with GD, which cannot shape right-to-left scripts — Arabic certificates fall back to the training badge in link previews. Fix: bundle an Arabic font and shape the text, or render that case with a headless browser.
GET /training/{id} ships every lesson’s full HTML up front (dominant initial-load cost), and “Mark as complete & continue” waits on a full GET /user/{id} refetch. Both fixes are specced in Suggested Changes.
The 7.2 MB CanvasKit engine ships raw and nothing is cacheable, so first paint takes seconds even on good internet. The nginx directives are written but not yet applied on the vhosts (the single biggest perf win available).
filteredLearners / filterLearners / searchController are unread since Update 69 (the members panel keeps its own search). No effect today; remove in a cleanup pass.
POST /chapters/import creates the chapter and its lessons only: the quizzes of an exported chapter file (quizzes, or quiz in older files) are not imported, so exporting a chapter and importing it into another training loses its quizzes. Fix: import each entry of quizzes[] with the quiz import code of Export_Import_Quizzes_Exams_Controller (row added 19/10/2026, Update 123).
Since Update 123 a chapter holds several quizzes, shown after its lessons in creation order. There is no quiz position or reorder endpoint, and lessons and quizzes cannot be interleaved; the training editor lists the quizzes read-only. Fix: quizzes.position + POST /quizzes/reorder, then drag handles on the curriculum’s quiz rows.
27 controllers run validate() inside try { … } catch (\Throwable $th), which returns a 500 with the message instead of the 422 with per-field errors. The app then shows a server error for a bad input. Update 124 fixed GET /trainings and GET /quizzes. Fix: rethrow ValidationException before the \Throwable catch, or validate before the try (row added 19/10/2026, Update 124). 20/10: POST /classroom/create and PUT /classroom/update/{id} fixed too (Update 131, the slug work).
The chatroom code shipped in Update 129, but no server runs php artisan reverb:start and BROADCAST_DRIVER is still log, so the apps poll every 5 seconds instead of receiving messages instantly. Fix: Reverb keys + BROADCAST_DRIVER=reverb, a supervised reverb:start, and the nginx /app/ proxy (Chatrooms Setup Guide, section 5).
The paperclip accepts images up to 20 MB, but PHP’s default upload_max_filesize / post_max_size and nginx’s client_max_body_size are much lower, so a large image fails. Fix: 25M (PHP) and client_max_body_size 25m; (nginx) in Plesk (Chatrooms Setup Guide, section 3).
TO BE FIXED (backend), found 01/11/2026 (Update 185). POST /trainings/export-import/import builds the slug from the raw label with a lower-case-only pattern before lowercasing it and cuts it to 13 characters, so “Imported training” becomes -mported-trai; a taken slug gets a random suffix instead of -2, -3. Imported trainings get broken URLs and R2 folders. Fix: use Training::generateUniqueSlug($trainingData['slug'] ?? $trainingData['label']), like POST /training.
To check (backend), found 04/11/2026 (Update 224). The app now saves phone_number as “+216 98378579” (country code, one space, digits). The Konnect init payload sends it as saved; Konnect’s expected format was not checked and no payment was tried. If Konnect expects 8 local digits, its checkout may show the number wrong or refuse it. Fix: check against Konnect; if needed, send only the digits after “+216 ” for a Tunisian number (nothing for a foreign one).
chatrooms:prune-attachments (unsent chatroom images) and notifications:delete-expired only run with an every-minute php artisan schedule:run cron, which no deployment note sets up. Fix: add the cron in Plesk → Scheduled tasks (Chatrooms Setup Guide, section 3).

🔵 Low — cosmetic, noise and technical debt

flutter test always ends with one “failed to load” error. Fix: delete the file or restore a real test. 09/10: still the only failure — 436 tests pass otherwise (Update 94).
Both pages read ExamProvider.showSideSheetExam for their Filters side sheet, so a sheet opened on one tab is still open after switching to the other. Pre-existing, but visible now that the two are neighbouring Marketplace tabs. Fix: one flag per tab, or close the sheet on a tab change.
The new message suggestion sends a learner their login details, so the password lands in the notifications table, in the admin Recent log and in the learner’s in-app notifications (and in any future BCC archive mailbox). Nothing to fix in code — use it knowingly, and replace the xxxyyyzzz placeholders before sending.
exam_card_with_progress.dart calls updatePageIndex(2) without going there; since Update 59 the URL decides the tab, so the hint is overridden. Cosmetic. Fix: drop the call or use goToMyContentTab.
Full breakdown and fix order in major_code_updates.md Update 45. The notable buckets: 13 use_build_context_synchronously (real “widget disposed” crash risk — the Konnect payment flow first), 38 Radio → RadioGroup, 5 ShowCaseWidget (removed in showcaseview v6 — migrate before upgrading), 7 onReorder → onReorderItem (⚠️ remove the manual index adjustment when migrating), 12 Matrix4 calls, 12 unused imports, 6 experimental flutter_quill APIs. 28 of the 116 are dart fix-able. 09/10: after the 08/10/2026 package bump and dead-code batch, flutter analyze reports 0 errors / 10 warnings / 134 infos: the 75 deprecations and 13 use_build_context_synchronously are unchanged, and flutter_lints 6 added 53 strict_top_level_inference infos (members without a declared type, mostly in providers such as chapter_provider.dart). The ShowCaseWidget / Radio / onReorder buckets above still apply.
CertificationsController.php and the three resources/lang/*/messages.php carry pre-existing formatting findings. No runtime effect. Fix: a formatting-only commit.
“Sami Z.” inside RTL text draws as ”.Sami Z” — standard bidi behavior. Fix: give name texts their own text direction (or a bidi isolate).
The compact completed-training card’s gold button truncates on Galaxy-S-width / zoomed displays. Fix: shorter label or smaller font in the compact card.
Help Center and learner analytics are laid out at 1235px+, the exam-taking pages at 1458px, inside sideways scroll views. Fix: size from the real width, as My Content does since Update 76 (Help Center is shared with the staff roles).
FilledButton.styleFrom(textStyle: …) without fontFamily replaces the theme style — ~12 styles in 5 files would render in the browser’s fallback font. Fix: add fontFamily: 'Poppins'.

⚠️ Verification debt (#11)

Updates 53 to 69 (and most later UI work) are verified by widget tests only — the glass cards’ real scrolling cost, the certificate flows, the tab URLs, the remade leaderboard / continue-learning / progress cards, the classroom notify dialog and more have not been looked at in a real browser or on a device, and the real POST /certification round trip runs through a fake client. Updates 90 to 94 (the Marketplace Instructors tab, the avatar-menu language row, the instructor pager arrows, the re-colored admin reminders page) and the 08/10/2026 package bump were likewise verified through tests and the test renderer only — and the bump still lacks an iOS build and a manual pass on the screens whose packages changed (drag-and-drop questions, uploads, YouTube fullscreen on web, 2FA recovery-codes download, the three dropdowns, certificate download on Android).
A manual pass on web and on a phone is the standing action item: visual or timing problems would not show in the tests. Many individual update entries end with “Not checked in a real browser” for exactly this reason.

✅ Recently resolved (for context)

  • #50 R2 uploads landing flat in files/ whatever folder the app named, and #51 the three backend-only folders outside the layout (chatrooms/{slug}/, files/conversations/, files/instructor_background_items/) → closed on 01/11/2026 (backend R2StoragePathService + Update 184): the backend builds every key from the ids the app sends (training_id, lesson_id, classroom_id, quiz_id), one folder per training / classroom / quiz / chatroom slug under files/; a folder string from a client is never trusted; older objects keep their keys. See R2 Storage Paths.
  • Login payload bugs B1 – B4, B6, B7 of the Auth User Optimize plan → closed by Update 125 (19/10/2026): progress and attempt ceilings come from the backend, the Attempts tab loads its rows on open, certificates carry training_id / quiz_id at boot.
  • #33 stale page-title sizes in student_web_responsive_test.dart → the expectation was aligned with the code in Update 125.
  • GET /auth/user payload size (1.5 MB, the “App Speed” row 2) → Update 125 (~20 KB, 28 queries).
  • Chapter quiz backfill (Update 34 workaround) and one quiz per chapter → closed by Update 123 (19/10/2026): chapters carry quizzes[] and keep every quiz, and the backfill was deleted. The quiz missing under lessonless chapters was a draft quiz, which the backfill exposed through GET /chapter/{id}; published quizzes always came back from GET /training/{id}.
  • #6 My Content forced to 1536px → fixed by Update 76.
  • #18 Classroom by-student payload exposure → fixed by Update 73 (whitelisted persons).
  • #28 Certificates showing the default badge → fixed by Update 82 (training.badge_logo eager-loaded).
  • #20 Dashboard “Edit profile information” opening the cart → the widget was deleted in the 08/10/2026 dead-code clean-up (removed, not fixed); the table row can be closed.
  • Marketplace leaks (lesson content / answer keys / instructor PII in student network tabs) → closed by Updates 39 + 44 (preview-* endpoints) — the hardening of the old endpoints remains critical (see above).
  • Exam views always 0 → fixed by Update 47 (quizzes.views_count migration — remember to run php artisan migrate on each backend).