Copilot Chat and Context: Ask, Edit, Agent and Plan Flows, #-References, the Workspace Index and Managing Context
Give Copilot Chat the files that hold the answer with #-references, not the whole repo, and ask a question that can be proven wrong: in Bookstore half of the context was a single package-lock.json, while the cancellation answer sat in a five-thousand-token slice and was 'there is no cancellation flow'. Ask, Plan and Agent; the workspace index, search.exclude and compaction.
Version note: The code in this post was run in the Bookstore repo with .NET 10 and Git. Information about chat modes, #-references and the workspace index is taken from the Visual Studio 2026 (18.12), Visual Studio Code (VS Code) 1.146 and Copilot CLI 1.0.112 documentation as of 15 November 2026; no Copilot Chat session was opened for this post, and no Copilot answer is shown.
Guess First
Selin, the Product Owner, brings a question about the institutional customers of Bookstore, the online bookstore used as the shared example across this site's series: "If a school cancels an order, does the stock we reserved come back?" With Order.cs open in the editor, Emre types into Copilot Chat: "How does order cancellation work in the Ordering module?"
Order.cs contains this enum:
public enum OrderStatus
{
Placed,
PaymentPending,
Paid,
Shipped,
Delivered,
Cancelled
}
What is your guess? Is there a cancellation flow in Bookstore? What will the chat answer be based on: the open file, the whole repo, or the model's general knowledge? What does a bad answer to this question look like? We will find the answer ourselves before asking Copilot, because the first step in giving the right context is knowing where the answer is.
Modes: Ask, Plan, Agent and What Happened to Edit
The picker at the top of the Chat window decides what permissions the same conversation runs with. The situation as of 15 November 2026:
| Flow | VS Code (Copilot harness) | Visual Studio 2026 | Copilot CLI |
|---|---|---|---|
| Asking questions, reading code | Ask: read-only tools only, does not change files | Ask: code does not change unless you choose Apply | The default ask/execute mode |
| Plan first | Plan, with /plan followed by the task | Plan agent: writes a plan with read-only tools, hands it to the agent with Implement plan | Plan mode, with Shift+Tab |
| Multi-step work | Agent; plus Autopilot mode | Agent; Interactive and Autopilot in 18.12 | The same session, with --autopilot |
| Edit | Deprecated, Agent is recommended | Agent mode replaced Copilot Edits | None |
In practice, asking a question in Ask guarantees that the answer will not touch files; for the "understanding" work this post is about, it is the right mode. Plan produces an implementation plan before any code is written: Visual Studio's Plan agent writes the plan as a draft under .copilot/plans/ and, after you refine it, hands it to agent mode with Implement plan. We covered the explore, plan, implement, verify flow itself in the third post of the AI-Assisted Development series; here we only see its Copilot counterpart. Multi-step work with the agent is the topic of the fifth post.
There is also a change in VS Code to know about: chat participants invoked with @, such as @workspace, are deprecated and not supported in the Copilot harness. Today's equivalent of the "ask @workspace" advice in old blog posts is the agent using its own search tools, or you giving a #-reference.
Where Context Comes From
What goes to the model on every chat request is much larger than the sentence you typed. The VS Code documentation describes it in layers: built-in system instructions, customizations such as custom instructions and custom agents, your message, the conversation so far, implicit context from the editor, explicit context you add with #-references, and the outputs of the tools the agent runs. The model sees only what is inside this window; information that is not in the window can only be added by searching for it with a tool.
The behavior of implicit context differs by surface. In VS Code's Chat view the active file or selected text can be added to the request; in the Agents window implicit editor context is off by default. The new Chat experience in Visual Studio 18.12 can send the active file, the cursor or selection and the visible line range; you can turn this off in the settings or ask for it to be refreshed before tool calls. The documentation's warning matters: a file open in the editor does not mean the whole file is in the context. If a source really matters, add it explicitly.
Giving Context with #-References
In VS Code, typing # opens a picker with two kinds of things. Context items add information directly: # followed by a file, folder or symbol name, and #selection for the selected text. Tools make something happen: semantic search with #codebase, text search, finding symbol usages, reading errors from the Problems panel, listing source control changes, reading the last terminal command's output, fetching a web page (#fetch) and searching another GitHub repository (#githubRepo). Dragging files from the Explorer and the Add Context button do the same job; pasting an issue or pull request URL into the prompt makes VS Code add it as an attachment.
In Visual Studio you also add methods, classes and files from the solution to the prompt with #; with 18.12 you can also drag files from Solution Explorer or Windows Explorer into the prompt. In Copilot CLI the /context command shows how full the context is, with a token breakdown.
One distinction matters: #codebase does not put the whole repo into the context; it runs a search and adds the pieces it finds. According to the documentation the agent already runs semantic search on its own when needed; writing #codebase forces it for that prompt.
The Workspace Index and Search Quality
Semantic search relies on an index. If the repo is on GitHub.com or GitHub Enterprise Cloud, GitHub keeps the index and builds it once per repo; for Azure DevOps repos the index is built automatically too. For folders outside these two, Copilot can make indexable files searchable by uploading them to GitHub with "external ingest"; this needs the organization's "Semantic indexing for non-GitHub repositories" policy to be enabled. In Business and Enterprise, leaving this policy unconfigured keeps the feature off. You can see the index status in the Copilot menu in VS Code's Status Bar and rebuild it with the "Build Codebase Semantic Index" command. Without an index the agent still works with text search, file search and language support, but the results can be weaker.
In a large solution, what hurts search quality most is unnecessary files. The documentation's point is this: when the agent runs a text search, every match that comes back enters the conversation's context, even if it never opens the file. To see how big this is in Bookstore, we added a small script to the repo; it estimates the token count as roughly a quarter of the character count:
#:property PublishAot=false
// Rough context cost of files tracked by git: about 4 characters per token.
// Run: dotnet run eng/context-size.cs -- [path prefix ...]
using System.Diagnostics;
using System.Globalization;
CultureInfo.CurrentCulture = CultureInfo.InvariantCulture;
string[] prefixes = args.Length > 0 ? args : [""];
ProcessStartInfo git = new("git", "ls-files") { RedirectStandardOutput = true };
using Process process = Process.Start(git)!;
string[] tracked = (await process.StandardOutput.ReadToEndAsync())
.Split('\n', StringSplitOptions.RemoveEmptyEntries);
await process.WaitForExitAsync();
List<(string Path, int Tokens)> files =
[
.. tracked
.Where(p => prefixes.Any(prefix => p.StartsWith(prefix, StringComparison.Ordinal)))
.Select(p => (p, File.ReadAllText(p).Length / 4))
.OrderByDescending(f => f.Item2)
];
Console.WriteLine($"{files.Count} files, ~{files.Sum(f => f.Tokens):N0} tokens");
foreach ((string path, int tokens) in files.Take(5))
{
Console.WriteLine($"{tokens,10:N0} {path}");
}
$ dotnet run eng/context-size.cs
81 files, ~119,604 tokens
60,089 web/bookstore-web/package-lock.json
9,345 src/Bookstore.Api/Data/Migrations/20261114234258_AddInstitutions.Designer.cs
9,320 src/Bookstore.Api/Data/Migrations/BookstoreContextModelSnapshot.cs
8,177 src/Bookstore.Api/Data/Migrations/20261114005447_InitialCreate.Designer.cs
5,073 src/Bookstore.Api/Data/Migrations/20261114005447_InitialCreate.cs
$ dotnet run eng/context-size.cs -- src/Bookstore.Api/Ordering/ src/Bookstore.Api/Inventory/ tests/Bookstore.Api.Tests/OrderingTests.cs
11 files, ~5,561 tokens
1,588 src/Bookstore.Api/Ordering/OrderingModule.cs
1,011 tests/Bookstore.Api.Tests/OrderingTests.cs
873 src/Bookstore.Api/Ordering/OrderService.cs
572 src/Bookstore.Api/Ordering/OrderingConfiguration.cs
486 src/Bookstore.Api/Inventory/InventoryService.cs
Even in a small repo the picture is clear: half of the roughly 120 thousand tokens is a single package-lock.json, the five largest files are 77 percent of the total, and all of them are generated by tools. The Ordering and Inventory slice needed to answer the cancellation question is about 5,600 tokens. The "give it the whole repo" approach buries the one-twentieth that holds the answer in noise.
The way to reduce this is exclusion settings. .gitignore and files.exclude remove a file both from text search and from the semantic index; search.exclude keeps the file in the Explorer and removes it only from text search and grep. Migration files and the lock file should stay visible in the project, but they should not fill the results of a search; so we added this to Bookstore's workspace settings:
"search.exclude": {
"**/package-lock.json": true,
"**/Migrations/*.Designer.cs": true
}
We deliberately did not exclude BookstoreContextModelSnapshot.cs: it is the only file that shows the latest state of the model, and the agent must be able to find it for questions about migrations. One more warning: according to the documentation, .gitignore is bypassed for a file that is open in the editor or that you have selected text in; leaving an ignored .env open can bring it into the context.
Bookstore: Asking the Cancellation Question Right
Back to Guess First. Let us search for the answer ourselves before asking Copilot; one of the first searches the agent runs will look similar:
$ git grep -c "Cancel" -- src tests web
src/Bookstore.Api/Catalog/CatalogModule.cs:3
src/Bookstore.Api/Catalog/CatalogService.cs:4
src/Bookstore.Api/Institutional/InstitutionalModule.cs:1
src/Bookstore.Api/Inventory/InventoryService.cs:4
src/Bookstore.Api/Ordering/Order.cs:1
src/Bookstore.Api/Ordering/OrderService.cs:2
src/Bookstore.Api/Ordering/OrderingModule.cs:7
tests/Bookstore.Api.Tests/CatalogTests.cs:3
tests/Bookstore.Api.Tests/InstitutionalTests.cs:1
tests/Bookstore.Api.Tests/OrderingTests.cs:1
$ git grep -nw "Cancelled" -- src tests web
src/Bookstore.Api/Ordering/Order.cs:10: Cancelled
$ git grep -n "OrderStatus\." -- src
src/Bookstore.Api/Ordering/Order.cs:18: public OrderStatus Status { get; set; } = OrderStatus.Placed;
src/Bookstore.Api/Ordering/OrderService.cs:51: Status = OrderStatus.Placed
The first search returned 27 lines in ten files, and nearly all of them are CancellationToken: in .NET the word cancel appears in every async method with a different meaning. Matching the whole word leaves one result: the enum value. The only value ever assigned to the status is Placed. There is no cancel endpoint in OrderingModule, and no method in IInventoryService that releases reserved stock. The correct answer is: Bookstore has no order cancellation flow today; Cancelled is only a defined status.
This is a hard question for chat. A question asked as "how does it work?" assumes that the answer exists; for a model that only sees Order.cs, the Cancelled value suggests a cancellation flow, and there is a risk that it describes a "typical" one from its general knowledge. We did not try this with Copilot; but setting up the question to reduce this risk is in our hands. In Ask mode, giving the relevant files explicitly and allowing a "no" answer:
Ask mode.
#OrderingModule.cs #OrderService.cs #InventoryService.cs #OrderingTests.cs
Can an order be cancelled in Bookstore today?
If yes: which endpoint, which status transitions, and how the reserved stock is released.
If no: say so, and list what is missing.
Cite file and line for every claim. Do not describe code that is not in these files.
This prompt has three things. The context is narrow and correct: the four files that hold the answer, about five thousand tokens. The question can be proven wrong: "does it exist, and if not, what is missing" instead of "how does it work". And every claim has to be tied to a file and a line; comparing the answer with the git grep output above takes a few seconds. If the answer is "there is no flow; missing: the endpoint, the status transition rule, releasing stock", Selin's question is answered too: this is a feature request. The next step is Plan: produce a plan for the cancellation flow with /plan, review it, and only then let the agent implement it.
Long Sessions: When Context Fills Up
Every model has a context window, and in a long session messages, tool outputs and file contents fill it. When the conversation approaches the limit, VS Code automatically compacts the older parts into a summary; you can also do this by hand with /compact. Copilot CLI does the same in the background when the conversation approaches 95 percent of the token limit. The price of compaction is that details from earlier messages are summarized or lost.
That is why opening a new chat after finishing a task is often better than compaction. In VS Code, opening a new chat in the same session keeps the workspace and the code changes and resets the conversation history; for an unrelated task a new session is recommended. In Visual Studio 18.12 you can keep several Chat sessions open in separate tabs. Information that must persist, such as "tests run with dotnet test", belongs in custom instructions, not in the chat; that is the topic of the next post. Remember the calculation from the first post too: because every step resends the whole conversation, a bloated session hurts both answer quality and credit use.
In Production
A chat answer is not documentation. Before putting an explanation in the wiki or a pull request description, verify its claims against the code; the "cite file and line" rule in this post makes that cheap.
For private repos, Copilot may ask for additional permission for semantic search; VS Code asks for it at startup. For repos outside GitHub, external ingest means uploading files to GitHub for the index; the company policy should turn it on knowingly or keep it off. External ingest is not available on GitHub Enterprise Cloud with data residency or on GitHub Enterprise Server.
Files excluded with content exclusion are not used in chat either, but it is not a file system security boundary; details in the tenth post.
Common Pitfalls
Giving the whole repo as context. In Bookstore half of it turned out to be a single lock file; the answer gets lost in noise and credits are wasted.
Asking "how does it work" even for something that does not exist. Ask "does it exist" first and allow a "no" answer.
Assuming the whole open file is in the context. Add the important source explicitly with #.
Following old advice: @workspace is not in the Copilot harness, and Edit mode is deprecated.
Using a chat answer as documentation without verifying it. As the code changes the answer goes stale, but the sentence in the wiki stays.
Carrying the conversation of a finished task into the next one. A new chat is cheap; a bloated context is expensive.
A Small Experiment
★☆☆ Run eng/context-size.cs in your own project. What percentage of the total are the five largest files? Which of them are generated by tools?
★★☆ Ask Bookstore this question in two ways: first with only Order.cs open, "How does order cancellation work in the Ordering module?", then with the Ask prompt from this post. Compare every claim in both answers with the git grep output and mark it as correct, wrong or unverifiable.
★★★ Produce a plan for the cancellation flow in Plan mode: the endpoint, which statuses allow cancellation, releasing stock and tests. Review the plan against Bookstore's module rule (a module never reads another module's tables) and fix the steps that break it.
In this post we rebuilt the context for every question. In the next post we move what is permanent to a permanent place: copilot-instructions.md, path-specific instructions with applyTo, AGENTS.md and prompt files for repeated work.