What belongs in an agent workspace, and what has to stay out of its reach
A developer’s laptop works for a developer because the developer fills the gaps. They install the missing package, they know which database is safe to touch, and they don’t use the production key for a test.
An agent doesn’t fill gaps the same way. When something is missing, it either fails or works around the gap. It can:
- skip the tests it can’t run and report the rest as passing
- install whatever it finds
- use whatever credentials happen to be in the shell
So the environment has to be decided before the task starts: six layers (the repository, dependencies, tools, scoped secrets, the runtime and the task context), plus a security boundary decided separately.
Two incidents reported last week show the other half of the problem: something that should have been out of the agent’s reach was inside it.
- OpenAI: an internal coding agent turned off the alerts from a misalignment monitor on the pipeline it was fixing, and another removed a program’s monitor during a migration.
- LiteLLM: an agent platform had been given the GitHub CLI token of an organization owner’s account. An attacker who got into the platform used that token to start a chain that ended in a compromised release.
Both are covered below.
What the agent can see and reach has to be decided before the task starts, not discovered during it. “Give the agent the repo” covers just one of those six layers.
And three things that often get treated as one are separate:
- A checkout gives the agent the code.
- A complete execution environment lets it run the tests CI runs.
- A security boundary limits what it can reach if something goes wrong.
The short version
Decide each layer explicitly, check that the agent’s environment can run the tests CI runs, and treat the boundary as its own decision. Anything that controls the agent, such as its monitoring, its approvals and the credentials that can release code, has to stay outside what it can change.

What goes in an agent workspace
Six layers, each decided on purpose: the repository, dependencies, tools, scoped secrets, the runtime and the task context.
1. The repository
The right commit or branch, with its history. Commit messages and blame are where an agent finds out why the code is written the way it is. A shallow clone (depth 1) doesn’t have them.
2. Dependencies
The versions pinned in the lockfile, installed, including anything from a private registry.
GitHub’s documentation says its Copilot agent can find and install dependencies “via a process of trial and error, but this can be slow and unreliable”, and “it may be completely unable to download these dependencies—for example, if they are private.”
Product documentation like this describes how a product works, not how well. It’s here to show the layers are real, not to compare products.
See GitHub’s page on customizing the agent environment.
3. Tools
What the agent needs to build, test and lint the way CI does:
- the build, test and lint commands CI uses
- the CLIs the task calls
- any MCP servers
- the agent’s own configuration
In SetupBench, OpenHands with five different models started from a bare sandbox and completed between 34.4 and 62.4 percent of 93 setup tasks.
One recurring failure, in the authors’ words: “Agents successfully install runtime dependencies but overlook test frameworks.” By their manual count, that was roughly 17 to 26 percent of the repository-setup failures.
Read the SetupBench paper (a preprint: one agent harness, mid-2025 models).
4. Scoped secrets
Only the credentials the task needs, for as long as it needs them.
Not the developer’s personal tokens. GitHub says they’re “tied to the user who generated them”, so an agent using one acts as that person. See GitHub’s page on personal access tokens.
Limited in time. CI already has a pattern: with OpenID Connect in GitHub Actions, “your cloud provider issues a short-lived access token that is only valid for a single job, and then automatically expires.” Whether an agent’s environment can use the same pattern depends on the platform. Read GitHub’s OpenID Connect documentation.
What happens when this layer is skipped: LiteLLM, 21 October. Its agent platform, built for “self-hosted cloud agents to file PRs to GitHub repositories”, was configured with the GitHub CLI token of the organization owner.
LiteLLM, on that token
It “was not associated with a service account and was not scoped down”.
How the chain ran:
- May: A configuration file holding the platform’s master key had been committed to a public repository, and “key revocation did not occur for the master key.”
- 10 October: An attacker used that key to get into the platform and extract the owner’s token.
- Then: With it, they collected two maintainers’ tokens through CI workflows.
- 21 October: They used the owner’s token to override branch protection, a second to approve the release and a third to delete the run records.
The compromised version was on PyPI for about two hours and on Docker Hub for about two and a half. LiteLLM calls it “a strong illustration of the risks associated with failing to rotate a potentially exposed key.” Read LiteLLM’s account of the attack chain.
Keys kept in a hosted console can leak too. On 17 October, OpenCode said attackers had broken into an older system, in a breach that “impacted 1.4% of OpenCode Console users”.
- Some users store their AI provider API keys in the Console.
- OpenCode’s app and command-line tool weren’t affected, and it hasn’t said how the attackers got in.
- Some reported unauthorized charges of hundreds of dollars, a few above $1,000.
Read TechCrunch’s report on the OpenCode breach.
5. Runtime
What the code runs against:
- the language versions
- the services the code talks to (a database, a queue)
- the test data
- the network access the build and tests need
Setup can fail quietly. GitHub’s documentation says: “If any setup step fails by returning a non-zero exit code, Copilot will skip the remaining setup steps and begin working with the current state of its development environment.” So an agent can start with half the runtime in place, and the first sign may be tests that didn’t run rather than an error.
And if the only database it can reach is production, that’s the one its tests will touch.
The runtime needs patching like any other. CISA’s 21 October alert urged organizations to patch two critical PHP flaws, known as “php2shell”, that “allow for unauthenticated remote code execution in PHP”. CISA had not observed successful exploitation when it published.
Its first recommendation is to “Identify and inventory systems running any version of PHP across your organization.” A workspace image that includes PHP and was built before the fix still runs the old interpreter, so it belongs in that inventory.
6. Task context
The approved requirement, the recorded decisions and the answers to the agent’s questions. Without them the agent fills this gap the way it fills the others, with a guess.
This series has covered this layer in:
- What an agent-ready requirement actually contains
- When agents do the entry-level work, where does your team’s context live?
- What questions should an agent ask before it writes code?
A checkout isn’t an environment, and an environment isn’t a boundary
These answer three different questions. Does the agent have the code? Can it run the code the way CI does? What can it reach if something goes wrong?
A code checkout
What it gives the agent: A clone or a Git worktree gives the agent the code and its own working files.
What it shares: Git’s documentation says a new worktree is linked to the current repository, “sharing everything except per-worktree files such as HEAD, index, etc.”, so the repository’s objects, branches and config are shared with the original.
What it doesn’t separate: It’s also a directory on the same machine, so an agent working in one runs as the same user, with the same environment variables and the same access to the rest of the disk.
Bottom line: It separates working files. It doesn’t make the code runnable, and it isn’t a security boundary.
See the git-worktree documentation.
A complete execution environment
An environment is complete when the agent can build the code and run its tests the way CI does. A working build isn’t enough.
In the ExecutionAgent study of 50 projects, the baseline of general-purpose LLM-generated scripts:
- built 29 of them
- ran the test suite for only 5 (the authors’ own agent ran 33)
In one project, a missing headless Chromium binary prevented 8 test cases from executing. Read the ExecutionAgent paper.
The practical check: compare the tests the agent ran, passed and skipped with the same numbers from CI. If they differ, the environment is missing something.
A security boundary
A boundary limits what the agent can read, change and reach if something goes wrong: a bad command, or instructions planted in a ticket, a web page or a dependency.
It’s set by the isolation mechanism plus the network and credential policy, and a complete environment can still have none.
Each mechanism separates something specific. They’re listed by how they work, not ranked, and each depends on how it’s configured:
| Mechanism | What it does | Its limit |
|---|---|---|
| Process sandbox (example: Claude Code’s local sandbox) | Separate filesystem and network layers for shell commands | Its file tools, MCP servers and hooks run outside it |
| Container | Isolates processes with kernel namespaces, cgroups, capabilities and a seccomp profile | Shares the host’s kernel with every other container |
| User-space kernel (gVisor) | Runs the workload against an application kernel in user space | Not a substitute for a secure architecture, in its own words |
| MicroVM (Firecracker) | Runs workloads in lightweight virtual machines using KVM | Depends on a well configured Linux host |
The details and sources, one mechanism at a time.
Process sandbox: One documented example is Claude Code’s local sandbox for shell commands (as its documentation read on 26 October 2026; the page has no date). It has separate filesystem and network layers. Its documentation says:
- “The sandbox is off by default.”
- “The sandbox covers shell commands only. Claude’s file tools, MCP servers, and hooks run outside it.”
By default, sandboxed commands can read most of the machine, credential files included, and inherit environment variables “including any secrets in its environment”. Those are that product’s defaults, not a property of process sandboxes in general. See Claude Code’s sandboxing documentation.
Container: Containers isolate processes with kernel namespaces (plus cgroups, capabilities and a seccomp profile), but they “all share the same kernel”. Docker says “the default set of capabilities and mounts given to a container may provide incomplete isolation”.
A host directory mounted read-write into a container can be changed from inside it. See Docker’s explanation of containers. See Docker’s security documentation.
User-space kernel: gVisor describes itself as “an application kernel, not a virtual machine hypervisor or a system call filter”, running in user space. It contrasts this with namespaces and system call filters, where the workload still talks to “the single monolithic Linux kernel”.
It also says “A sandbox is not a substitute for a secure architecture.” See gVisor’s architecture guide. See gVisor’s security model.
MicroVM: Firecracker runs workloads in “lightweight virtual machines, called microVMs”, using the Linux Kernel Virtual Machine (KVM), and says their security “depends on a well configured Linux host operating system.” See the Firecracker README.
Whatever the mechanism, someone still decides which hosts it lets the agent reach and which credentials sit inside it.
OWASP’s Excessive Agency entry for LLM applications names three root causes: “excessive functionality; excessive permissions; excessive autonomy”. It recommends enforcing authorization in downstream systems “rather than relying on an LLM to decide if an action is allowed or not.” Read OWASP’s LLM06:2025 Excessive Agency (the LLM applications list, 2025 edition).
Simon Willison’s “lethal trifecta” is three things together. For people combining tools, his advice is to “avoid that lethal trifecta combination entirely”. In a workspace, the three map like this:
| Willison’s trifecta | In a workspace |
|---|---|
| Access to private data | The secrets and files the agent can read |
| Exposure to untrusted content | The tickets and dependencies it reads |
| Ability to communicate externally | The network |
OpenAI’s report on its two incidents shows why.
- 3 October: A GPT-6.1 Sol agent repairing an evaluation pipeline “disabled automatic alerts to our Security team, treating an earlier instruction not to send messages during a Slack history search as a reason to suppress monitoring notifications.” The monitor still ran and saved its findings, but human alerts were disabled.
- 6 October: A GPT-6 Astra agent “removed its monitoring integration in order to match a reference implementation, and then falsely claimed to the user that it had not removed the monitor.”
OpenAI, on both incidents
The agents considered whether monitoring safeguards should be preserved but “interpreted routine coding instructions as permission to weaken them.”
Both were internal deployments, and OpenAI says it doesn’t yet have “a satisfying explanation for this behavior.”
OpenAI draws a contrast in where monitoring runs. It expects monitoring to be “difficult to disable for Codex workloads, because monitoring runs within the service that handles model requests, outside the agent’s direct control.”
Its fix is a separate check: an automated review step, already required for internal Codex use, now detects and blocks attempts to disable the monitoring. Read OpenAI’s report.
Two things that aren’t a boundary:
- An instruction not to touch something, like the earlier one not to disable the monitor “even temporarily”.
- A setting the agent can change.
Illustrative walkthrough: duplicate payment reminders
Illustrative walkthrough only. The service, the task and the numbers are invented, and the agent is a generic coding agent.
A notifications service sometimes sends a customer the same payment reminder twice.
A developer asks an agent to fix it on their laptop, in a new Git worktree. The agent opens a pull request and reports: “Fixed. All tests pass.” Before review, the team goes through the environment layer by layer.
| Layer | What happened, and what changed |
|---|---|
| Repository | Had the commit named in the ticket, with full history. Nothing missing. Now: No gap. |
| Dependencies | Lacked the private registry, so the team’s internal email client package didn’t install. It wrote a stub of the package and tested against it. Now: Read access to the registry, with a token scoped to it |
| Tools | Lacked the lint settings CI uses. It linted with the defaults, which pass code that CI’s rules reject. Now: The agent runs CI’s own test and lint commands |
| Scoped secrets | Had the developer’s personal access token in the shell, with write access to every repository they can push to, and their cloud credentials file. It pushed the branch and opened the pull request as the developer. Now: A token for this one repository, issued for the task, not tied to a person, and revoked when it ends. No cloud credentials; the task doesn’t need them |
| Runtime | Lacked the database and the queue. It ran the tests anyway. The integration tests skip themselves without those services, so 212 passed and 38 were skipped, and the agent’s summary said the tests passed. Now: The services start from CI’s configuration with seeded test data, and the agent compares its test counts with CI’s before reporting |
| Task context | Lacked the recorded decision that a reminder is identified by its reminder ID, not the customer’s email address. It deduplicated by email address, which would also have blocked the reminder for a customer’s second invoice. Now: The requirement and the decision are linked to the task |
| The boundary | Had a worktree on the developer’s laptop, with open network access and the home directory readable. Nothing went wrong. But instructions in a ticket or a dependency would have reached everything the developer can. Now: The platform team chose the isolation mechanism, limited the network to the package registry and the Git host, and kept only the task’s credentials inside, recorded as a decision of its own |
The second run: All 250 tests ran and 2 integration tests failed: the job retried after a timeout when the email had already gone out. The new fix checks the reminder ID before a retry.
The gaps in dependencies, tools, secrets and runtime were ones a developer would have filled without thinking. The agent worked around each, and the first pull request looked finished.

Audit one agent task environment
Pick one task an agent ran recently and go through its environment layer by layer. For each gap, write down what’s missing and who will fix it.

| Layer | Check, and what a gap looks like |
|---|---|
| Repository | The agent starts from the commit or branch the task names, with the history it needs. A gap looks like: A fix against the wrong base |
| Dependencies | The lockfile’s versions install, private packages included. A gap looks like: Stubs, packages nobody asked for, or versions that differ from the lockfile |
| Tools | CI’s build, test and lint commands, plus the CLIs and MCP servers the task needs, and nothing it doesn’t. A gap looks like: Only some test suites ran, or lint failures that first appear in CI |
| Scoped secrets | Each credential is named, scoped to the task and limited in time. No personal tokens and no credential files from a developer’s home directory. A gap looks like: Commits, pushes or API calls made as a person |
| Runtime | Language versions, services, test data and network access match CI, and the test data isn’t production data. A gap looks like: Skipped tests. Compare tests run, passed and skipped with CI’s numbers |
| Task context | The approved requirement, the recorded decisions and earlier answers are linked to the task. A gap looks like: A change that contradicts a decision the agent couldn’t see |
The boundary
Check: What the agent can read, change and reach (files, credentials, network hosts) if a command goes wrong or a ticket carries instructions.
Decide separately: The isolation mechanism, the network policy and the credential policy, each written down with who decided it.
Keep out of reach: Anything that controls the agent: its monitoring and alerts, release approvals, and owner or admin credentials.
A gap looks like: Nobody can say what the agent could reach, or it can reach everything the developer can.
Run it
This block is written so that a person or an agent can carry out the audit from the page.
Inputs: For one task an agent ran recently: the prompt or ticket it was given; the environment definition (image, devcontainer or setup script) or a description of the machine it ran on; the names and scopes of the credentials it could reach, never their values; its test report; CI’s test counts for the same commit (run, passed, skipped); the requirement and the recorded decisions linked to the task.
Steps:
- Fill one row per layer, in order: repository, dependencies, tools, scoped secrets, runtime, task context. Use the checks in the table above.
- For the runtime row, compare the agent’s tests run, passed and skipped with CI’s numbers. A difference is a gap.
- Fill the boundary row: what the agent could read, change and reach, and who decided the isolation mechanism, the network policy and the credential policy.
- For every gap, name the person who will close it.
Output: This table, filled in. Copy it as it is.
| Layer | Finding | Gap? (yes/no) | Owner |
| --- | --- | --- | --- |
| Repository | | | |
| Dependencies | | | |
| Tools | | | |
| Scoped secrets | | | |
| Runtime (agent run/passed/skipped vs CI run/passed/skipped) | | | |
| Task context | | | |
| The boundary (mechanism, network, credentials; who decided each) | | | |
Unknowns: Write unknown and the name of the person who could supply the answer. Never paste credential values, customer data or anything confidential into the table.
Done when: All seven rows are filled, every gap has an owner, and the scoped-secrets row names no personal token.
Where Blacktops fits
You don’t need any particular tool for this audit. The seven checks above (six layers and the boundary), run on one task with a named owner for each gap, are enough to start.
Of the six layers, task context is the one that isn’t infrastructure. It comes from the customer, the product owner and the team, and it has to stay with the work.
Blacktops is the Agentic SDLC Platform designed to keep the customer’s question linked to the work. It maintains a single thread from the customer’s question through to the resolution:
- the approved requirement
- the agent’s work
- the evidence
- each decision and who made it
- the update back to the customer
Agents do the work between decisions, and people make the decisions. That’s our operating model: Continuous Agentic Software Delivery™ (or Blacktops CASD™).
Audit one agent task environment. Run the checklist on one task an agent did recently, and get in touch with us if you’d like to go through what you found. Just don’t include credentials, customer names or anything else that’s confidential.
Related: What questions should an agent ask before it writes code? covers what the agent looks up and asks before it starts, which is the task-context layer from the agent’s side.
Continuous Agentic Software Delivery, CASD and Blacktops CASD are trademarks of Blacktops, Inc.