- Step 14 of 17 in the agent-building course
Use GitHub Copilot to Fix a Stale-Delete Bug You Can Verify

A delete function takes a version number and then ignores it. The normal test passes: Alice's preference disappears and Bob's stays. The problem shows up later, when an old, delayed request comes in and deletes Alice's newer preference.
That makes it a good first task for a coding assistant. The fix is small, and you can see for yourself whether it worked.
You'll use Copilot in VS Code to look at the bug, propose a fix and then review the patch. The download includes a reference solution written by hand and the test results from running it. It doesn't include a recorded Copilot session, and nothing here claims Copilot wrote that solution.
Set up the exercise and Copilot separately
You need Python 3.10 or newer, Git, VS Code and access to GitHub Copilot in VS Code. Sign in through VS Code's Copilot setup, and check that your account or company allows the model and tools you pick. You won't need Copilot's cloud agent or any deployment.
Download and unzip the stale-delete coding exercise into a throwaway folder. It uses only Python's standard library and made-up records. Look at these files yourself before you open a chat with the model:
| File | Purpose |
|---|---|
store.py | Intentionally incomplete delete function to fix |
test_store.py | Six real SQLite tests defining expected behavior |
reference_store.py | Authored solution for comparison after attempting the task |
reference.patch | The concrete SQL change in patch form |
check_exercise.py | Verifies the supplied starter and reference results |
If you want to see what Copilot comes up with on its own, don't give it the reference files. And don't point the broken starter code at a real database.
From the unzipped folder, save a starting point in Git and run the tests:
git init
git add store.py test_store.py
git commit -m "Record stale-delete exercise baseline"
python3 -m unittest test_store -v
Use python if that's your Python command. Git needs your name and email set up before it will commit.
You should see six tests run: four pass and two fail. The two failures are on purpose. One deletes with an outdated version. The other deletes a value after it's been recreated.
Look at what the tests expect
The function takes an expected_version, but the starter code runs this:
DELETE FROM preferences WHERE owner = ? AND key = ?
It should be stricter. Delete only when the owner, the key and the exact version all match. If any of them don't, return false and leave every row alone. A second try after a successful delete also returns false.
The version strings here point at one specific saved copy of a record. They aren't user names or passwords.
The tests use a real temporary SQLite file, not a fake database. One test closes the connection after deleting and opens a new one. That checks the delete stuck between connections. It doesn't test a crashed process or a remote server.
Ask Copilot for a plan using the relevant files
Open the exercise folder in VS Code and open Copilot Chat. GitHub's IDE chat guide covers its three modes: Ask, Plan and Agent. Pick Plan first.
Attach store.py and test_store.py with Add Context → Files & Folders, or type # and pick each file. The VS Code context guide explains both ways.
Send this:
Inspect store.py and test_store.py. Explain why the two stale-delete
cases fail and propose the smallest change. Keep all six tests intact.
The function must delete only the exact owner, key and expected_version.
Do not add dependencies, network calls, schema changes or a new API.
Do not inspect reference_store.py or reference.patch for this attempt.
Do not edit files yet.
A good plan spots the missing version check and keeps the existing owner and key check.
Watch for three wrong turns. Deleting a test, accepting outdated versions, or looking up the newest version right before deleting all change the task. That last one is sneaky. It would delete a record the caller never actually looked at.
Attaching files tells Copilot where to focus. It doesn't stop it from opening other files in the folder. So only use a folder you're fine with the assistant reading.
Implement and inspect the diff
Once the plan looks right, click Start Implementation to hand it to Agent, as GitHub's guide describes. Ask it to change only store.py and then run python3 -m unittest test_store -v. Leave VS Code's approval prompts on, and read any command it wants to run before you allow anything outside this exercise.
The reference fix adds the version check and passes in a third value:
- "DELETE FROM preferences WHERE owner = ? AND key = ?",
- (owner, key),
+ "DELETE FROM preferences WHERE owner = ? AND key = ? AND version = ?",
+ (owner, key, expected_version),
The ? placeholders matter. The fix shouldn't paste owner, key or version straight into the SQL text, because that opens the door to SQL injection. It should also keep the existing transaction block, and keep returning true or false based on whether exactly one row was deleted.
Open the Source Control diff for store.py in VS Code, then check test_store.py for changes you didn't ask for. Or run:
git diff -- store.py test_store.py
python3 -m unittest test_store -v
Run the tests yourself, even if the assistant says they pass. You're done when all six original tests pass.
GitHub also offers an optional Copilot review of uncommitted changes. That's just another model reading your code, and it depends on your account. It doesn't replace reading the diff and running the tests.
Compare against the executed reference
Here's how the starter and the reference solution did on the six tests:
| Case | Starter | Reference |
|---|---|---|
| Matching version deletes only Alice | Pass | Pass |
| Unknown owner has no effect | Pass | Pass |
| Retry does not delete another row | Pass | Pass |
| Stale version preserves current value | Fail | Pass |
| Recreated value survives an old delete | Fail | Pass |
| Successful deletion survives reopening | Pass | Pass |
reference.patch was applied to a fresh throwaway copy, and the patched store.py passed the same six tests. check_exercise.py separately confirms that the original starter fails the two expected tests and the reference passes all six.
One gotcha. Once you've fixed your own store.py, that checker will complain that the starter no longer fails. That's expected. Use the normal unittest command to check your finished work.
If Copilot writes a different patch, judge it against the rules above. A bigger rewrite can be correct, but it needs a reason. Write down anything extra it does, rather than accepting it just because the tests pass.
Prove the test catches the bug
In a throwaway copy of your finished exercise, remove only AND version = ? and the matching value. Run the tests again. The two stale-delete tests should fail. Then put the version check back.
Breaking the code on purpose tells you more than a count of passing tests. It shows the tests catch the exact mistake this task is about. They still don't test logins, someone tampering with the database, changes from another server at the same moment, or backups.
Keep a short record: the starting failures, the final patch, the exact command you ran and its result. If you do the exercise, also note which Copilot model you used and how the session went. The reference solution here can't give you that.
Next, try the Cursor app-slice exercise, which uses the same approach on a small screen. The course index has the full order.



