Lesson 2: Write the task contract before the change
Objective
You can read TASK.md as a set of testable statements, trace each statement
to a test in tests/test_report_tool.py, and write one contract line for your
own project using the repository’s task-contract template.
Why this lesson exists
Vague requests produce vague code. “Handle bad rows better” gives an agent
permission to guess; “invalid rows are collected in errors with index and
reason, valid rows still produce a report” gives it a target and gives you a
way to check. The contract is where you decide what “done” means — before the
agent burns your trust with a plausible-looking rewrite.
The lesson
Open TASK.md. Notice what every line has in common: it names an observable
behavior, not an implementation. Four statements from the contract, and the
tests that pin them:
| Contract statement | Pinned by |
|---|
| JSON data files load as lists of objects | test_load_json_records_returns_list_of_dicts |
Unsupported extensions raise ValueError | test_unsupported_suffix_raises_value_error |
Invalid rows are isolated with {"index", "reason"}; valid rows still aggregate | test_invalid_records_are_isolated_with_reasons |
| Group totals are rounded to two decimals | test_group_totals_are_rounded_to_two_decimals |
Also notice the boundary clauses — the lines that say what the change may NOT
do: no new dependencies, change only starter/report_tool.py, standard
library only. Boundary clauses are what keep an agent’s “helpful” rewrite from
swallowing your whole file.
The full template lives in the companion repository
(templates/TASK_CONTRACT.md). Its fields: user outcome, current behavior,
expected behavior, inputs and outputs, allowed files, out of scope, failure
and recovery, acceptance commands, and authority (network/commit/push). Every
field you leave blank is a decision you delegated to the machine.
Exercise
Write the next contract yourself, small and real:
- Pick one script you actually own (a report, a scraper, a sync job).
- Write three contract statements for it in the table form above — behavior,
not implementation.
- For each statement, write the name of the test that would pin it.
- Add one boundary clause (a file the agent must not touch, or a dependency
it must not add).
Ask the agent to critique your contract against TASK.md — it should find
ambiguities, not add features.
Checkpoint
Show your four lines to the agent and ask: “Which of these could you satisfy
by cheating — passing the test without the behavior?” If neither of you can
find a cheat path, your contract is concrete enough. Fix any line that fails
this test.
Expected evidence
Your contract (4 lines) and the cheat-review result. In Lesson 3 the agent
will code against the course’s TASK.md only — yours ships in Lesson 5.