# Continuing ADEBLED FixIT locally

Everything you need to pick this up on your desktop. Section 1 is setup. Section 2 is the prompt to
paste into Claude Code. Section 3 is the honest state of the project.

---

## 1. Local setup (30 minutes, once)

### 1.1 Install a PHP + MySQL stack

You need PHP **8.1 or newer** and MySQL 5.7+ / MariaDB 10.2+. Pick one:

| | Windows | Mac | Linux |
|---|---|---|---|
| Easiest | **Laragon** (laragon.org) — PHP, MySQL, Apache in one installer | **Herd** or MAMP | `sudo apt install php php-mysql mariadb-server` |
| Alternative | XAMPP | XAMPP | — |

Laragon is the closest match to cPanel and the least fuss. Install it, start it, and confirm:

```bash
php -v          # must say 8.1 or higher
php -m          # must list: pdo_mysql, curl, openssl, mbstring, json
```

### 1.2 Put the project somewhere

Unzip `adebled-fixit.zip` to a working folder, e.g.:

```
C:\laragon\www\fixit\        (Windows / Laragon)
~/Sites/fixit/               (Mac / Linux)
```

The folder layout deliberately mirrors cPanel:

```
fixit/
  includes/        ← application code. NOT web-accessible on the server.
  public_html/     ← the document root
  bin/             ← cron scripts
  scenarios/       ← scenario JSON seed files
  sql/             ← database migrations
  docs/            ← the full plan (11 documents)
  demo/            ← two working standalone demos
```

### 1.3 Create the database

Open phpMyAdmin (Laragon: `http://localhost/phpmyadmin`) or the CLI, then:

```sql
CREATE DATABASE fixit CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
```

Import the three SQL files **in this order**:

```bash
mysql -u root fixit < sql/schema.sql
mysql -u root fixit < sql/002-curriculum.sql
mysql -u root fixit < sql/003-simulations.sql
```

`002` and `003` contain `ALTER TABLE ... ADD COLUMN` statements. On a **re-run** those throw
"duplicate column" errors — that is expected and harmless, it just means they are already applied.

After importing you should have 33 tables, with `modules` (13 rows), `lessons` (88 rows),
`surfaces` (38 rows), `plans`, `settings`, `credit_costs`, `rubrics` and `badges` seeded.

### 1.4 Configuration

`includes/config.php` does not exist yet and must never be committed. Create it — the first job in
the prompt below is to generate `config.sample.php`, then you copy it:

```bash
cp includes/config.sample.php includes/config.php
```

Fill in your local database credentials (Laragon default is user `root`, empty password).

### 1.5 Run it

```bash
cd fixit
php -S localhost:8000 -t public_html
```

Then open `http://localhost:8000`.

With Laragon you can instead point a virtual host at `fixit/public_html` and get
`http://fixit.test` — closer to production, and it makes cookies behave properly.

---

## 2. The continuation prompt

Open a terminal in the project folder, run `claude`, and paste everything between the lines.

---

> I'm building **ADEBLED FixIT**, a browser-based IT support training simulator for ADEBLED SERVICES
> (Nigeria). I teach a 12-module IT Support Masterclass live; trainees practise in this app after
> each module. Target hosting is **cPanel shared hosting — PHP 8.x + MySQL, no SSH, no Composer, no
> build step, vanilla HTML/CSS/JS, no framework**.
>
> **Read `docs/README.md` first, then `docs/01-architecture.md`, `docs/09-curriculum-map.md` and
> `docs/10-simulation-catalogue.md`.** Those are the authoritative plan. `docs/` has 11 documents
> covering architecture, monetization, database, admin panel, cPanel deployment, roadmap,
> the 88-simulation catalogue, and a DeepSeek integration design. Do not re-plan — the planning is
> done. Build.
>
> **What already exists and works** — do not rebuild any of this:
> - The database: `sql/schema.sql`, `002-curriculum.sql`, `003-simulations.sql`. 40 tables, 12
>   modules, 88 lesson rows, 39 surfaces, 6 rubrics.
> - `includes/` — `bootstrap.php`, `config.sample.php`, `db.php`, `helpers.php`, `settings.php`,
>   `auth.php`, `access.php`, `scoring.php`, `audit.php`, `practice.php`, `view.php`, `admin.php`.
> - The trainee app: landing, signup, login, logout, verify, forgot/reset password, dashboard,
>   modules, module, practice, profile, pricing.
> - `public_html/api/` — `practice-start.php`, `practice-save.php`, `practice-submit.php`,
>   `progress.php`.
> - `public_html/admin/` — overview with the health strip, users, user detail, practice items,
>   item builder with server-side schema validation, releases, settings, audit log.
> - `assets/js/surfaces.js` — TriageBoard, CallPanel, DocEditor, ConfigForm, Console, ChoicePanel,
>   MatchPanel, OrderPanel, all driven from `payload.config`.
> - Module 1's five simulations, seeded by `bin/seed-module-01.php` and graded server-side.
>
> **What to build next, in this order.** Verify each against the running app before moving on —
> PHP's built-in server plus curl, or Playwright if it is available:
>
> **Step 1 — The fault path.** This is the biggest hole. Port the terminal engine out of
> `demo/net-101.html` into `public_html/assets/js/terminal.js` **unchanged** — byte-accurate output,
> `DocumentFragment`, never `innerHTML +=`. Then seed NET-101 into `scenarios` with a
> `practice_items` row of `item_type = 'fault'` pointing at it. The server side is already written:
> `grade_fault()` in `scoring.php` and the fault branch of `practice_start()` / `practice_submit()`
> in `includes/practice.php`. It has never run against a real scenario row, so expect to fix things.
> Watch the JS budget: net-101's engine is ~47 KB and `practice.js` is 7 KB, so a fault page must
> **not** also load `surfaces.js` (22 KB) or it blows the 60 KB limit. Load per item type.
>
> **Step 2 — Email.** `mail_queue()` already writes to `email_queue`, but nothing drains it, so
> verification and reset mails never actually send. Write `bin/cron-mailqueue.php` with PHPMailer
> over SMTP (uploaded to `vendor/`, no Composer), plus `bin/cron-maintenance.php` for token cleanup
> and abandoned attempts. The admin health strip already watches for both and shows them red.
>
> **Step 3 — Payments.** Per `docs/02-monetization.md`. Prices come from the `plans` table, never
> from the client. Nothing is granted on the browser callback — the webhook is the only grant path,
> and it takes no session and no CSRF. Every credit movement is an append-only `wallet_ledger` row
> with an idempotency key.
>
> **Step 4 — Content.** Module 2 onwards, using `bin/seed-module-01.php` as the template and the
> admin item builder for anything hand-written. The payload schema is documented at the top of
> `includes/scoring.php`.
>
> **Rules that must not be broken** — these are load-bearing and are explained in the docs:
> 1. **The server is authoritative.** Scoring, prices and entitlement are decided server-side.
>    `scenario-load` must strip `root_cause`, `accepted_fixes` and `hints` from any payload sent to
>    the browser. Trainees will open DevTools in the first week.
> 2. **Money unlocks content; accuracy unlocks tiers.** The 70% gate is never purchasable.
> 3. **Performance budget:** terminal command response under 16ms, total JS under 60 KB, CSS under
>    20 KB, zero images in the core app, no CDN, no framework, no webfonts. Use `DocumentFragment`
>    for terminal output, never `innerHTML +=`.
> 4. **Byte-accurate command output.** Windows dotted leaders reproduced exactly, including the
>    inconsistent spacing. Errors verbatim. A typo must produce a real Windows error, never a
>    JavaScript stack trace.
> 5. **Viewport fit:** the page fits exactly when the viewport is tall enough and scrolls when it is
>    not (`height:100%` plus a `min-height` floor). **No horizontal scroll at any width down to
>    320px** — this was a real bug, the submit button overflowed at 360px, the most common Android
>    width for my trainees.
> 6. **All practice is 100% practical.** No quizzes, no multiple choice. Eight item types:
>    `fault`, `triage`, `configure`, `inspect`, `dialogue`, `decide`, `identify`, `document`.
> 7. **`config.php` is never committed**, lives outside the web root, chmod 600.
>
> Work through the steps in order. After each step, tell me what you verified and how. If something
> in the docs is wrong or contradicts itself, say so rather than working around it silently.

---

> **Update — security pass complete.** Since this section was written, the app has been through a
> full security review and hardening pass, and gained mail (PHPMailer + SMTP configured from admin),
> admin user creation with an invite flow, the DeepSeek integration (authoring + document grading,
> flags off by default), and cron. Read **[docs/13-security-review.md](docs/13-security-review.md)**
> and **[docs/12-deploy-checklist.md](docs/12-deploy-checklist.md)** first — they describe the
> current state. The tables below are still accurate about *content*, which has not moved.

> **Update — Step 1 is done. The fault path exists.** The terminal engine is out of the demo and in
> `public_html/assets/js/terminal.js`, NET-101 is seeded by `bin/seed-net-101.php` as a real
> `scenarios` row with a `practice_items` row pointing at it, and the fault branches of
> `practice_start()` / `practice_save()` / `practice_submit()` have been run rather than merely
> written. Verified by `tests/` — see [tests/README.md](tests/README.md) — including a byte-for-byte
> diff of the ported engine against the demo across 29 commands. **Still unverified: anything that
> needs a database**, because the machine this was built on has PHP but no MySQL. Import the SQL,
> run the seeder, and open the item before you teach with it. Step 2 (email) and Step 3 (payments)
> are unchanged, except that email was in fact built during the security pass.

## 3. Where the project actually stands

### Done

| | |
|---|---|
| **Planning** | 11 documents in `docs/` — architecture, monetization, database, admin spec, cPanel deployment guide, roadmap, curriculum map, 88-simulation catalogue, DeepSeek design, decisions log, open questions |
| **Database** | Complete and verified. **40 tables** (not 33 — the count in an earlier draft was wrong), all 12 modules and 88 lessons seeded, **39 surfaces**, 6 rubrics, plans, settings, badges. All three files import clean against MariaDB 10.11 with no errors. |
| **The application** | Boots and runs. See the breakdown below. |
| **Auth** | Signup, login, logout, email verification, forgot/reset password. Lockout verified: 5 failures locks the account, and the correct password is still refused while locked — the `login_attempts` query runs before `password_verify`. |
| **Access control** | `includes/access.php` — the three-axis model with distinct `locked_payment` / `locked_unreleased` / `locked_progression` reasons, each rendering different UI. Verified that an unreleased module shows no upsell. |
| **Practice runner** | `practice.php` + `assets/js/surfaces.js` + `assets/js/practice.js`. Five surfaces ported out of the demo and made data-driven from `payload.config`. Autosave, resume-after-reload and submit all verified. |
| **Scoring** | `includes/scoring.php` — server-authoritative, eight grading models. Verified: perfect paths score 100, worst paths 0, partial credit is exact, and a client that posts `score:100` is ignored. |
| **Module 1** | Seeded as real rows by `bin/seed-module-01.php`. The demo is now redundant. |
| **The fault path** | `assets/js/terminal.js` — net-101's engine, made data-driven from `scenarios.payload` and diffed byte for byte against the demo across 29 commands. NET-101 seeded by `bin/seed-net-101.php`. Hints are fetched one at a time from `api/practice-hint.php`, which is also what counts them, so the browser never holds one. A fault page loads `terminal.js` instead of `surfaces.js`, not as well as. |
| **Admin console** | Overview + health strip, users list/detail (suspend, role, cohort, grants, credits, bypass), practice-item list and builder with server-side JSON schema validation, module release per cohort, settings + feature flags, audit log viewer. Role guard verified: a trainee gets 403 on every admin URL. |
| **Deployment** | `.cpanel.yml` for cPanel Git Version Control, full step-by-step guide in `docs/05-cpanel-deployment.md` |

### Not done

| | Effort |
|---|---|
| **Payments (Paystack)** — fully designed, not built; `pricing.php` lists plans read-only | ~9 days |
| **Remaining 83 simulations** — content authoring, only you can do it | ~40–50 days |
| **Remaining bespoke surfaces** — Event Viewer, packet list, BIOS, BSOD, hardware bench, etc. | ~20 days |
| **The other cron jobs** — subscriptions and backup. Mail and maintenance were built in the security pass; the admin health strip watches for the rest and will show them red until they exist. | ~2 days |
| **A second fault** — NET-101 proves the path. The engine is now data-driven from `scenarios.payload`, so the next networking fault is a payload, not code; a fault that needs a surface other than terminal + services still is code. | ~1 day each |

**Total remaining: roughly 10–14 weeks part-time**, down from 14–18. The application layer is no
longer the bottleneck; content authoring is, exactly as `docs/06-roadmap.md` predicted.

### Verified how

The auth, access, scoring and admin work was checked against a real PHP 8.2 + MariaDB 10.11 stack
driven over HTTP, not by reading the code.

The fault path was verified differently, because the machine it was built on has PHP 8.2 and no
MySQL: 181 assertions across `tests/`, run rather than described — the scoring model, the answer
key, what the browser is allowed to receive, the ported engine diffed against the demo command by
command, and the surface contract `practice.js` depends on. See [tests/README.md](tests/README.md)
for what that covers and what it cannot.

What is still **not** verified anywhere:

- **Anything that needs a database.** Opening, resuming, autosaving, claiming and submitting an
  attempt; the hint counter's `UPDATE`; the retry cooldown. The SQL is written and linted, and the
  functions around it are tested against stubs, but the round trip has not run.
- **Anything visual.** No browser has rendered this. The viewport-fit and
  no-horizontal-scroll-at-320px rules are unconfirmed for the fault page in particular, which is
  the densest screen in the app — open it on a phone before the pilot.

### The biggest risk

**Content, not code.** 88 simulations at 3–4 hours each is the largest single cost in this project,
and it is authoring only you can do. Two things make it tractable:

1. **Stay one module ahead of your class.** You need Module 1's five simulations before you teach
   Module 1 — not all 88 before you launch. That is two days of writing.
2. **Build the DeepSeek authoring assistant early** (`docs/11-deepseek-integration.md`). You write a
   two-sentence brief, it drafts the scenario JSON, you edit. Admin-only, human-reviewed before
   publish. It could halve the largest cost in the project, and it is the reason that integration is
   ranked first ahead of anything trainee-facing.

### Still open, and blocking nothing yet

From `docs/07-open-questions.md` — all of section B. Prices, payment channels, refund policy, whether
annual exists at launch, and what happens to a subscriber who finishes all 30 faults. None of it
blocks building. All of it needs answering before you take real money.

The one worth thinking about early is **B6**: right now this is a course being billed like a
subscription. Someone who finishes in six weeks has no reason to keep paying — which is correct, but
it means your model is "sell a course repeatedly" rather than "retain subscribers", and that changes
what you build after launch.

---

## 4. Two things to check on your cPanel host before you get far

Both are ten-minute checks that cost a week if you leave them late. Full detail in
`docs/05-cpanel-deployment.md §Step 0`.

1. **Outbound HTTPS from PHP.** Some shared plans block it. Paystack and DeepSeek both need it.
   Upload the `_pretest.php` script from the deployment guide, load it, then delete it.
2. **MySQL version.** The schema uses `JSON` columns. On MariaDB older than 10.2, `JSON` is silently
   an alias for `LONGTEXT` — storage still works, but `JSON_EXTRACT` does not. Find-and-replace
   `JSON` with `LONGTEXT` if so, and never filter on a JSON path.
