Skip to content
Zarif Automates

Use GitHub Copilot to Fix a Stale-Delete Bug You Can Verify

ZarifZarif
|Published |Updated

A delete function accepts an expected record version but ignores it. The normal test passes: Alice's preference disappears and Bob's stays. The failure arrives later, when an old request deletes Alice's newer preference.

This is a useful coding-assistant task because the change is small and the acceptance criteria are observable. You will use Copilot in VS Code to inspect the bug, propose a fix and review the patch. The download includes an authored reference solution and executed test results. It does not contain a recorded Copilot session or a claim that Copilot produced 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 that editor. Sign in through VS Code's Copilot setup and check that your account or organization permits the selected model and tools. This lesson does not require Copilot's cloud agent or a deployment.

Download and extract the stale-delete coding exercise into a disposable folder. The application uses only Python's standard library and synthetic records. Before opening a model session, inspect these files yourself:

FilePurpose
store.pyIntentionally incomplete delete function to fix
test_store.pySix real SQLite tests defining expected behavior
reference_store.pyAuthored solution for comparison after attempting the task
reference.patchThe concrete SQL change in patch form
check_exercise.pyVerifies the supplied starter and reference results

Keep the reference solution out of the assistant's initial context if you want to assess its own proposed fix. Do not use the incomplete starter against a real database.

Initialize a local review baseline from the extracted folder:

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 is your installed Python command. Git needs a configured author identity for the commit. The observed baseline is six tests run, four passing and two failing. Those failures are intentional: stale-version deletion and deletion after a value has been recreated.

Inspect the failing contract

The function signature includes expected_version, but the starter's statement is:

DELETE FROM preferences WHERE owner = ? AND key = ?

The desired behavior is narrower. Delete only if owner, key and exact version all match. Otherwise, return false without changing any row. A retry after a successful deletion also returns false. The version strings in this fixture identify specific record instances. They are not user identities or authorization tokens.

The tests use a temporary SQLite file, not a mocked database. One test closes and reopens the connection after deletion. That checks persistence across connections, not a killed process or remote service.

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 documents the Ask, Plan and Agent choices. Select Plan for the first step. Use Add Context → Files & Folders to attach store.py and test_store.py, or mention each with # in the input. The VS Code context guide documents both methods.

Use this request:

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.

The relevant plan should identify the missing version predicate and preserve the existing owner/key scope. If it proposes removing a test, accepting stale versions or fetching a fresh version immediately before deleting, it has changed the task. A fresh lookup would authorize deletion of a record different from the one the caller reviewed.

The file-context instruction helps focus the task. It is not a security boundary preventing access to other files in the workspace. Use a folder containing only material appropriate for the assistant.

Implement and inspect the diff

After reviewing the plan, use Start Implementation to hand it to Agent, as described in GitHub's guide. Ask it to modify only store.py and run python3 -m unittest test_store -v. Keep your editor's tool approval controls in place and inspect proposed commands before allowing anything beyond that exercise.

The reference fix adds the version condition and binds the third parameter:

- "DELETE FROM preferences WHERE owner = ? AND key = ?",
- (owner, key),
+ "DELETE FROM preferences WHERE owner = ? AND key = ? AND version = ?",
+ (owner, key, expected_version),

The SQL placeholders matter. The fix should not interpolate owner, key or version directly into the SQL string. It should also preserve the transaction block and the return value based on whether one row was deleted.

Open VS Code's Source Control diff for store.py, then inspect test_store.py for unintended changes. You can also run:

git diff -- store.py test_store.py
python3 -m unittest test_store -v

Run the command yourself even if the assistant reports success. The accepted result is all six original tests passing. GitHub also documents an optional Copilot review of uncommitted changes. That is another account-dependent model review, not a substitute for the diff and test evidence.

Compare against the executed reference

The supplied reference solution produced these results:

CaseStarterReference
Matching version deletes only AlicePassPass
Unknown owner has no effectPassPass
Retry does not delete another rowPassPass
Stale version preserves current valueFailPass
Recreated value survives an old deleteFailPass
Successful deletion survives reopeningPassPass

reference.patch was applied in a fresh disposable copy, and the patched store.py passed the same six tests. check_exercise.py separately verifies the original starter's two expected failures and the reference module's six passes. After fixing your own store.py, that checker will correctly complain that the starter no longer fails. Use the normal unittest command to validate your completed exercise.

If Copilot produces a different patch, judge it against the contract. A larger rewrite can be correct, but it needs a reason. Record any extra behavior rather than accepting it because the tests happen to pass.

Prove the test detects the bug

In a disposable copy of your completed exercise, remove only AND version = ? and remove the corresponding bound value. Run the tests again. The two stale-delete cases should fail. Restore the correct version check afterward.

That mutation is a stronger check than counting tests: it shows that the suite detects the specific mistake the task addresses. It still does not test authentication, malicious database access, concurrent remote changes or backup retention.

Your review record should contain the baseline failure, final patch, exact command and final result. Keep the actual Copilot model and session behavior in your own record if you run the exercise. The authored reference here does not supply that evidence for you.

Continue with the Cursor app-slice exercise to apply the same approach to a small interface. Return to the course index for the full sequence.

Zarif

Zarif

Zarif builds AI agents and automation workflows and writes about what holds up in production: the sources worth following, the roles the AI era is creating, and agent workflows you can inspect end to end.