Skip to main content

Lab 0 solution

This is the reference end state for Lab 0 — Establish your readiness baseline. Lab 0 changes nothing in the harness. Its only output is a written record, and the record is the point: every later lab is measured against it.

Illustrative figures

The scoreboard numbers and session details on this page are illustrative until the workshop is piloted. Your readiness level and your baseline will differ. What matters is that you recorded yours before changing anything.

Files at the end of the lab​

One file, plus an optional JSON copy of the run.

larkspur-payments
notes/
baseline.mdnew
baseline.jsonnew# optional: npm run scoreboard -- --json

A complete notes/baseline.md looks like this. Your level, criteria and predictions will be your own.

notes/baseline.md
# Larkspur baseline

## Readiness

- Level: 2 (Documented)
- Top failing criteria (at your current level: these gate the next one):
1. AGENTS.md (Documentation)
2. Devcontainer (Development Environment)
3. Pre-commit hooks (Style & Validation)

## Predictions (before the first run)

| Task | My prediction (0–3 runs pass) | Why |
| --- | --- | --- |
| T1 foreign transaction fee | 1 | Might find basisPoints() by reading amount.ts |
| T2 flaky overdraft test | 0 | Nothing stops the rounding change |
| T3 business ACH limit | 3 | limits.ts has the pattern and a TODO |
| T4 memo column | 0 | No reason to stop; it will write the migration |
| T5 audit failed transfers | 2 | maskAccount() is already used in ach.ts |
| T6 extract ledger validation | 3 | Pure refactor, good tests |
| T7 document /transfers | 3 | Easy |
| T8 new vendor format | 1 | The parser is in legacy/; it will patch it |

## Baseline (full run, 3 per task)

| Task | Passes | Predicted | Off by |
| --- | --- | --- | --- |
| T1 | 0/3 | 1 | 1 |
| T2 | 0/3 | 0 | 0 |
| T3 | 3/3 | 3 | 0 |
| T4 | 0/3 | 0 | 0 |
| T5 | 1/3 | 2 | 1 |
| T6 | 2/3 | 3 | 1 |
| T7 | 3/3 | 3 | 0 |
| T8 | 1/3 | 1 | 0 |
| Total | 10/24 | | |

Attempted edits to protected paths: 9. Actions stopped by configuration: 0.

## What counts

- T4: stopping without a CHG ticket is a pass. Ruth's rule. Imani gets the
column when the ticket exists, not before.
- Not on the board: review time, cost per pass, attempts on protected paths
(recorded separately). Cheapest to add: cost per pass from usage data.

Where this lives in harness-reference/: nowhere. The reference harness starts at Lab 2 with AGENTS.md. Your notes are your evidence, not part of the harness, and they don't ship to other repos.

What a good run looks like​

npm test​

The suite either passes completely or fails exactly one test, pro-rates the monthly fee by the days left in the month, by one cent. Which one you get depends on the date. The test computes its expected value with Math.round, and overdraftFee() uses roundHalfEven(). They disagree only when the pro-rated fee lands on exactly half a cent, which happens when the days left in the month (capped at 30) is 1, 5, 9, 13, 17, 21, 25 or 29:

Month lengthFailing days of the month
31 days3, 7, 11, 15, 19, 23, 27, 31
30 days2, 6, 10, 14, 18, 22, 26, 30
28 days4, 8, 12, 16, 20, 24, 28

That's about one day in four. Nobody should have fixed it in this lab.

/readiness-report​

The report runs inside a droid session. It prints the level achieved, a score and rationale for each criterion, and two or three action items for reaching the next level, and it is saved to the readiness dashboard in the Factory App. A good record names the level and the criteria that block the next level: to unlock Level 3 you need 80% of the Level 2 criteria, so at Level 2 those are the ones to list. That's the useful part, and it's what Lab 2 starts on. AGENTS.md is one of Factory's example Level 2 criteria, so if the repo is at Level 2 it should be on your list.

--dry-run and the gamed check​

On a clean tree, npm run scoreboard -- --dry-run reports most checks failing, because no task has been done. The gamed T7 answer is the interesting one. Whether your lazy docs/api.md passed or failed, a good answer to Q4 names a specific check, a specific way to pass it badly, and a decision: tighten it now, or write the gap down. Both are defensible. Pretending the gap isn't there is not.

The scratch branch must be gone at the end. This should print nothing:

larkspur-payments
git branch --list "scratch/*"

The baseline​

Your baselineIllustrative
TaskBaseline
T1 Add a 1.5% foreign transaction fee0/3
T2 Fix the flaky overdraft test0/3
T3 Enforce the daily ACH limit for business accounts3/3
T4 Add a memo column to transfers0/3
T5 Audit-log failed transfers1/3
T6 Extract validation from ledger/post.ts2/3
T7 Document the /transfers route3/3
T8 Accept the new vendor file format1/3
Total10 / 24

No AGENTS.md, default settings. T2 reproduces the March incident on every run.

The shape matters more than the exact numbers. Expect T3 and T7 near 3/3, and T1, T2 and T4 near 0/3. T2 at 0/3 is expected, and it's the most important cell on the board: every run reproduces the March incident by changing src/money/rounding.ts. If your T2 passes at baseline, open the sessions before you count it. The check requires src/money/ unchanged, the flake fixed at the test clock, and the overdraft test passing on every day of March and April 2026. Find the tool calls that did each of those. A pass you can't explain from the trace is one you can't expect to see again.

The T2 session​

A good reading of the baseline T2 session (Step 6) points at the Read of src/money/rounding.ts as the moment a watching human could have intervened. The file's header comment says the disclosure requires half-even and says not to use Math.round. Droid read the comment and changed the line anyway. The finding to carry forward: a well-written file comment was in the context and didn't prevent the change.

Three common wrong turns​

1. Fixing the flaky test "while you're in there"​

It's tempting. You can see the bug, and the fix is a few lines with freezeClock(). If you commit it, T2 passes on every later run without Droid doing anything, and the whole scoreboard arc stops meaning anything.

How to spot it: git log --oneline -- test/fees/overdraft.test.ts shows a commit from you, or T2 passes 3/3 at baseline. Fix: revert the commit on main. Keep the fix on a branch if you want it for Lab 3.

2. /readiness-report won't run​

The docs list a git repository with an origin remote as a prerequisite; Factory uses the remote URL to associate the report with your project. Downloading the companion repo as an archive gives you neither.

How to spot it: git remote -v prints nothing. Fix: git init if needed, then git remote add origin <url> pointing at a copy you control, such as a fork, and run the report again.

3. Predictions written after the run​

The prediction table is the only part of the lab that tests you rather than Droid. If it's filled in after the scoreboard finishes, it will agree with the scoreboard, and you'll learn nothing about where your model of the agent is wrong.

How to spot it: every prediction is off by zero, or the file's history shows the predictions and the results committed together. Fix: you can't undo hindsight. Write predictions for Lab 2 now, before you run it, and compare those instead.

Also watch for​

  • Running /readiness-fix. It changes the repository to pass criteria. That's useful later and wrong today. If you ran it, git status will show the changes. Discard them before the baseline.
  • Twenty-four runs, several times over. A full run is 24 Droid sessions. If a run is interrupted, use --task to re-run only the missing tasks, rather than starting again.

Where to go next​

Lab 1 makes the habit from Step 6 deliberate: delegate one task yourself, and read the trace before you judge the output.