GitHub Copilot Coding Agent Tutorial
Delegating work to Copilot is a different activity from using Copilot. In agent mode you watch it work and correct it as it goes. Here you write down what you want, walk away, and evaluate a pull request later — which means everything depends on how well you wrote it down.
This lesson goes through one delegation end to end: choosing a task worth delegating, writing the issue, assigning it, following the session, and reviewing what comes back.
Key takeaways
- The issue is the whole interface. You cannot clarify mid-session the way you can in chat, so ambiguity becomes invented requirements.
- Task selection matters more than prompt wording. Well-scoped, verifiable, low-ambiguity work succeeds; open-ended architectural work does not.
- Repository access is scoped by default — GitHub states Copilot can access context in the repository you specified when starting the task.
- The output is a pull request, which is the safest possible artefact: your existing review process already applies to it.
- Review it as agent output, not as a colleague’s work. Check the premise before the implementation.
Pick a task that can succeed
Most disappointing delegations are disappointing because of what was delegated, not how.
The agent works from your issue, your repository and its tools. It has no access to the conversation you had in standup, the decision recorded in a document elsewhere, or the reason the previous approach was abandoned. Anything load-bearing that lives outside the repository has to be written into the issue or it does not exist.
Good candidates share four properties.
The scope is bounded. A specific file, a specific function, a named
behaviour. “Add pagination to the /users endpoint” is bounded. “Improve the
API” is not.
Done is checkable. A test that passes, a behaviour you can exercise, output you can compare. If you cannot describe the check, neither can the agent.
The pattern already exists in the repository. Agents are strongest when they can copy your conventions from a neighbouring file rather than invent them. The second endpoint of a kind is a far better delegation than the first.
Being wrong is cheap. The output is a pull request you can close. That is only comforting if closing it costs nothing, which is true for a feature branch and false for anything you would feel obliged to salvage.
Poor candidates are the mirror image: broad refactors, architectural decisions, anything requiring product judgement, anything where the requirements live in someone’s head, and anything touching code you do not understand well enough to review.
Write the issue
Treat this as a specification for someone joining today who cannot ask you questions.
Four sections carry almost all the value.
What to change, precisely. Name files, functions and endpoints. The agent can search, but naming the target removes an entire class of misunderstanding.
Why, in one sentence. Intent lets the agent choose sensibly where your instructions run out — and they always run out somewhere.
How you will know it worked. The test that should pass, the behaviour that should change, the command that should now succeed.
What not to touch. The most valuable section and the one people skip. Agents fill gaps; telling it the public API is fixed, or that the migration files are off limits, prevents helpful damage.
What
Add cursor-based pagination to GET /api/users in src/routes/users.py.
Why
The endpoint returns every user in one response. At current data volumes this times out for the largest tenants.
Details
- Follow the pagination already implemented in
src/routes/projects.py. Use the same query parameter names and the same response envelope. - Default page size 50, maximum 200.
- Preserve the existing response shape for callers that pass no pagination parameters.
Done when
tests/routes/test_users.pyhas coverage for first page, subsequent page, and the maximum page size.pytest tests/routes/test_users.pypasses.- No changes outside
src/routes/users.pyand its test file.
Do not
- Do not change the database schema.
- Do not modify the shared response helpers in
src/lib/http.py.
Notice how much of that is constraint rather than instruction. The “Do not” section is doing the work a tool restriction would do if this surface offered one — and since the cloud agent’s approval happens at the pull request rather than per action, written constraints are most of what you have.
Assign the work
Assigning an issue is the version worth learning first, because the issue is already the specification and the delegation is a single click. The agent acknowledges the assignment, opens a draft pull request, and starts work.
- Write the issueHuman judgementScope, intent, done-when, constraints.
- Assign to CopilotHuman judgementFrom the issue, the agents panel, or VS Code.
- Agent starts a sessionIt reads the repository and plans.
- Draft pull request appearsWork happens on a branch, visible while in progress.
- Agent iteratesEdits, runs commands, responds to failures.
- Pull request marked readyYou are requested as a reviewer.
- Review the diffHuman judgementPremise first, then implementation.
- Request changes or mergeHuman judgement@copilot in a comment sends it back.
Steps marked Human judgement are the ones that do not get delegated — they are where you decide whether what Copilot produced is actually right.
What happens while it runs
The agent works in a GitHub Actions-powered environment rather than on your machine. That has three consequences worth internalising.
Its session is inspectable. The draft pull request shows progress, and the session logs show what it did. When a result surprises you, the logs usually explain it — most commonly by revealing that it interpreted the task differently than you intended from the first step.
Its context is scoped. GitHub states that by default Copilot can only access context in the repository specified when you start the task. A task requiring knowledge from a second repository needs that context supplied deliberately.
Its environment may not match yours. If your test suite needs a service, a credential or a specific toolchain version, the agent’s environment needs it too. A repository that only builds after three undocumented setup steps will produce an agent that spends its session fighting the build.
Preparing the repository
The single largest predictor of whether delegation works is not the issue wording. It is whether the repository is legible to someone arriving cold.
A README that describes setup accurately. Not aspirationally. If the
documented steps do not produce a working checkout, the agent will discover that
the same way a new hire does, except it will improvise rather than ask.
Tests that run from a clean checkout. A suite that only passes on a machine with three environment variables set is a suite the agent cannot use to check its own work, and self-checking is most of what makes an agent session converge.
Custom instructions describing conventions. Which patterns you use, which you have abandoned, what belongs where. Everything Cluster 6 says about instructions applies with more force here, because there is no conversation in which to correct a wrong assumption.
An AGENTS.md if you have agent-specific guidance. Build commands, the shape
of the test suite, directories that are generated rather than authored. This is
context the agent needs and a human colleague would absorb by osmosis.
Small, well-named modules. Not agent-specific advice, but it matters more here: an agent asked to change behaviour in a 4,000-line file has to reason about all of it, and the failure modes of that are the same as yours, only faster.
How the agent uses your issue
Understanding the sequence makes the failures interpretable rather than mysterious.
It reads the issue first and forms an interpretation of the task. This interpretation is fixed early and rarely revisited — which is why an ambiguous first paragraph produces a session that is confidently off-target from step one rather than one that gradually drifts.
It then explores the repository to locate the relevant code. Naming files in the issue short-circuits this step and removes the possibility of it landing in the wrong module with a plausible reason.
It plans, edits, and runs commands. Failures are informative to it: a failing test is a signal to try something else, which is why repositories with working test suites produce better sessions than repositories without.
Finally it summarises. That summary is written to describe what it did, and it is generally accurate about actions and less reliable about whether those actions were the right ones — which is exactly the split your review should assume.
Review what comes back
The pull request is where delegation becomes real work again, and rushing this step forfeits the point of the exercise.
Start with the summary, not the diff. Read what the agent says it did and compare it against what you asked for. Disagreements here are cheap to catch and expensive to miss — a correct implementation of the wrong task looks perfectly fine at the line level.
Read the file list before the changes. Files you did not expect are the strongest available signal. A pagination task touching an authentication module deserves an explanation before you read another line.
Check the tests actually test. An agent that can edit tests can make tests pass. Confirm new tests assert the behaviour you wanted and that existing assertions were not weakened to accommodate the change.
Then read the diff normally. By this point you are doing ordinary code review, which you already know how to do.
Iterating without starting over
If the pull request is close but wrong, you do not have to redo the delegation.
Commenting on the pull request and mentioning @copilot asks for changes on the
existing branch, in the same way you would ask a colleague.
The same specificity rules apply, and for the same reason.
@copilot the page size limit is not enforced. GET /api/users?limit=5000
still returns every row.
Cap it at 200 in src/routes/users.py and add a test in
tests/routes/test_users.py that requests 5000 and asserts 200 rows.
Two failure modes to avoid. Do not iterate more than a couple of times on a result that is fundamentally wrong — that is a signal the issue was under-specified, and rewriting the issue and starting fresh is faster than steering. And do not accumulate a long comment thread of small corrections; the agent is working from the pull request, and a thread of contradictory adjustments is exactly the ambiguity you were trying to avoid.
What it will not do well
Worth stating explicitly, because the failure modes are consistent enough to predict.
Work requiring a decision you have not made. Given a genuine fork in the road, it picks one and proceeds. The result is a confident implementation of an option you might have rejected in ten seconds of discussion.
Changes spanning many files with subtle coupling. It can make the mechanical change everywhere. Whether the seventh call site had a reason to differ is exactly the kind of thing that gets flattened.
Anything depending on running systems it cannot reach. Behaviour that only manifests against a real database, a staging environment or live data is not something it can verify, and unverifiable work is where agent output is least reliable.
Tasks where the tests are the thing being changed. Asking it to fix failing tests invites making them pass, which is a superset of fixing them and includes several outcomes you do not want.
When to close it
Closing an agent pull request is a normal outcome, not a failure, and treating it as one is important to using this well.
Close it when the approach is wrong rather than incomplete. Close it when the diff has grown beyond what you are willing to review carefully. Close it when reading it has taught you what you actually wanted, which happens often enough to be a legitimate use of the feature on its own.
The branch and the session remain; nothing is lost except the time already spent, and that time was the agent’s rather than yours.
Making this a team practice
Delegation stops being a personal trick and becomes a workflow at the point where more than one person is doing it, and a few conventions prevent the obvious problems.
Label agent-authored pull requests. Reviewers should know what they are looking at, because reviewing agent output well requires different attention from reviewing a colleague’s.
Keep the same review bar. The temptation is to be either harsher (rejecting reasonable work because of its origin) or softer (merging because reviewing carefully is tedious). Both are worse than treating it as a normal change with one extra check on the premise.
Delegate from issues that already existed. The best candidates are the small, well-specified items that have been sitting in the backlog precisely because nobody wants to context-switch for them. Writing an issue solely to delegate it often costs more than doing the work.
Notice what fails repeatedly. Three delegations that all get the same convention wrong is not three agent failures — it is one missing line in your instructions file.
Writing the “do not” section
The section people skip is the one that changes the output most, so it is worth a moment on how to write it.
Name the files that are off limits. Not “do not change unrelated code” — “do
not modify src/lib/http.py”. A named file is a boundary; a category is an
interpretation.
State the interfaces that are fixed. Public API shapes, database schemas, event payloads. These are the things whose change breaks something the agent cannot see, and they are exactly what a helpful improvement targets.
Say what should not be added. New dependencies, new configuration options, new abstractions. Each is a reasonable thing to reach for and a thing you would rather decide on yourself.
Bound the diff. “No changes outside these two files” is enforceable by inspection and makes review trivial. It is also the constraint most likely to be worth relaxing on the second attempt, which is fine — relaxing a stated constraint is a decision, and discovering an unstated one is a surprise.
Delegation as a habit
The teams that get most from this treat it as a queue rather than an event.
Keep a list of delegatable work. The small, well-specified items that never reach the top of anyone’s day. When you have ten minutes, delegate one rather than starting it.
Delegate before you context-switch, not after. The moment you decide something is not worth switching for is exactly the moment to write the issue, while you still remember why it matters.
Batch the reviews. Three pull requests reviewed in one sitting is a better use of attention than three interruptions. The asynchrony is the feature; using it synchronously wastes it.
Notice the rejection rate. If you close most of what comes back, the problem is almost always task selection rather than the agent — you are delegating work that needs judgement you have not written down.
Common questions
Does the agent have access to my other repositories? By default it works in the repository you specified when starting the task. Broader access is something you configure deliberately.
Can it run my tests? It runs commands in its environment. Whether your tests pass there depends on whether your repository can be set up without undocumented steps.
What if the repository has custom instructions? They apply. An AGENTS.md or
.github/copilot-instructions.md is context for this agent as much as for chat —
which is a strong argument for having one before you delegate.
Cluster 6 covers writing
them.
Can I use a custom agent with it? The support matrix in the pillar records the current position per surface. Custom agents covers the format.
How long does a session take? Long enough that you should treat it as background work rather than something to wait on. If you find yourself watching the pull request refresh, the task was probably small enough to have done in agent mode.
Can I stop a session? Closing the pull request ends the delegation. There is no partial credit — the work on the branch stays there if you want to look at it.
Does it count against my premium requests? Agent sessions consume requests across many turns rather than one. Cluster 6’s request model explains the mechanics.
Next
The cloud agent explained covers where this runs, what it can reach, and how it differs from agent mode in your editor. If you would rather delegate from a terminal with per-tool approval, Cluster 5 covers the CLI, and GitHub Copilot vs Claude Code compares this delegation model with the other one people evaluate against it. And if the agent keeps making the same wrong assumption, the fix is usually custom instructions rather than a longer issue.
Sources
Every version-sensitive claim on this page was checked against first-party documentation. Only sources actually used are listed.
Your progress
Saved in this browser only. No account, no server, and nothing leaves your device. Clearing site data resets it.Sync across devices if you want it everywhere.
Saved in this browser and synced to your account, so it follows you between devices. Manage or delete it.
Was this lesson helpful?
We record which lesson you rated and whether it helped. Nothing identifies you — no account, no cookie, no session.