# Resume here

## Handoff, 2026-08-19 — everything is GREEN, and here is exactly what is half-done

Five agents were killed at once by a **session usage limit** (resets 2:10am Asia/Calcutta). Nothing
was lost: a snapshot was taken before they died, and two of them received a preserve instruction in
time. **Snapshot to restore from: `20260819-0612-GREEN-j3-j4-j10-complete-cb03-quarantined.tar.gz`.**

All three gates pass right now — `validate-lessons` (6 lessons), `validate-corpus`, `lint-prose`
(0 errors), and **509/509 tests**. Do not start by re-verifying; start by reading the two items below.

**1 · `trip-state-machine` curveball 03 is quarantined, not finished.** It sits at
`corpus/trip-state-machine/incomplete/03-no-show-timeout/` with a `README.md` in that directory
explaining precisely what is missing and how it was diagnosed. The short version: five of its eight
`reference-patch/` files are **byte-identical to the baseline**, and `TripRules.java` — the
transition table — has zero mentions of `WaitExpired` while its test suite references it 15 times.
The agent staged the file list and died before applying the edits.

**No `budget.json` was written, deliberately.** Measuring the patch as it stands gives
`reference_diff = 1`, which is an artifact of the missing edits rather than the cost of the feature.
Recording it would have enshrined a false measurement and handed the user a 3-line budget for a
change that really costs more. The measurement method is validated: it reproduces
`01-driver-cancels` at 8 and `02-start-without-the-tap` at 3, matching both recorded budgets.
Curveballs 01 and 02 are complete and the problem itself validates.

**2 · `rate-limiter` still has no `problem.json`,** so `validate-corpus` **skips the entire problem**
— which reads as success and is not. Its `contract/`, `reference/`, `scaffold/`, `tests_base/` and
`tests_concurrency/` all exist, and the agent's last report was a green base suite at 28/28. Write
`problem.json` first, then re-run the validator to surface every remaining gap at once. Still owed:
`acceptance.json` (generate via `displayNames()`, never retype), `DECISION_LOG.md`, three curveballs,
and the **mandatory stress suite that must provably fail a non-thread-safe implementation**.

**3 · J3, J4 and J10 are complete and validate.** J4 and J10 were finished by hand after their
agents died — their `predict.json`, `retrieval.json`, and J10's whole `faded/` stage. J10's GapTest
was verified both directions: **7/7 against `solution/`**, and it cannot pass against `src/` because
the gapped source does not compile. That matches J1's existing convention, where the gaps also open
a class declaration.

**A gotcha that cost time:** the GAP marker regex in `server/lib/lessons.mjs` is
`/^\s*\/\/\s*GAP:\s*(.+?)\s*$/` — exactly `// GAP:`, on its own line, **with no number**. Writing
`// GAP 1:` produces "faded/ has no GAP markers", which sounds like the file is missing rather than
mislabelled.

Six bridge lessons remain: **J5, J7, J8, J9, J11, J12.** Briefs for J5 (interfaces, anchored to
`logger`), J9 (exceptions, anchored to `file-system`) and J10 were already written and are worth
reusing — they were detailed and the two that ran produced good work.

**Written:** 2026-08-18. **Last updated:** later the same day, after the corpus sprint.
**State:** ~401 tests green. Backend complete. Front end complete and browser-verified. **14 of 20 problems authored**, 3 of 46 lessons.
**The user will not use the app until it is 100%**, so unfinished work is expected — but nothing that exists should be half-true.

Read this file first, then `COMPLETION.md` for the row-by-row checklist.

---

## What to verify in the first five minutes

```bash
cd C:/Users/PratyushKumar/PycharmProjects/lld-trainer
node --test                       # expect 390+ pass, 0 fail. NEVER pass a directory (see gotchas)
node tools/validate-corpus.mjs    # expect only "B8 / E5 uncovered" if food-ordering + notification-service are absent
node tools/lint-prose.mjs         # expect 0 errors over lessons/
node tools/build-ui.mjs && cd ui-src && npx tsc --noEmit   # expect zero TS errors
```

Then `node server/index.mjs` and open `http://127.0.0.1:7777`. Eight problems are authored, so real drills work.

## Where things stand

| Area | State |
|---|---|
| **Backend** | **Complete.** 25 modules in `server/lib/`, all eight phase graders, anchored rubric with citation validation, five company modes, syllabus tracking, evidence ledger, readiness, lesson infrastructure, prose linter, corpus validator |
| **API** | **Complete.** Phases 0–7, rounds, curveball injection, lessons, `/api/standard`, ledger, readiness, syllabus |
| **Front end** | All 14 views exist and build. Phases 0–2 **verified in a browser**; phases 3–7, Learn, Ledger, Readiness, Standard were written by an agent that had not reported when this was written — **verify before trusting** |
| **Corpus** | **18 of 20** authored and validator-clean. `rate-limiter` and `trip-state-machine` are PARTIAL — see below |
| **Lessons** | **3 of 46**: `J1`, `J2`, `J6` (Java Bridge), prose-clean and validator-clean. Now tiered — see the tier model below |
| **Packaging** | `run.ps1`, `run.sh`, `tools/package.mjs` → 679 KB zip. **Never verified on the Mac** |
| **Toolchain** | JDK discovery added. Finds an installed JDK 21 (verified: PyCharm's bundled JBR on the Ryzen) instead of downloading 180 MB |
| **Reach/sync** | `tools/lib/sync-policy.mjs` decides last-write-wins and backups. The rest of the pipeline (`deploy.ps1`, self-updating `run.sh`, companion export) is **unbuilt** |

### Update — the tier model for lessons

The user asked whether 43 remaining lessons were too big and asked for an Alex Xu conversational
register. Both are now settled and enforced:

- `docs/specs/2026-08-18-lesson-voice.md` carries an **AMENDMENT** allowing second person,
  contractions, author-and-reader `we`, and body-text questions, raising the `idea.md` cap 150 → 250.
  The condescension ban is unchanged — conversational writing is where condescension hides best.
- `TIER_REQUIREMENTS` in `server/lib/lessons.mjs` requires different artifacts per tier. Tier 1 (14
  items, listed in `TIER_1_ITEMS`) gets the full treatment; tier 2 skips the contrast pair; tier 3 is
  text-only process items; bridge lessons require `from-cpp.md` and no contrast. This cut contrast
  pairs from 34 to 14 and removed ~20 `when-not.md` files, about 35% of the remaining artifact work,
  **without lowering the bar on any lesson that carries real weight**.
- The validator refuses a tier in **both** directions: a Tier 1 item declared as tier 2 is an error
  (it would silently drop the measured contrast pair), and a `contrast/` directory on a tier 2 lesson
  is an error (either the tier is wrong or the pair is invented).
- **J1, J2 and J6 predate the amendment.** They are correct but in the older terser register. One
  revoice pass is owed so the bridge track reads with a single voice.

## Four problems are PARTIAL — finish them, do not restart them

Five agents were killed mid-task by a **session usage limit** (not a crash, not a bad state). Each had
already written most of its problem and died during curveball verification. Their snapshots and files
are intact. Resume each with a brief that says "this directory is ~80% done, finish and verify" rather
than authoring from scratch.

**All four partials were finished.** `food-ordering` and `notification-service` are complete and own
**B8** and **E5** respectively. `rate-limiter` and `trip-state-machine` were still authoring when this
was last updated — if either lacks a `problem.json`, **first run its base-suite check** (command in
`docs/plans/2026-08-18-corpus-authoring.md`) to learn what already works before changing anything.
Resuming worked well: food-ordering's finisher found the base suite already green and B8 already
correctly implemented, so it verified rather than rebuilt and only added the missing artifacts.

**A trap worth knowing:** food-ordering was missing `problem.json`, so `validate-corpus.mjs` skipped
the entire problem *silently by design* (it prints `skipped X — no problem.json yet`). A directory that
looks finished but has no `problem.json` is invisible to every other check. Always confirm the
validator actually **inspected** a problem rather than skipping it.

## SYLLABUS COVERAGE IS CLOSED

Independently verified 2026-08-18: `checkSyllabus` reports **`uncovered: []`, `unknown: []`** across the
8 problems that have a `problem.json`. All 34 items are exercised, and every tag references a real
item. The closure claim the whole design rests on — *"the LLD syllabus is fixed, and coverable, not an
infinite arbitrary mess pool"* — is now provable rather than aspirational.

## NOTHING IS TO BE CUT — the user overrode the earlier scoping advice

An earlier version of this file said the remaining problems were "depth, not coverage" and could be cut
if the calendar tightened. **That guidance is withdrawn.** The user's instruction:

> *"since we wont be returning to add anything to this app, let make it content-complete and correct.
> It'll be a long run tool."*

So: **all 20 problems and all 46 lessons get authored**, and syllabus coverage stays at 100%. He does
not intend to come back and top it up later, which means anything skipped now is skipped forever. The
frontier FE tier also stays in scope.

Two consequences for anyone resuming:

- `file-system`, `lru-cache` and `rating-system` are **required**, not optional. `rating-system` is
  additionally listed in Atlassian mode's allow-list in `server/lib/modes.mjs`, so omitting it would
  leave that mode pointing at a problem that does not exist.
- **A slack diff budget is a defect, not a compromise.** `middleware-router`'s curveball 02 measured 47
  lines against a budget of 71, which stops Phase 4 discriminating between a clean absorption and a
  rewrite. Every other curveball in the corpus lands between 0 and 19. That one is being rewritten to
  come in under 10, and the same bar applies to anything authored from here.

## The fifth partial: phases 3–7 front end

All 14 views exist and the UI builds. That agent died while investigating a hazard it had found —
its last words were *"check whether the same hazard exists in the file-tab rails (Workbench and
SkeletonSprint)"*, so **there is a known-but-unnamed defect in the tab rails worth hunting.** Read
`docs/verify/fe-surfaces.md` if it wrote one; otherwise re-verify from scratch using
`docs/verify/fe-verification.md` as the method.

## Remaining work, in priority order

1. **Verify the phases 3–7 front end.** It is the largest unverified surface. The three things to check by observation, not inference: the zone transition visibly quiets the interface on entering a drill; Phase 4's diff number updates live as you edit; reduced motion leaves every surface usable. Method to copy: `docs/verify/fe-verification.md`.
2. **Finish the corpus — 2 problems.** Only `rate-limiter` and `trip-state-machine` remain, both PARTIAL. Assignments in `docs/CORPUS-ASSIGNMENTS.md`. `rate-limiter` still owes `problem.json`, `acceptance.json`, curveballs and a **mandatory stress suite**; `trip-state-machine` still owes `problem.json` and possibly `reference/src` and `tests_base` entirely — verify before planning.
3. **Author 43 lessons.** `docs/plans/2026-08-18-learn-layer.md` has the structure and the tier model; `lessons/J1` is the worked template. Order: the remaining 9 bridge items (J3, J4, J5, J7–J12), then the 14 Tier 1 syllabus items in `TIER_1_ITEMS`, then the rest. Anchor every lesson in a real corpus problem — the voice spec forbids `Foo`/`Bar`, and a lesson about parking lots should use the parking lot.
4. **Build the reach/sync pipeline.** `docs/specs/2026-08-18-reach-and-sync.md`. The decision half exists (`tools/lib/sync-policy.mjs`, 21 tests). Still unbuilt: `deploy.ps1` with its ordered gates, `manifest.json`, self-updating `run.sh`, the companion export, and the R2 upload. **Two things must not be forgotten:** checkpoint WAL before copying `dojo.db` (`PRAGMA wal_checkpoint(TRUNCATE)`), and gate the companion — the Ledger and Readiness snapshots are his practice performance on a public, indexable URL.
5. **Frontier FE tier F1–F13.** `docs/specs/2026-08-18-design-system.md`. Build **F13, the telemetry HUD, first** so no effect ships without its frame cost visible. Lobby surfaces only.
6. **Verify on the Mac.** Unzip, `./run.sh`, confirm the JDK bootstrap picks `mac-aarch64` and fonts render offline. JDK discovery may make the download unnecessary if a JetBrains IDE is installed there.

## Known inconsistencies to fix

**1 · RESOLVED 2026-08-19.** `validate-corpus.mjs` now enforces a zero-arg `create()` (see the
comment at its line 139 for why: the editor's live binding rail calls `Entry.create()` with no
arguments and cannot invent a clock). All 20 problems comply — verified by inspecting every
`contract/Entry.java`, not inferred from the validator passing. The original note follows for
context only.

~~**`Entry.create()` arity varies across problems.**~~ The deterministic-contract spec says tests only ever write `Entry.create()`. But:
- `snake-and-ladder` has `Entry.create(Dice)` **plus** a zero-arg overload that delegates — good.
- `library` has **only** `Entry.create(TimeSource)` — no zero-arg form.

Nothing in `server/**` or `ui-src/**` calls `Entry.create` today, so nothing is broken yet. But the live binding rail and any generic harness will want a zero-arg entry point. **Standardise: a zero-arg `Entry.create()` must always exist**, with parameterised overloads optional. Fix `library`, and add a check to `validateProblemDir`.

**2 · `A6` may be over-claimed.** `library` flagged its own A6 as weak because the exception hierarchy is handed over in `contract/` rather than designed by the candidate; `parking-lot` and `snake-and-ladder` dropped A6 for the same reason. Only `vending-machine` genuinely makes the hierarchy the point. Consider letting vending-machine carry A6 alone and untagging it elsewhere — an inflated tag makes "you have held every item" a lie, which is the one thing the syllabus must never be.

**3 · `retrieval.json` shape.** `server/test/lessons.test.mjs` stubs `{prompts: ['string']}`; the authored bridge lessons use `{prompts: [{id, prompt, expect}]}`. The object form is better — it carries the expected answer, which the Learn UI needs. The validator currently accepts both, so **the tests are passing against a shape no real lesson uses.** Settle it in the validator and update the fixture. Deferred only because lesson agents were mid-flight and tightening the validator under them would have broken their runs.

**4 · Phase 2 A-item credit is a presence check.** `creditableItems()` credits A1/A2/A3/A4/A7 from Phase 2 using regex structural markers. Conservative and comment-stripped, but presence, not semantics. Documented in `server/lib/syllabus.mjs`; tighten if it ever over-credits.

---

## Gotchas — each of these cost real time to find

**`node --test <directory>` FAILS on this Node (v25.8.2)** with `Cannot find module`. Use bare `node --test`. A single file works if named explicitly.

**A test that starts an HTTP server must call `server.closeAllConnections()` before `close()`.** Node's `fetch` keeps connections alive, so a bare `close()` waits on idle sockets and the test process hangs *after* every assertion has passed — no output, no failure, just a timeout. Cost 8 minutes of wall clock to find in `api-contract.test.mjs`. If a new API test hangs with zero output, this is why.

**Bootstrap accepting a JDK is not the same as the app using it.** `compile()` and the JUnit runner resolve `javacPath(jdkHome())`, and `jdkHome()` reads `.toolchain/jdk-home.txt`. Before that pointer existed, bootstrap could accept a `javac` from PATH, print success, and then every compile failed because the server was still looking in `.toolchain/jdk-21`. Any future change to JDK resolution must keep both halves in step.

**A `javac` is not automatically a usable `javac`.** `MIN_MAJOR = 21` in `tools/jdk-discovery.mjs` is load-bearing: the corpus uses sealed interfaces and switch patterns, so a Java 17 JDK compiles nothing and the user reads a wall of syntax errors in code he did not write. Every discovered candidate is version-probed, and rejections are printed with the version found.

**Snapshots are `.tar.gz` archives, not directories.** They used to be directories, which made `node --test` discover test files *inside* them — 1389 tests with duplicates. Never revert that.

**`tar` under Git Bash rejects absolute Windows paths** — it reads `C:\...` as a remote hostname (`Cannot connect to C: resolve failed`). Keep every path passed to `tar` relative and set `cwd`. If a snapshot fails in Bash, run it from PowerShell.

**`execFileSync('npm.cmd', …)` throws `EINVAL` without `shell: true`** on Node 25 (Windows batch-file hardening). Already handled in `tools/build-ui.mjs`; copy that pattern for any `npm`/`npx`/`tsc` spawn.

**`claude` on Windows is an npm shim with no `.exe`.** `execFile('claude')` gives `spawn claude ENOENT`. `resolveClaudeBin()` in `server/lib/grader-llm.mjs` reads the real exe path out of `claude.cmd`. **`shell: true` is not the fix** — it trips DEP0190 *and* swallows the empty-string argument to `--allowedTools`, silently re-enabling tools during grading. The prompt goes over **stdin**, never argv (Windows caps a command line near 32 KB, which a code-review prompt would breach).

**JUnit's XML writer puts `@DisplayName` in a `system-out` CDATA block**, not the `name` attribute (which holds `method()`). `parseJunitXml` handles it. Those strings are the user's visible checklist, so do not "simplify" it.

**Three or more agents compiling Java concurrently starves a full test run** — it buffered zero output for 25 minutes once. Keep the Java-heavy pool small and tell authors to expect slow compiles rather than assume a hang.

**`writeHead()` before `await readFile()`** crashed the server on any missing static file (`ERR_HTTP_HEADERS_SENT`). Read first, then write headers.

---

## Conventions that must be copied, not reinvented

**Corpus** — proven by `parking-lot`, refined by `vending-machine` and `elevator`:
1. `reference-patch/` per curveball (new file + `PATCH.md` diff) so a measured budget is reproducible without a discarded working copy.
2. `contract-delta/` overlays **by filename**, so a missing new method is a compile error rather than a runtime surprise.
3. Curveball tests never assert a literal id the candidate invents — compare ids the implementation itself returned.
4. `acceptance.json` is **generated** from the suite's `@DisplayName` strings via `displayNames()`, never retyped.
5. Every arbitrary rule lives in the contract javadoc. The user must never fail on a rule he could not have known.
6. Diff budget = `max(3, d + ceil(d/2))` where `d` is the reference's own measured diff. If `d` exceeds ~20, **the reference has the wrong seams — fix the reference, not the budget.**
7. Stress suites must be **proven against a deliberately broken variant**, with the tuning numbers recorded. Elevator's method: it found 64 threads let its broken variant pass 1 run in 5, moved to 200 × 20 for 6/6 detection, and wrote down why.

**Agent briefs:**
- Give an explicit file boundary. That is what makes parallelism safe rather than lucky.
- Require incremental snapshots after each step, and progress notes to `docs/verify/*.md` for long verification runs, so a crashed agent is resumed instead of restarted.
- Ask for the *method* behind each claim, and say plainly that inferring is not observing. Several agents corrected real defects because they were asked to prove rather than assert.

---

## The five judgements worth preserving

These came out of the design conversation and are easy to erode by accident.

1. **`INVALID` is never `no-hire`.** A compile or binding failure scores nothing, records nothing, and is re-runnable. It is enforced in all four deterministic graders and excluded from every ledger statistic. This is the foundation of the user trusting a deterministic judge at all.
2. **40% of the score cannot be argued with.** D2 and D4 come from a compiler, a suite and a diff. `buildRubricPrompt` **throws** if asked about them, so a model cannot be consulted. That is what makes the subjective 60% believable.
3. **An unmeasured dimension is `null` → `unscored`, never `0`.** Applies to meta mode's D2/D4 and to any dimension whose citations failed validation twice.
4. **Coverage counts what was demonstrated, and only what a phase can actually evidence.** D2 credits nothing — a green suite proves the code works and claims nothing about design. Concurrency items need a surviving contended run. `held` needs two demonstrations ≥3 days apart.
5. **Maximal motion on non-execution surfaces; calm the moment a clock starts.** The user's words: *"when i need to do something within 12 mins, there should be no fighting b/w me and the site."* Enforced by a `zone` value in context, not by discipline.

## Honest assessment of the timeline

The user's original target was 3 weeks, stretched to 4–5, with practice starting only at 100%. Backend and API are done. The remaining bulk is **content**: 12 problems and 43 lessons, each of which has taken a capable agent 30–50 minutes of wall clock. That is the critical path, it parallelises well, and it is the part that most directly determines whether the tool teaches him anything.

If the calendar tightens, the **frontier FE tier is the honest thing to cut** — nothing in F1–F13 is load-bearing for a single drill, and abandoning it still leaves a complete trainer.
