Skip to content

Tutorial 2 How the Code Works

James Morris edited this page Jul 29, 2026 · 1 revision

Tutorial 2 · How the Code Works

Goal: understand the handful of design choices that make this project easy — and safe — to change with an AI agent. You don't need to memorize the code; you need the mental model, so you can direct the agent precisely.

← Prev: Tutorial 1 Orientation · Next: Tutorial 3 Your First Agent Task


The map

lockedin-cli/
├── bin/lockedin.js      # entry point — the ONLY part that does input/output
├── src/lockedin.js      # the core — pure functions that return strings
├── test/
│   ├── unit.test.js     # tests the core functions directly
│   └── cli.test.js      # spawns the real binary and checks its output
├── tools/screenshot.js  # renders the SVGs you see in docs/
└── package.json         # metadata + the `npm test` script

Four ideas make it tick. Learn these and you can ask the agent for almost any change with confidence.

Idea 1 — A pure core that returns strings

Open src/lockedin.js. Notice that the render functions don't print — they return strings:

function renderConnect() {
  const names = shuffle(NAMES).slice(0, 6);
  // ...builds up an array of lines...
  return out.join('\n');   // returns text; prints nothing
}

All the actual printing (and reading your keystrokes) happens in bin/lockedin.js. This "core returns data, edge does I/O" split is the single most important thing here, because a function that just returns a string is trivial to test:

const out = app.renderConnect();
assert.ok(out.includes('professional network'));

No mocking a terminal, no capturing stdout. When you ask the agent to add a feature, this is why it can also add a fast, reliable test for it.

Idea 2 — Content pools

The jokes live in plain arrays near the top of src/lockedin.js:

const HOOKS = [ 'Unpopular opinion:', 'Let that sink in.', /* ...~25 total... */ ];
const LESSONS = [ '→ Your network is your net worth.', /* ... */ ];

Generators pick from these pools. In v1.2.0 every pool grew to ~25 variations, and a second layer of template pools was added (things like POST_REVEALS and REFLECT_TAGSETS) so the structure of each output varies, not just the words. Adding content is often as simple as adding strings to an array — a perfect first task to hand an agent.

Idea 3 — Seeded randomness = deterministic tests

Random output and automated tests sound incompatible. This project squares that circle with a seedable random number generator:

setSeed(42);
const a = generatePost();
setSeed(42);
const b = generatePost();
// a === b  → same seed, same post

Every random choice goes through the shared pick() / shuffle() helpers, which draw from that seeded generator. Set LOCKEDIN_SEED=42 and the CLI is fully reproducible. The golden rule for any change: new randomness must use pick / shuffle, never Math.random directly — otherwise determinism (and the tests) break. Keep this in your back pocket; you'll remind the agent of it in Chapter 3.

Idea 4 — Two layers of tests

  • test/unit.test.js calls core functions and asserts on the strings they return (fast, precise).
  • test/cli.test.js actually spawns bin/lockedin.js and checks its real stdout and exit code (proves the whole thing works end-to-end).

Together they're the gate. They also encode invariants — promises the app makes. For example, a post always opens with a known hook and always contains at least three lessons; the "connect" screen always says incalculable. When an agent changes code, these tests catch anything that quietly broke a promise.


Why this matters for working with an agent

Put the four ideas together and you get a codebase where an agent can:

  1. Add a feature as a pure function that returns a string,
  2. Add a test for it without any I/O gymnastics,
  3. Keep output reproducible via the seeded RNG, and
  4. Prove it didn't break anything by running the existing gate.

That's the loop you'll run in the next chapter.

✅ Try it with your agent

Ask your agent to verify your understanding — reading, not editing:

  1. "List every content pool in src/lockedin.js and how many entries each has."
  2. "Show me one invariant that test/unit.test.js enforces about generatePost, and explain what would break it."
  3. "Where is the seeded RNG defined, and which functions must use it instead of Math.random? Don't change anything — just explain."

When the agent's answers match this chapter, you're ready to build.

Next: Tutorial 3 Your First Agent Task

📘 LockedIn CLI wiki

Tutorial

Reference


Satire · Sátira · 風刺. Not affiliated with LinkedIn. GPL-3.0-or-later.

Clone this wiki locally