Lesson 2: Write the task contract, let cordis.yml carry the setup
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 make working rules
part of the composed agent instead of a prompt you have to retype.
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. With a composed agent there is a second failure
mode: the rules you typed into one session are not in the next — unless
they live in the composition.
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 line | Test |
|---|
| “a JSON file loads into the same record list as CSV” | test_load_json_records_returns_list_of_dicts |
“invalid rows land in errors with index and 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 |
| “the report write is atomic and creates missing parents” | test_write_report_creates_missing_parent_directories |
Now the durable half. In DeepSeek Harness the agent is declared in
cordis.yml: the model plugin, the tool plugins it may use, the session
storage where trajectories land, and the instructions it starts with. Put
this course’s working rules where they survive restarts — in the agent’s
configured instructions, for example:
- Change only starter/report_tool.py. tests/, solution/, scenario/ are read-only.
- One failing test group per step; run `python verify.py starter` after each.
- Standard library only — no new dependencies.
Start a new session and ask the agent to state its working rules. If it
quotes yours back, the composition is carrying them; if not, check whether
the instructions actually landed in the configuration the session loaded.
Exercise
Write one contract line for a script you actually own, using the same
shape: inputs, outputs, error cases, and “done means <command> exits 0”.
Then write the two rules you would put in that project’s agent
composition.
Checkpoint
Run python verify.py — this checkpoint’s claim code prints when
you can answer:
- Which
TASK.md line does
test_invalid_records_are_isolated_with_reasons pin, in your own
words?
- Which artifact carries rules across sessions — the prompt, or the
cordis.yml composition?
- Why does an append-only trajectory make a composed agent easier to
audit than a chat transcript?
Expected evidence
Your drafted contract line, your two rules, and the new session’s
statement of its working rules.