P.10 · The Rep Ladder

P.10 · 45 min

The Rep Ladder

Ask what makes an engineer senior and you will hear "experience," as if it were time served. It is not time. It is pattern exposure: the senior has seen this exact failure forty times, so the diagnosis takes forty seconds. That exposure historically took a decade to accumulate because reps arrived at the speed of employment. You do not have a decade, and you do not need one: reps can be manufactured. This lesson is the manufacturing plan, and it runs alongside the entire core course, not before it. Do project 1 this week and start Part 0 the same day.

The system: ladder, gym, post-mortem

Three components. The ladder is ten projects of escalating scope, each with a spec and verification criteria, taking you from a 40-line script to a small SaaS. The gym is a set of broken programs with planted bugs you find by reading, because diagnostic reps are rarer and more valuable than building reps. The post-mortem is the five-minute writeup after each rep that converts the rep into judgment; skipping it is how people do ten projects and learn three projects' worth.

How the agent's role changes as you climb, and this progression is the whole point:

  • Rungs 1 to 3: agent as tutor only. You type every line, under the tutor-only contract from P.5. These rungs build the reading-and-writing floor everything else stands on.
  • Rungs 4 to 6: agent as pair. The agent may draft; you must be able to explain every line, and you must modify something meaningful in each draft yourself. If a line survives to commit that you cannot explain, that is a rules violation you are doing to yourself.
  • Rungs 7 to 10: agent as builder, you as engineer. You write the spec and the verification, the agent writes the code, you verify like it is your name on it, because it is. This is the discipline the entire core course drills; the ladder's top is the course's ground floor.

One rule spans all ten: the verification criteria are the project. Meeting the spec without running the verification is a rep that did not happen.

The ladder

Rung 1: Stale-lead flagger (CLI tool)

Brief. A command-line tool that reads a CSV of leads and reports which ones have gone stale. You built a cousin of this in P.5; now you build it from a spec, cold, with file input.

Requirements. Runs as node flag.js leads.csv. Reads a CSV with header name,status,days_since_contact. Prints each hot lead stale for more than 7 days as FOLLOW UP: name (n days), then a summary count line. Handles a missing file argument with a usage message instead of a crash.

Verification. Create a test CSV with 8 rows where you know the right answer is exactly 3 flagged leads. Run it: 3. Delete the file argument: usage message, no stack trace. Feed it an empty CSV: summary reports zero, no crash.

Escalation notes. New here: reading a file, parsing lines, defending against bad input. Parse the CSV by splitting lines and commas yourself; a library would hide the rep you came for.

Rung 2: Repo health report (API consumer)

Brief. A CLI that takes a GitHub owner/repo argument and prints a one-screen health report, using the live API from P.4.

Requirements. node health.js vercel/ms prints name, description, stars, open issues, and the date of the last push, labeled and readable. A repo that does not exist prints not found: <arg> cleanly (remember: 404 is a case, not a crash). No authentication.

Verification. Run against a repo you can check in the browser: every number matches the repo page. Run against vercel/this-does-not-exist: the clean message. Run with no argument: usage line.

Escalation notes. New here: fetch, await, JSON navigation, and distinguishing status codes in code instead of in curl. Your P.6 async patterns become load-bearing for the first time.

Rung 3: Contact book (CRUD app)

Brief. A persistent contact manager in the terminal. CRUD is create, read, update, delete: the four verbs of every business application, and the same four you drilled in SQL in P.7. Checkpoint P requires this rung.

Requirements. Commands: node contacts.js add "Dana" dana@example.com, list, update <id> <field> <value>, delete <id>. Data lives in SQLite (a JSON file is acceptable if SQLite fights your setup, but SQLite is the rep worth having). Duplicate emails are rejected with a message. Deleting a nonexistent id says so.

Verification. Add three contacts, list shows all with ids. Quit, rerun list: still there (persistence is the point). Add a duplicate email: rejected. Update one phone, list confirms. Delete one, list shows two. Every criterion observed, not assumed.

Escalation notes. New here: state that outlives the process (P.9's first concept, now in your hands), command-line argument routing, and the difference between an in-memory array and a real store. This is the smallest project with the full shape of an application.

Rung 4: Contact API (HTTP server)

Brief. Put the contact book behind HTTP. Same data, new interface: now programs can be its user.

Requirements. A server exposing GET /contacts (list, JSON), POST /contacts (create from a JSON body), DELETE /contacts/:id. Correct status codes: 200, 201 on create, 404 for missing ids, 400 for a body without an email. Reuses rung 3's database.

Verification. All checks via curl from a second terminal: create returns 201 and the new contact; list includes it; delete then re-delete the same id returns 404 the second time; a bad body returns 400 without crashing the server. The server survives all of it and keeps serving.

Escalation notes. New here: you are the server from P.4's diagram. Routes, request bodies, and the discipline of correct status codes. Pair rules begin: the agent may draft the server scaffolding, you must explain every route and write at least one endpoint yourself.

Rung 5: Contact web UI

Brief. A browser front-end for rung 4: the first time a human who is not you could use your software.

Requirements. A page served by your server showing the contact list and a form to add one. Submitting the form creates the contact via your POST endpoint and the list reflects it. A delete button per contact. No framework required: plain HTML and a little client-side JS is the honest version of this rep.

Verification. Add a contact through the form, refresh the page: present. Restart the server, refresh: still present. Delete from the UI, check with a curl to GET /contacts: gone from the API too, proving one shared store, no shadow copies.

Escalation notes. New here: the browser half of P.4, forms, and rendering data into HTML. Also your first two-audience bug surface: things can now break in the UI, the API, or between them, and locating which is a diagnostic rep.

Rung 6: Accounts and login (auth)

Brief. Multiple users, each seeing only their own contacts. Auth is where amateur projects end and real ones begin, because auth is where mistakes have victims.

Requirements. Signup and login with email and password. Passwords hashed (never stored raw; have your tutor explain bcrypt-style hashing before you build). Sessions via cookie. Every contact belongs to a user; every route enforces it.

Verification. Create users A and B, each with contacts. Log in as B: only B's contacts appear, in the UI and via direct API calls with B's session. The old unauthenticated curl from rung 4: rejected. Open the database and look at the password column: unreadable hash. Wrong password: rejected without hinting which part was wrong.

Escalation notes. New here: security as a requirements category. The B-cannot-see-A test is your first adversarial verification: you are attacking your own app. Keep the pair rules; write the ownership check in every route yourself.

Rung 7: Webhook intake (third-party integration)

Brief. Your app joins the ecosystem you already operate in: it accepts webhooks, like the GHL and Zapier endpoints you have configured from the other side for years.

Requirements. A POST /webhooks/lead endpoint that accepts a JSON payload (simulate the sender with curl) and creates a contact. Deliveries carry an event id; replaying the same event id must not create a second contact (P.9's idempotency, now mandatory). Malformed payloads are logged and answered with 400, never crash.

Verification. Send a delivery: contact created, 200. Send the exact same payload again: 200, still exactly one contact (prove it by counting rows). Ten malformed payloads in a row: ten 400s, server healthy, contacts unchanged.

Escalation notes. New here: building for a caller you do not control, and idempotency as a design requirement rather than a virtue. Builder rules begin: write the spec and verification first, let the agent build, then verify adversarially yourself.

Rung 8: Follow-up jobs (background work)

Brief. The app develops a heartbeat: work that happens later, without a request driving it.

Requirements. A jobs table as a queue. When a webhook creates a contact, enqueue a "send welcome" job (writing a line to a log file stands in for sending email). A separate worker process claims jobs, runs them, marks them done. Failed jobs retry up to 3 times, then land in a dead-letter state a human can list.

Verification. Create 5 contacts fast: 5 jobs appear, worker drains them, log shows 5 welcomes, no duplicates. Kill the worker mid-queue, restart it: the remaining jobs complete, none lost, none doubled. Force one job to always fail: exactly 3 retries, then dead-letter, worker moves on.

Escalation notes. New here: two cooperating processes, crash-safety as a verification criterion, and P.9's queue tradeoffs stop being theory. The kill-the-worker test is the rep: designed cruelty toward your own system.

Rung 9: Ship it (deployment)

Brief. Software on your laptop is a rehearsal. Put the app on the public internet, configured like an adult.

Requirements. The rung 6 to 8 app deployed to a real host (any mainstream platform with a free or hobby tier works; have your tutor compare current options rather than trusting a list that will age). Secrets in environment variables, not in the repo (P.2's env vars, now with stakes). A /health endpoint. You can read your production logs.

Verification. Sign up and use the app from your phone, off your home network. Grep your repo for the database credential: zero hits. Hit /health from anywhere: 200. Trigger an error on purpose and find it in the production logs within two minutes.

Escalation notes. New here: environments differ, and the difference is configuration. Half the pain of deployment is P.2 and P.9 wearing a trench coat: PATHs, env vars, and state that did not survive the trip. Expect two hours of confusion; it is part of the rep, not a sign you failed.

Rung 10: Mini SaaS

Brief. The graduation rep: a small tool a stranger could sign up for and use without you present. Not a business, a complete artifact.

Requirements. Pick a niche you know (you have a sales-ops brain; use it: a lead tracker, a follow-up cadence tool, a pipeline report generator). Signup, login, the core workflow, a plans flag (free versus pro gating one feature; simulated payment is fine, the gate is the rep), an admin view listing users, deployed, with jobs and webhooks where they naturally fit.

Verification. One person who is not you signs up unassisted and completes the core workflow, while you watch silently and take notes. The pro gate blocks free users and admits pro users. Your admin view shows the real usage. Every rung 6 to 9 verification still passes in production.

Escalation notes. New here: nothing technical, and that is the lesson. Rung 10 is integration: the moment you realize every hard part was a previous rung. The watching-silently test will hurt and teach more than any code review; write down every place your user hesitated.

The debugging gym

Building reps teach construction; the gym teaches diagnosis, which is the rarer skill and the one agents most need from you. Three programs follow, each with exactly one planted bug of escalating subtlety. The protocol, strictly: read the program and find the bug by reasoning before you run it. Write your diagnosis in your notes: the symptom you predict, the mechanism, the fix. Then run it to confirm, fix it, run again. Checkpoint P requires two of the three, diagnosed in writing.

Gym 1. Symptom class: loudly, visibly wrong.

gym-1.jsjs
// gym-1.js: commission report
// Tiers: 10% on deals of 10k and up, 7% from 5k, otherwise 4%.

const sales = [4800, 12500, 900, 7300, 15200];

function commissionFor(amount) {
if (amount >= 10000) return amount * 0.1;
if (amount >= 5000) return amount * 0.07;
return amount * 0.04;
}

function totalCommission(allSales) {
let total = 0;
for (let i = 0; i <= allSales.length; i++) {
  total += commissionFor(allSales[i]);
}
return total;
}

console.log(`Total commission: $${totalCommission(sales)}`);

Predict the exact output before running. Hint discipline: if stuck for ten minutes, ask your tutor for the category ("look at the loop bounds"), never the answer.

Gym 2. Symptom class: plausible but wrong. The output looks like a report; it is lying.

gym-2.jsjs
// gym-2.js: weekly leaderboard
// Prints the top three reps by deals closed this week.

const reps = [
{ name: "Dana", closed: 9 },
{ name: "Marcus", closed: 21 },
{ name: "Priya", closed: 3 },
{ name: "Tom", closed: 108 },
];

function topThree(allReps) {
const scores = allReps.map((r) => r.closed);
scores.sort();
const cutoff = scores[scores.length - 3];
return allReps
  .filter((r) => r.closed >= cutoff)
  .sort((a, b) => b.closed - a.closed);
}

console.log("Top three this week:");
for (const rep of topThree(reps)) {
console.log(`  ${rep.name}: ${rep.closed}`);
}

Expected: the top three of four reps. Trace scores through the sort by hand, writing the array's actual order down. The mechanism here bites JavaScript developers with years of experience.

Gym 3. Symptom class: the code is wrong about being wrong. Every line looks reasonable; the arithmetic itself is the trap.

gym-3.jsjs
// gym-3.js: invoice check
// The quote total was $410.00. Verify the line items match it.

const lineItems = [
{ desc: "Usage credits", price: 105.91 },
{ desc: "Platform fee (prorated)", price: 165.39 },
{ desc: "Seats, 3 of them (prorated)", price: 138.7 },
];

function invoiceTotal(items) {
let total = 0;
for (const item of items) {
  total += item.price;
}
return total;
}

const total = invoiceTotal(lineItems);

if (total === 410.0) {
console.log("Invoice matches the quote. Safe to send.");
} else {
console.log(`MISMATCH: invoice total is $${total}, quote was $410.00.`);
console.log("Flagging for manual review.");
}

Check the arithmetic on paper first: the three prices sum to exactly 410.00. Then run it. Your diagnosis must explain the gap between your paper and your terminal, and your fix must name the general rule (P.7 planted it) that makes this entire bug class impossible.

The post-mortem habit

After every rung and every gym bug, five minutes, three questions, written where you will see it again (your notes here, or your own system):

PromptPost-mortem, then file it

Here is my post-mortem for PROJECT/BUG:

  1. What broke or surprised me, specifically?
  2. What was the actual mechanism, in one sentence a beginner could read?
  3. What will I do differently on the next rung because of this?

Interview me for two or three follow-up questions to sharpen it, then give me back a tightened version worth keeping.

This is the component people skip, and the difference it makes is not subtle. Reps without reflection build familiarity; reps with reflection build judgment. Ten projects and ten honest post-mortems will outperform thirty projects without them, and you will be able to watch it happening in your own writeups: by rung 6 your post-mortems start predicting problems instead of reporting them.

CheckpointPrequel checkpoint: reps on the board

Complete the first three rungs of the ladder and at least two gym diagnoses, with post-mortems. This checkpoint is the prequel's exit: it certifies you can build a small real thing, verify it honestly, and diagnose by reading. If boxes are unchecked, the gap will not close on its own: Part 0 onward assumes every one of them, and unverified ground compounds. The ladder itself continues alongside the whole course: rungs 4 to 6 pair naturally with Parts 1 and 2, rungs 7 to 10 with Tracks A and B.

Knowledge check

Knowledge check

Q1Why does the ladder force agent-as-tutor for rungs 1 to 3 before allowing agent-as-builder later?
Q2You finished rung 3 and everything seems to work when you use it casually. You skip the listed verification and move on. What did you actually skip?
Q3The gym requires diagnosing bugs by reading before running the program. Why handicap yourself when running it would reveal the symptom instantly?
Q4By rung 10, the technical escalation notes say 'nothing new.' What is rung 10 actually testing?

Sources

The ladder and gym are original course material with no external claims to verify. All three gym programs were executed during authoring (July 2026) and produce the buggy behavior their planted defects predict; the fixed versions produce the correct output described in each diagnosis. Rung 9's hosting-platform choice is deliberately delegated to a live conversation with your tutor, because any specific platform recommendation printed here would be the first thing in Part P to rot.