Lesson 2: Write the task contract, let .github instructions carry the rules
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 durable
instructions a Copilot session picks up automatically — and you can prove
from the response’s References that they were used.
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. In Copilot there is a second failure mode: a rule you typed
into one chat does not exist in the next. The contract fixes the task;
instructions files fix the working rules.
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. Copilot sessions read two kinds of instructions
files:
- Always-on:
.github/copilot-instructions.md at the workspace root
(or AGENTS.md, which Copilot also reads). Every request in the
workspace gets it.
- Targeted:
.github/instructions/<name>.instructions.md with YAML
front matter. applyTo is a glob relative to the workspace root; VS
Code attaches the file when the agent creates or modifies a matching
file. description lets the agent load it on demand when the task
matches.
This course’s working rules only matter when the course folder is being
changed, so they belong in a targeted file. Tell the agent:
“Create .github/instructions/flypython-course.instructions.md with
front matter name: FlyPython course rules, a one-line description, and
applyTo: 'courses/hands-on-with-github-copilot/**', containing these
working rules: only starter/report_tool.py may change; tests/, solution/,
scenario/ are read-only; one failing test group per turn; run
python verify.py starter after each; standard library only.”
Read the file it creates before you approve anything else — the applyTo
glob is the part most often wrong.
Then open a new chat session and ask it to change a docstring in
starter/report_tool.py and to summarize the rules it is following. Expand
References in the response: the instructions file should be listed. If
it is not, open the Agent Customizations editor and check the file is
discovered, then check that applyTo matches the path the agent edited.
Undo the docstring change before you continue (Lesson 3 shows how).
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 two rules for that project and decide which belongs in
.github/copilot-instructions.md and which in an applyTo file.
Checkpoint
When you can answer the questions below, claim this checkpoint with
python verify.py --attest l02:
- Which
TASK.md line does test_invalid_records_are_isolated_with_reasons
pin, in your own words?
- Which mechanism carries rules across sessions — the chat prompt, or an
instructions file?
- When does an
applyTo instructions file get attached, and how did you
confirm that yours was?
Expected evidence
Your drafted contract line, your two rules with their file choice, and the
References list from the new session showing the instructions file.