Skip to content
GitHub Copilot Train-the-Trainer
Trainer Guide

Session M2-06 trainer guide: Repair and refactor a broken Python application

Delivery objective

Teach learners to repair a deliberately broken inventory-reconciliation application, then improve its design without changing its output. Keep each repair small enough to explain and check.

Check that each learner finishes with a working command-line application, added regression tests, and a completed repair log. Use file comparisons to check that the refactor preserved the supplied text and JSON outputs.

Important

Check GitHub Copilot access before the session. If Copilot access is missing or unavailable in the training environment, stop and do not continue.

One-hour plan

Time Segment
0:00-0:08 Start repairs from the failure output
0:08-0:18 Read Python failures in execution order
0:18-0:30 Demonstrate syntax and import recovery
0:30-0:42 Use fixtures to recover behavior
0:42-0:52 Add regression tests before refactoring
0:52-1:00 Refactor boundaries and hand off to the lab

Trainer preparation

Before delivery:

  1. Open the repository in its Dev Container or a Codespace.
  2. Check Python 3.11 or later.
  3. Check that GitHub Copilot Chat and inline suggestions work.
  4. Create a private working copy of lab/starter/.
  5. Install requirements-dev.txt in that copy.
  6. Run the starter command and preserve the first failure.
  7. Run the solution tests.
  8. Keep lab/solution/ closed until the lab review.
  9. Read the supplied .github/lsp.json, prepare pyright-langserver, and check CLI /plan and /lsp test python in the copied repository.

Use these commands from the session directory:

cd lab/starter
python -m venv .venv
source .venv/bin/activate
python -m pip install -r requirements-dev.txt
python -m inventory_reconciliation \
  --warehouse fixtures/input/warehouse.csv \
  --ledger fixtures/input/ledger.csv \
  --output-dir build

The expected first failure is SYN-01 in inventory_reconciliation/cli.py. Do not fix later faults during this demonstration.

Teaching model

The repair ladder

Start at the first layer that prevents you from checking the next:

Layer Question Check
Parse Can Python read every imported file? python -m compileall inventory_reconciliation
Import Can the package and entry point load? python -c "import inventory_reconciliation.cli"
Start Can the command show help? python -m inventory_reconciliation --help
Behavior Does the output match the contract? pytest -q and fixture comparison
Structure Can the code change safely? regression tests stay green

Skipping a layer obscures the current failure. A test suite cannot check business logic while test collection is blocked by a syntax error.

One failure at a time

Teach this loop:

  1. Capture the exact command and failure.
  2. State one hypothesis.
  3. Ask Copilot for the smallest relevant explanation or change.
  4. Inspect the proposed edit.
  5. Run the narrowest useful check.
  6. Record the result in the repair log.

Have learners make the diagnosis rather than accept Copilot's suggestion without checking it. Use command output to test each hypothesis.

Controlled starter sequence

The starter uses named faults so every learner can compare progress without sharing source-level answers.

Order Fault Surface Intended signal
1 SYN-01 cli.py Parser stops at the first malformed function definition
2 SYN-02 service.py The next imported module cannot parse
3 IMP-01 cli.py Startup imports a module that does not exist
4 IMP-02 reporting.py Startup imports a symbol that does not exist
5 LOG-01 parsing Duplicate SKU rows overwrite earlier quantities
6 LOG-02 reconciliation SKUs present only in the ledger disappear
7 LOG-03 rules Severity thresholds do not match the contract
8 STATE-01 service Repeated runs leak results through global state
9 DESIGN-01 service/reporting Rules are duplicated and control flow is tangled

The first four faults should appear in order when learners use the checks in the lab. The behavior defects are deterministic and exposed by the supplied fixtures.

Prepared demonstration

1. Capture the first failure

Run:

python -m inventory_reconciliation \
  --warehouse fixtures/input/warehouse.csv \
  --ledger fixtures/input/ledger.csv \
  --output-dir build

Read the traceback from the bottom. Point out the file, line, exception type, and source line. Ignore every later concern until Python can parse that file.

2. Ask Copilot about one failure

Use a prompt such as:

Explain this SyntaxError using only the shown file and traceback. Identify the
smallest syntax repair. Do not refactor or change behavior.

Show that a good repair prompt includes the traceback and names what may change.

3. Check the repair

Run:

python -m compileall inventory_reconciliation

The command should expose SYN-02, the next startup fault.

4. Stop before solving the lab

Show how to add an entry to repair-log.md:

Field Example
Failure SYN-01
Failure output SyntaxError in cli.py
Hypothesis Function header is incomplete
Change Small syntax-only edit
Check Re-run compile command
Result Parser advances to SYN-02

Do not demonstrate the behavior fixes.

Behavioral contract

The fixture pair represents two views of stock:

  • warehouse.csv contains physical counts;
  • ledger.csv contains expected counts.

The intended rules are:

  1. Trim surrounding whitespace and uppercase each SKU.
  2. Sum duplicate rows after normalization.
  3. Reject negative quantities with a row-specific error.
  4. Reconcile the union of SKUs from both files.
  5. Calculate variance = warehouse - ledger.
  6. Use MATCH for zero, SHORT below zero, and OVER above zero.
  7. Use severity none for zero, review for absolute variance 1-5, and urgent for absolute variance 6 or more.
  8. Sort report rows by SKU.
  9. Keep repeated runs isolated.

Show learners how the expected text and JSON files define the result. Do not let them change those files to make broken code pass.

Regression tests before refactoring

The starter tests cover the fixture-level contract and repeated-run isolation. Learners must add at least two focused tests:

  • the boundary between review and urgent;
  • rejection of a negative quantity with file and row context.

They may add more. Each test should name one rule and fail for one reason.

Refactoring targets

Keep these five jobs in separate files:

Boundary Responsibility
parsing.py Read, validate, normalize, and aggregate CSV data
rules.py Classify variance status and severity
service.py Reconcile two quantity maps
reporting.py Render stable text and JSON output
cli.py Parse arguments, call the service, write files

Explain that the starter mixes these responsibilities on purpose. Have learners remove the module-level result list, call one rule implementation, and turn nested branches into named functions.

Do not ask for a large rewrite. First add tests for the expected behavior. Then make one structural change at a time.

Spend five minutes of the refactor lab block on an actual Python LSP lookup. Ask for the definition and references of severity_for; inspect the operation or /lsp logs and check the returned locations. Then inspect service.py for inline copies of the same rule. Those copies need not appear as symbol references. Record both before approving /plan. Do not use LSP diagnostics to skip the ordered syntax and import repairs.

Stop if the required server or lookup cannot run. A startup check or text search does not replace the operation. Keep the lab at 120 minutes by using this lookup inside the existing 25-minute refactor block.

Useful Copilot prompts

Repair prompt:

Use the traceback and this file only. Suggest the smallest change that resolves
the current failure. Preserve behavior and do not refactor.

Test prompt:

Write one pytest test for the severity boundary at absolute variance 6. Follow
the existing test style. Do not change production code.

Refactor prompt:

/plan The tests are green. Plan removal of global state and duplicated
classification logic using the checked references and inline duplicates.
Preserve public signatures and output order. Name protecting tests. Do not edit.

Review prompt:

Review the diff for behavior changes. Compare it with the supplied fixtures,
expected outputs, and tests. Call out any unproved assumption.

Facilitation notes

  • Keep learners on the current failure. Later defects are distractions until the program reaches them.
  • Require repair-log entries before broad edits.
  • Use a new failure after a repair to decide what to investigate next.
  • Reject changes to expected outputs unless the class first proves the contract is wrong.
  • Ask learners to explain each Copilot suggestion before they accept it.
  • Pair learners when one is stuck for more than eight minutes.
  • Keep the solution hidden until the review.

Common wrong turns

Wrong turn Coaching response
Rewrite the application from scratch Return to the first failing command and preserve the supplied interface
Ask Copilot to "fix everything" Narrow the prompt to one failure and one file
Change fixtures to match broken output Restate that fixtures are the contract
Refactor while imports still fail Restore startup first
Remove the repeated-run test Explain that global state often passes once and fails in a process
Mock all file reads Keep at least one fixture-level test through the real parser

Lab handoff

Learners submit:

  • a completed repair log;
  • a working command-line application;
  • at least two added regression tests;
  • unchanged expected-output fixtures;
  • a refactored design with no process-wide result state;
  • passing pytest output and generated reports.

Success means the same inputs produce the supplied outputs before and after the refactor.