> ## Documentation Index
> Fetch the complete documentation index at: https://theteam-13fc7f49.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Backend Clean-Up (Laravel)

> The repository clean-up plan for LMS_Backend — repo weight, duplicated/abandoned composer packages with versions, code-style debt, and the full backend updates & optimizations backlog.

Mirror of **`LMS_Backend/clean_up.md`** (generated 08/10/2026; Section H added 09/10/2026). Scope: what bloats the repo, what
is tracked but regenerable or junk, which composer packages are unused / abandoned / outdated, and
the backend updates & optimizations already flagged across `LMS_Frontend/major_code_updates.md`.
Nothing has been executed yet unless marked ✅. Statuses re-checked against the code on 06/11/2026.

***

## Why the repo is heavier than it needs to be

The tracked tree is **\~22.2 MB across 935 files** (pack \~13.4 MiB):

| Tracked content | Size | Files | % of tree |
| - | -: | -: | -: |
| `graphify-out/` (knowledge-graph output + caches) | **12.1 MB** | **538** | **\~55%** |
| `public/` (mostly `images/`) | 4.6 MB | 20 | 21% |
| `resources/` (views, certificate fonts) | 2.6 MB | 44 | 12% |
| `app/` | 1.4 MB | 147 | 6% |
| root files | 0.7 MB | 19 | 3% |
| `database/` | 0.5 MB | 110 | 2% |
| everything else | \~0.3 MB | — | 1% |

* **`graphify-out/` IS the problem**, exactly like it was in the frontend repo: the ignore rule
  exists in `.gitignore` but is **commented out** (`#graphify-out/`). 538 files including a
  2.3 MB `graph.json` and a 2.0 MB `graph.html` — regenerable local tool output.
* **10 `.DS_Store` files are tracked** (root, `app/`, `app/Http/`, `database/`, `public/`,
  `public/images/`, `resources/`, `resources/views/`, `storage/`, `tests/`).
* `vendor/` (158 MB) and `node_modules/` (34 MB) are correctly ignored — nothing to do there.

***

## A. Untrack + gitignore (biggest win, zero risk) — ⬜ pending

| Path | Size | Action |
| - | -: | - |
| `graphify-out/` (whole folder, 538 files) | 12.1 MB | Uncomment `graphify-out/` in `.gitignore`, `git rm -r --cached graphify-out` |
| `.DS_Store` ×10 | \~0.1 MB | Add `.DS_Store` + `**/.DS_Store` to `.gitignore`, `git rm --cached` them |
| `@php` (empty stray file at root — looks like a shell-redirect accident) | 0 KB | Delete |

**Estimated saving: \~12.2 MB and \~550 files (≈55% of the tracked tree).**

```gitignore theme={null}
# macOS Finder junk
.DS_Store
**/.DS_Store

# graphify knowledge graph output (uncomment the existing line)
graphify-out/
```

***

## B. `public/` images — unreferenced files + oversized referenced ones

### B.1 Zero references in `app/`, `database/`, `resources/`, `config/`, `routes/` (\~560 KB)

**Verify against the live databases before deleting**: a public image can be referenced by a URL
stored in an existing DB row (platform / landing settings, partner rows) even when no code or
seeder mentions it anymore.

| File | Size | Note |
| - | -: | - |
| `public/images/featured_category_image.png` | 188 KB | No code/seeder reference |
| `public/images/featured_category_image_2.png` | 188 KB | No code/seeder reference |
| `public/images/partner_company_logo_2.png` | 68 KB | No code/seeder reference |
| `public/images/partner_company_logo.png` | 52 KB | No code/seeder reference |
| `public/images/old_default_company_image.png` | 32 KB | The name says it: superseded |
| `public/company.png` | 32 KB | No reference anywhere |
| `public/images/default_company_image.png` | — | ⚠️ No code reference found either, but "default company image" smells DB-referenced — check before touching |

### B.2 Referenced but oversized — compress / convert (keep the file names)

The landing-page defaults (`LandingPage` model, seeder and observer all point at them) — keep the
paths, shrink the bytes:

| File | Size | Referenced by |
| - | -: | - |
| `public/images/background_image.png` | **2.9 MB** 🔴 | LandingPage model / seeder / observer, migration default |
| `public/images/header_image.png` | 484 KB | idem |
| `public/images/banner_image.png` | 348 KB | idem |

A 2.9 MB PNG served as a landing background is also a **runtime cost** for every visitor, not just
a git one. Compressing these three should save \~3 MB in-repo **and** on the wire.

### B.3 Keep as-is

* `resources/fonts/certificate/Montserrat-{Light,Regular,SemiBold,Bold}.ttf` (\~1.8 MB) — required
  by `CertificateImageService` (the GD-drawn `og:image` share previews, Update 83). An **Arabic**
  font will have to join them when the RTL-shaping issue (known issue 30) is fixed.
* `public/images/platform_logo.png`, `verify_mail_image.jpg`, `favicon.png` — referenced by
  mails, PDFs and blade views.

***

## C. Root-level clutter (tidiness only) — ⬜ pending

| File | Suggestion |
| - | - |
| `@php` (empty) | Delete (Section A) |
| `GlobalClassDiagram.puml`, `GlobalUseCaseDiagram.puml` | Move to `docs/` (create the folder) |
| `future_modifs_ReadMe_2.md` | Move to `docs/` or fold into this documentation repo |
| `readme_new_added_features.md` | Move to `docs/` or fold into this documentation repo |
| `Certification_Proxy_README.md`, `CLAUDE.md`, `README.md` | **Keep at root** — referenced by agent onboarding and the proxy setup |

The `x_README_API_Documentation/` folder **no longer exists** in the backend repo (neither tracked
nor on disk), yet **the backend's own `CLAUDE.md` §9 still lists 14 of its files** as the endpoint
documentation to consult, and this documentation repo's `CLAUDE.md` points to it too. Either
restore the folder (e.g. under `docs/`) or update both `CLAUDE.md` files — today those references
send a developer (or an AI agent) to files that are not there.

***

## D. Composer packages — unused, duplicated, abandoned

### D.1 ✅ Duplicated / dead CORS packages (remove first)

✅ **DONE 19/10/2026 — one CORS middleware, no abandoned package.** `\Fruitcake\Cors\HandleCors` left
`app/Http/Kernel.php`, and `barryvdh/laravel-cors` + `fruitcake/laravel-cors` left `composer.json` and
`composer.lock` (Update 126). Only Laravel's own `HandleCors` runs now, reading `config/cors.php`.
Checked locally on 06/11/2026: `OPTIONS` preflights and a real `GET` on `/api/*` each answer with one
`Access-Control-Allow-Origin` header. `fruitcake/php-cors` stays in `composer.lock` on purpose: it is
the library `HandleCors` calls, a dependency of `laravel/framework`, not one of the abandoned packages.
The text below is the original finding. **Still to do:** the `minimum-stability` bonus (Section G.3).

`composer.json` requires **both** `barryvdh/laravel-cors` **and** `fruitcake/laravel-cors`, both
pinned to the unstable `dev-develop` ref, and **both are officially abandoned** (composer prints
the warning on every install). Laravel 10 ships its own CORS middleware, and `app/Http/Kernel.php`
currently stacks **two CORS middlewares** globally:

```php theme={null}
\Illuminate\Http\Middleware\HandleCors::class,   // framework — keep this one
// ...
\Fruitcake\Cors\HandleCors::class,               // abandoned package — remove
```

**Action:** drop `\Fruitcake\Cors\HandleCors::class` from the Kernel, remove **both** packages,
keep `config/cors.php` (the framework middleware reads the same file). Bonus: with the two
`dev-develop` constraints gone, `"minimum-stability": "dev"` can likely return to `"stable"`.

⚠️ Re-verify CORS afterwards (`/api/*` preflights from the web app; remember `/storage/*` images
are answered by **nginx**, not Laravel — Update 19).

### D.2 Zero usage found — removal candidates

| Package | Installed | Evidence |
| - | - | - |
| `php-open-source-saver/jwt-auth` | 2.8.2 | Only `app/Models/User.php` (the `JWTSubject` interface + 2 methods) and `config/jwt.php`. **No `jwt` guard in `config/auth.php`, no `auth:api` route, no `JWTAuth::` call anywhere** — Sanctum is the real auth. Remove interface + methods + config + package |
| `torann/geoip` | 3.0.9 | `config/geoip.php` only — zero usage in `app/` or `routes/` |

⚠️ After each removal: `composer update --lock`, boot the app, run the test suite.

### D.3 Keep (verified in use)

| Package | Used by |
| - | - |
| `laravel/sanctum` | The API auth (`auth:sanctum` everywhere) |
| `pragmarx/google2fa` + `bacon/bacon-qr-code` | TOTP 2FA + QR rendering |
| `barryvdh/laravel-dompdf` | Every PDF: invoices / order PDFs / instructor exports (`UsersController`, `OrdersController`, `InstructorDashboardController`) and, since 24/10/2026, the admin dashboard export (`DashboardsController::downloadMinimalDashboardPdf`) |
| `league/flysystem-aws-s3-v3` | Cloudflare R2 storage |
| `rappasoft/laravel-authentication-log` | `User` model trait + config (login logging) |
| `guzzlehttp/guzzle` | HTTP client (Konnect, etc.) |

**DONE 24/10/2026 — one PDF engine.** `barryvdh/laravel-snappy` (with `knplabs/knp-snappy`),
`config/snappy.php`, the `PDF` → `SnappyPdf` alias and the `WKHTML_PDF_BINARY` / `WKHTML_IMG_BINARY`
variables are removed. `admin_dashboard_pdf` now renders with dompdf: its three flexbox rules became
tables and the fixed footer moved into the bottom page margin (checked on a 5-page sample). Guarded by
`tests/Feature/AdminDashboardPdfTest.php`. The wkhtmltopdf binaries can be uninstalled from the
servers once this code is deployed: see [Wkhtmltopdf removal](https://theteam-13fc7f49.mintlify.site/clean-up/wkhtmltopdf-removal.md).

### D.4 Version freshness — installed vs latest (`composer outdated --direct`, run 08/10/2026)

The Installed and Latest columns stay the 08/10/2026 snapshot; the Status column was re-checked against
`composer.lock` on 06/11/2026.

| Package | Installed | Latest | Status |
| - | - | - | - |
| `laravel/framework` | 10.49.0 | 13.35.0 | ✅ **UPGRADED 25/10/2026 → 12.70.1**, patched to 12.70.2 on 01/11/2026 (was 🔴 Laravel 10 end-of-life, security fixes ended 02/2025) |
| `laravel/sanctum` | 3.3.3 | 4.3.3 | ✅ **UPGRADED 25/10/2026 → 4.3.3** (with the framework) |
| `phpunit/phpunit` (dev) | 10.5.55 | 13.4.1 | ✅ **UPGRADED 25/10/2026 → 11.5.57** (with the framework; 12+ needs PHP 8.3, the TEST server runs 8.2) |
| `nunomaduro/collision` (dev) | 7.12.0 | 8.9.5 | ✅ **UPGRADED 25/10/2026 → 8.9.5** (with the framework) |
| `laravel/tinker` | 2.10.1 | 3.0.2 | ⬆️ major behind |
| `guzzlehttp/guzzle` | 7.10.0 | 8.2.0 | ⬆️ major behind |
| `pragmarx/google2fa` | 8.0.3 | 9.1.0 | ⬆️ major behind |
| `rappasoft/laravel-authentication-log` | 3.0.0 | 6.1.1 | ✅ **UPGRADED 25/10/2026 → 6.1.2** (v3 only supports Laravel 10; upgrade migration + v6 config) |
| `bacon/bacon-qr-code` | 3.0.1 | 3.1.1 | 🔸 minor behind |
| `barryvdh/laravel-dompdf` | 3.1.1 | 3.1.2 | ✅ **UPDATED 19/10/2026 → 3.1.2** |
| `barryvdh/laravel-snappy` | 1.0.4 | — | ✅ **REMOVED 24/10/2026** (D.3) |
| `league/flysystem-aws-s3-v3` | 3.29.0 | 3.35.3 | ✅ **UPDATED 19/10/2026 → 3.35.3** |
| `php-open-source-saver/jwt-auth` | 2.8.2 | 2.9.3 | 🗑️ **UNUSED** (D.2) |
| `torann/geoip` | 3.0.9 | 3.0.10 | 🗑️ **UNUSED** (D.2) |
| `barryvdh/laravel-cors` | dev-develop | — | ✅ **REMOVED 19/10/2026** (D.1) |
| `fruitcake/laravel-cors` | dev-develop | — | ✅ **REMOVED 19/10/2026** (D.1) |
| `laravel/pint` (dev) | 1.25.1 | 1.32.1 | 🔸 minor behind |
| `laravel/sail` (dev) | 1.45.0 | 1.68.0 | ✅ **UPDATED 25/10/2026 → 1.69.0** |
| `nunomaduro/phpinsights` (dev) | 2.13.1 | 2.15.0 | 🔸 minor behind |
| `spatie/laravel-ignition` (dev) | 2.9.1 | 2.12.0 | ✅ **UPDATED 25/10/2026 → 2.12.1** |
| `mockery/mockery` (dev) | 1.6.12 | 1.6.15 | ✅ **UPDATED 25/10/2026 → 1.6.15** |
| `fakerphp/faker` (dev) | 1.24.1 | — | ✅ |

Like the frontend table: **this is the map, not an instruction to upgrade.** The Laravel 10 → 11/12
jump is a real migration (new skeleton, config changes, Sanctum 4). But unlike the frontend's gaps,
the framework one has a **security dimension**: Laravel 10 no longer receives security fixes, so
the upgrade should be planned as its own project, not just noted.

✅ **DONE 25/10/2026 — Laravel 12.70.1** (12.70.2 since 01/11/2026). Laravel 10.49 → 12.70.1 in one
step (Carbon 3, Symfony 7.4), with Sanctum 4, rappasoft auth-log 6 (+ its upgrade migration), PHPUnit 11
and Collision 8; the classic Laravel 10 structure is kept. `config.platform.php = 8.2.35` keeps the lock
installable on the TEST server's PHP 8.2. The 166 tests passed unchanged. Details and deploy steps:
README → *Laravel 12 upgrade*. Laravel 13 is the next step (it needs PHP 8.3+, so not before the TEST
server leaves 8.2).

***

## E. Code style & static analysis

* `./vendor/bin/pint --test` currently fails on **157 files** repo-wide (measured 08/10/2026) —
  `class_attributes_separation`, `concat_space`, `single_quote`, blank-line rules, etc. The
  known-issues table (row 10) only tracks the handful relevant to recent work; the full debt is
  bigger. **Fix:** one formatting-only commit (`./vendor/bin/pint`), ideally right after open
  branches merge so it doesn't create conflicts; from then on run Pint per-touched-file.
* `nunomaduro/phpinsights` is installed (`config/insights.php`) but wired into no CI — either
  adopt a minimum score in CI or drop the dev dependency.

***

## F. Two language directories — consolidate ⚠️

Both `lang/` (Laravel 9+ root location: `en`, `ar` — framework files plus `category.php`) **and**
`resources/lang/` (legacy location: `en`, `fr`, `ar` — the `messages.php` used by the localized
certification responses of Update 56, plus `validation.php` copies) exist. Neither tree is
complete: root `lang/` has **no `fr`**, and the two `validation.php` variants can drift apart.

**Fix:** pick one location (root `lang/` is the Laravel 10 convention), merge the trees so all
three locales carry every file, delete the other directory, and run the localized-message tests
(`CertificationIssuedOnceTest` covers the fr/ar messages) to prove translations still resolve.

***

## G. Backend updates & optimizations backlog

Everything below was flagged in `LMS_Frontend/major_code_updates.md` (update number in
parentheses). The security rows also live in [Issues & Errors](https://theteam-13fc7f49.mintlify.site/changelog/issues-and-errors.md).

### G.1 🔴 Security hardening (do these first)

| # | Endpoint / area | Problem | Fix |
| - | - | - | - |
| 1 | `POST /certification` | No ownership / completion check (`CertificationPolicy::create` returns true for everyone) — any user can issue any certification (issue 1) | Server-side "caller == student\_id" + completion check in `CertificationsController::store` |
| 2 | `POST /notifications/send` | `authorize()` returns true, no role check — a student can notify the whole company (issue 17) | Admins / instructors only; instructors limited to their own classrooms' learners |
| 3 🔶 | Classroom write endpoints | `create` / `update` / `archive` / `unarchive` / `force_delete` check no role; `update` + ~~`force_delete`~~ skip the company filter (issue 21). ✅ `force_delete` has the company filter since 20/10/2026 | Staff-only + `where('company_id', …)` |
| 4 | `GET /training/{id}` | `TrainingPolicy::view` is `return true` — full lesson content + answer keys for anyone with the URL (Update 44 follow-up) | Staff / owner-only now that students use `GET /preview-training/{idOrSlug}` |
| 5 | `GET /quiz/{id}` | Students hold `QUIZZES_VIEW` by default — the full answer key is reachable (Update 44 follow-up) | Restrict to staff / owners (keep the owner-view line added in Update 35) |
| 6 | `GET /quiz/{id}` pre-exam | ⚠️ Unverified graph finding (Update 89): the answer key may ship **before an exam starts** | Trace and verify; if real, strip `is_correct` until the attempt is submitted |

### G.2 🔥 Payload & performance optimizations

| Area | Problem | Suggested fix |
| - | - | - |
| ✅ `GET /auth/user` (19/10/2026) | ~~Ships the student's entire exam dataset (questions, options with `is_correct`, attempts) in the login payload, through a silently-dropping whitelist (Update 35 🚩)~~ — now the slim, whitelist-based payload of `AuthUserPayloadService` (cards + server-computed `progress` / `attempts`, \~20 KB; `?legacy=1` for one release). See [Auth User Optimize](https://theteam-13fc7f49.mintlify.site/clean-up/auth-user-optimize.md) | Done |
| `GET /training/{id}` | Returns every lesson's full HTML `content` up front — the course player's dominant load cost (Update 37) | A structure endpoint without `content` + per-lesson `GET /lesson/{id}` on demand; at minimum gzip + lazy-load content |
| ✅ `POST /lesson/{id}/toggle_lesson_validation` (19/10/2026) | ~~The app must chain a full `GET /user/{id}` afterwards just to refresh `currentUser.lessons` (Update 37)~~ — the app no longer makes that call: it updates `validatedLessonIds` locally from the lesson it just toggled (Update 125). The response itself did not change (`message` + `user_progress`) | Done on the app side |
| ✅ Chapter quiz serialization (19/10/2026) | ~~`GET /training/{id}` omits a chapter's `quiz` when the chapter has no lessons~~ — every chapter payload now carries `quizzes` (all its quizzes, lessons or not; students get the published ones, staff the drafts too) + the deprecated `quiz` = the first. Guarded by `tests/Feature/ChapterMultipleQuizzesTest.php`. No lesson-dependent drop could be reproduced: the missing quiz was most likely a **draft** (the editor creates quizzes unpublished; `GET /chapter/{id}`, the app's backfill, returned drafts, `GET /training/{id}` never did) | Done; the frontend backfill was deleted |
| Staff list endpoints | No "active + archived together" mode; the frontend's All view costs 2–3 requests per page (issue 31) | Accept `archive_status=active\|archived\|all` (or `with_archived=1`) and return `archived_at` in every row |
| `/preview-trainings` | No `total_quizzes_count`, so catalog cards hide the quizzes stat (issue 3) | Add chapter quizzes + final quiz count to `MarketplacePreviewService::trainingCard` |
| `preview-*` payloads | `total_active_sales` still reaches students' network tabs (Update 47) | Drop it from `trainingCard` / `examCard` |
| ✅ Certifications (migration of 17/04/2026) | ~~Check-then-insert can still race for non-app callers (Update 3)~~ — the unique indexes on `(student_id, training_id)` and `(student_id, quiz_id)` exist (`2026_04_17_000001_enforce_unique_certifications_per_target`, already noted in Update 56), and `store` returns the existing row on a duplicate-key error. Guarded by `tests/Feature/CertificationIssuedOnceTest.php` | Done |
| Certification notification | Inserted outside the transaction → 500 for a created certificate (issue 2) | try/catch or queue the notification |
| Gamification | All-time score only, no level (issue 13) | `?period=week` + a `level` / tier per entry |
| Lessons pivot | No validation timestamp (issue 16) | Expose the pivot `created_at` on the user's lessons |
| ✅ Classroom force-delete (20/10/2026) | ~~Archived classrooms 404 on `force_delete` (issue 22)~~ — the lookup now uses `withArchived()` + the company filter (super admins keep their bypass). Guarded by `tests/Feature/ClassroomSlugTest.php` | Done |
| Certificate share images | GD cannot shape Arabic (issue 30) | Bundle an Arabic font + glyph shaping (e.g. `ar-php`), or render RTL cases with a headless browser |

### G.3 Framework & infrastructure

| Item | Detail |
| - | - |
| ✅ **Laravel 10 EOL** (25/10/2026) | ~~10.49.0 today; security fixes ended 02/2025. Plan the 11 → 12 migration (Sanctum 4, phpunit 11+, collision 8 move with it)~~ — now Laravel 12.70.2 (Section D.4, README → *Laravel 12 upgrade*). Deploy: `composer install`, `php artisan migrate`, `optimize`, restart PHP-FPM + Reverb |
| ✅ CORS stack (19/10/2026) | ~~One middleware, zero abandoned packages (Section D.1)~~ — done: only Laravel's own `HandleCors` runs |
| 🔶 `minimum-stability` | Back to `stable` once the `dev-develop` CORS pins are gone. The pins left on 19/10/2026 and `composer.lock` holds no `dev-*` package anymore, but `composer.json` still says `dev` |
| nginx `/storage/` CORS | Applied + verified on `backend-test` (Update 19) — **replay on the dev / prod vhosts** when they go live |
| nginx wasm / cache headers | The frontend-vhost directives of Update 33 (brotli/gzip for `application/wasm`, immutable cache for hashed assets, `no-cache` for `index.html` + the service worker) are **documented, not applied** |
| Deploy checklist | `php artisan migrate` on every backend before the matching frontend deploy (e.g. `quizzes.views_count`, Update 47); `CERTIFICATE_PUBLIC_URL` + `php artisan config:clear` per environment (Update 81); the `location ^~ /certificate/` proxy on each frontend vhost (Update 81) |
| Queues | Notification sends and the FCM fan-out (when push lands) belong on a queue worker, not in the request cycle |

***

## H. Production database — orphaned Laravel Telescope tables (≈6.6 GB) ✅

✅ **DONE 06/11/2026 — Telescope is removed from the production environment.** The finding and the
steps stay below as the record of the work.

> Found 09/10/2026 on `theteamdb_prod` (phpMyAdmin on Plesk) with the table-size query of
> [Monitoring & Logs](https://theteam-13fc7f49.mintlify.site/deployment/monitoring-and-logs.md). Full runbook (server checks, code removal
> if needed, SQL, the keep-but-bounded alternative) on that page.

| Table | Size | Rows (approx.) |
| - | -: | -: |
| `telescope_entries` | **6.02 GB** (5.08 data + 0.94 index) | 7,470,127 |
| `telescope_entries_tags` | **0.57 GB** | 4,661,327 |
| `quiz_questions` — the biggest *real* table | 0.03 GB | 57,966 |

Laravel Telescope is **not in the repository**: nothing in `composer.json` / `composer.lock`, no
`config/telescope.php`, no `TelescopeServiceProvider`, no Telescope migration, and
`git log --all -S telescope` is empty on every branch. The package was either installed on the server
outside git or removed from the code without dropping its tables. Either way the two tables are dead
weight (≈98 % of the database) that nothing in the app reads, so dropping them cannot break the app —
once step 1 confirms the server matches the repo.

1. ✅ **Check the server** (SSH / Plesk terminal, backend root): `composer show laravel/telescope` must
   answer "not found"; `grep -rn -i telescope composer.json config/app.php app/Providers .env` and
   `php artisan route:list | grep -i telescope` must print nothing. If Telescope IS installed there,
   remove it first (runbook, "Removing Telescope safely") — the database steps are the same afterwards.
2. ✅ **Prove nothing writes**: `SELECT MAX(sequence) FROM telescope_entries;` twice, 5 minutes apart —
   same value.
3. ✅ **Drop** (phpMyAdmin → SQL; child table first because of the foreign key):
   `DROP TABLE IF EXISTS telescope_entries_tags;` `DROP TABLE IF EXISTS telescope_entries;`
   `DROP TABLE IF EXISTS telescope_monitoring;` then
   `DELETE FROM migrations WHERE migration LIKE '%create_telescope_entries_table%';`
4. ✅ **Re-run the size query**: the database goes from ≈6.7 GB to ≈0.1 GB. Needs
   `innodb_file_per_table = ON` (the MySQL default — `SHOW VARIABLES LIKE 'innodb_file_per_table';`);
   if OFF the space is reused inside `ibdata1` but the file never shrinks.

No backup of the rows is worth taking (request / query debug logs, 6 GB of them); a schema-only dump
(`mysqldump --no-data theteamdb_prod telescope_entries telescope_entries_tags telescope_monitoring`)
is enough to recreate the empty tables if Telescope ever comes back. `php artisan telescope:prune` /
`telescope:clear` are NOT the tool: they delete 1,000 rows at a time and would run for hours on 7.5 M
rows — and the commands do not exist without the package. `DROP TABLE` takes seconds.

***

## Execution order

1. ⬜ **Section A** — `.gitignore` additions, `git rm -r --cached graphify-out`, drop the
   `.DS_Store`s and `@php`. **\~12.2 MB / \~550 files gone, zero functional risk.**

2. 🔶 **Section D.1** — kill the duplicated abandoned CORS packages + the second Kernel
   middleware; re-verify CORS; restore `minimum-stability: stable`. ✅ Packages + middleware gone
   19/10/2026, CORS re-checked 06/11/2026; only `minimum-stability` is left.

3. ⬜ **Section D.2** — remove `jwt-auth` + `geoip` (+ configs + the `User` JWT interface); run
   the suite.

4. ⬜ **Section B** — compress the three landing PNGs (\~3 MB); delete the unreferenced images
   after a DB check (\~560 KB).

5. ⬜ **Section E** — one Pint formatting-only commit (157 files).

6. ⬜ **Section F** — consolidate the two lang trees.

7. ⬜ **Section C** — move the root diagrams / notes into `docs/`; fix the
   `x_README_API_Documentation` references in both CLAUDE.md files.

8. 🔶 **Section G** — the G.1 security rows go first (tracked in Issues & Errors); G.2 / G.3 are
   the optimization backlog; the Laravel upgrade is its own project.

9. ✅ **Section H** (done 06/11/2026) — drop the orphaned Telescope tables on production (≈6.6 GB, 98 % of the
   database). Independent of the repo, the only item with an immediate payoff: do it first.

Like the frontend: doing Sections A–C **before** the next history-sensitive operation keeps the
repo permanently light — files removed later still live in pack history otherwise.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.