Don’t Repeat Yourself (DRY) is one of the first principles developers learn. If two modules need the same logic, extract it into a shared function. If two config files need the same block, pull it into an include. Over time, it becomes second nature to factor out shared logic and reference it from multiple places. It’s a good instinct – in code, it saves you maintenance headaches and keeps your codebase clean.
When you start building AI skills and instructions, the same instinct kicks in. You write a skill that needs setup steps for your environment. Then you write a second skill that needs the same setup. The reflex: factor those steps into a shared file (say, shared/environment-setup.md) and reference it from both skills with a line like “See shared/environment-setup.md for environment configuration details.”
In traditional code, that’s free. The compiler or runtime resolves the reference at build time or load time. No extra cost. No extra latency. With AI instructions, that line may come with some steep costs. Understanding why requires knowing how content reaches the model.
Two loading mechanisms, vastly different costs
When an AI agent in VS Code starts processing your request, content reaches the model through one of two paths:
- Harness loading
- The AI’s infrastructure (the “harness”) discovers files automatically based on configuration – instruction files, skill definitions, hooks – and attaches their content to the request before the model sees it. No model decision is required. The full content is attached to the model’s context on the very first turn.
- Model loading
- The model reads something that references another file or reasons that it needs the contents of a file, so it emits a tool call (such as
read_file). The harness executes the call, returns the content, and the model continues. Each fetch requires a new turn (a complete request-response cycle). Each turn re-sends the entire conversation – system prompt, all previous messages, all accumulated context – plus the new content.
Instruction files are harness-loaded. When the harness starts a new conversation, it attaches the full file content to the model’s context in one shot. The model sees it immediately without needing to request it. By comparison, a skill’s body is model-loaded. Skill descriptions are harness-loaded – so the model knows what skills are available – but the full SKILL.md content is not. The model reads the skill’s description, reasons that it may be able to use it, and emits a request for the harness to load the full skill content and add it to the context. Similarly, if any file is referenced by a skill or instruction – a schema file, a shared setup document, example code, another instruction file – it must also be model-loaded. The model has to decide it needs the file, request it to be loaded by the harness, wait for the response, and then continue. That’s a separate turn – and another complete re-send of your context to the LLM.
What “see other file” actually costs
Let’s step through this. Assume you have a file that contains “see shared/environment-setup.md for details.” Here’s what happens at runtime:
- The model reads the content and encounters the reference.
- The model then reasons that this is needed.
- The model responds with a
read_filetool call forshared/environment-setup.md. Typically, it will request the first 100 to 200 lines, though the exact range depends on the model. - The harness returns the current context, adding the requested file content.
- The model processes the response and continues reasoning.
If the file is small, that’s two turns instead of one. The token cost for the second turn includes everything from the first turn again (the conversation prefix), plus the new file content. Already more expensive than if the content had been inline. (Prompt caching can reduce the effective cost of re-sent context, but each additional turn still adds latency and compounds the total token count.)
But it gets worse if the file is large. The model may ask for:
- Chunks of the file, 100-400 lines at a time depending on the model and file size
- A search of the file for specific content, returning line numbers
- The entire file if it is small enough and the model decides it needs it all
Each new request is a new turn, adding the requested content to the context and re-sending the entire conversation. The token cost grows with each request. If the content had been included inline, it may have been sent in the first turn by the harness, reducing the number of requests and the total token cost.
The first 100 lines are not guaranteed
Here’s the part that makes composition genuinely risky: the model might not read the entire file. It reads the first chunk (often 100 to 200 lines, depending on the model), evaluates whether it has enough context for the task at hand, and decides whether to continue. If the model judges that those first lines contain sufficient information – even if they don’t – it stops reading and proceeds with what it has. That critical setup step on line 142? The model will never see it.
This is why the first 100 lines of any model-loaded file matters disproportionately. They’re the only lines you can reasonably expect the model to always see. Everything below that threshold is best-effort. The model will usually load the rest, but there is no guarantee if it thinks it has enough for the current task. All of that said, the model may also decide it knows enough and skip the file entirely. If it doesn’t think it needs the file, it won’t request it at all. That means your “see other file for details” line may not even trigger a load. The only way to know for sure is to check the agent logs and see what actually got loaded.
Bloating isn’t the answer
The obvious reaction is: “Fine, I’ll just inline everything into the skill itself. No references, no model loading.” But that creates a different problem. You could bloat the context window with instructions that may not all be relevant. The model’s attention degrades as context grows, and important details get diluted by volume. Instructions, skills, or agents that are too large may end up being truncated by the model, leaving you with incomplete context.
The key is to balance harness-loaded content (cheap and guaranteed) with model-loaded content (expensive and probabilistic). Your goal should be to have the most critical instructions in harness-loaded files, while relegating less critical or large content that can be searched to model-loaded references. To help the model with that loading, you can provide additional details – section headers, the length of the file, and clear summaries of what the file contains. That way, if the model does decide to load the file, it can find what it needs quickly and avoid unnecessary requests. For example, if you have a large file with hundreds of lines, you might mention the sections of the file in the skill description, so the model can search for the relevant section and load those lines instead of iterating through the entire file.
The agent issue
There’s a particular part of the process that you must know about with agents. When the harness builds the context window, it places the most important details at the start and the most recent results or user requests at the end. The harness composes the context this way due to recency bias – the model pays more attention to the beginning and end of the context, and may give less attention to details in the middle. (Modern frontier models have improved on this considerably, but the effect still holds for long conversations with large contexts.) Past turns, tool results, and file attachments ultimately end up moving towards the middle of the context, while instructions and agent definitions remain in a fixed position at the start.
This means that if you reference a shared file from within an agent, the content of that file is not prioritized and placed at the beginning of the context. It will appear as a recent response when the model first requests it, but as the conversation continues, it will move towards the middle of the context. Over time, the model may give less attention to the content of that file. It could even be subject to compaction (where the harness summarizes and replaces the conversation history to free up space in the context window). In other words, these references should only be used for content that is not critical to the agent’s operation. If the content is critical, it should be harness-loaded and placed at the start of the context.
When references are worth the cost
Not all model loading is bad. Some content genuinely belongs in a separate file:
- Large schemas or data files that the model needs to validate against. These are too big to inline and change independently of the skill. The model may only need a small portion of the file, so it can search for what it needs instead of loading the entire file.
- Examples and templates that aren’t critical instructions.
- Large reference documents with contents that may only be needed occasionally.
- Scripts. These are referenced so the model runs them rather than reading and reasoning about their contents. Keeping scripts out of the inline context prevents the model from trying to interpret or paraphrase them instead of executing them.
For these cases, accept the cost but mitigate it. Lead the file with the most critical information. Use structured headers so the model can find what it needs quickly. Keep files short enough that the model is likely to load them completely. And check the agent debug logs to verify what actually got loaded.
Summary
When building AI skills and instructions, the DRY principle can backfire. References to shared files create model-loaded fetches – each one is a new turn that re-sends your entire context, multiplying token costs. The model may not even read the full file, and the referenced content gets buried in the middle of the context where it receives less attention.
The solution isn’t to inline everything (that bloats context and degrades attention), but to be strategic about what you reference. Keep critical instructions harness-loaded where they’re guaranteed to appear in the first turn. Reserve model-loaded references for large schemas, examples, and reference documents that the model can search on demand. When you do use references, front-load the most important information and use clear structure so the model can find what it needs quickly.
The key insight: AI instructions aren’t code. That innocent “see other file for details” line isn’t a free pointer – it’s a request for the model to make a choice, spend tokens, and potentially miss content. Design your skills with that cost in mind.
