Skip to content

Course · OpenCode · Rules (AGENTS.md)

Discovery and the nesting walk

feedmill, the course’s illustrative Go feed aggregator, carries a root AGENTS.md with two rules in it - timestamps parsed to UTC at ingest, fetches routed through the shared rate-limit limiter - and the agent has been applying both on tasks that touch them. So you reach for the obvious next step. feedmill’s HTTP sync server lives under cmd/sync/ and has a convention of its own: every handler must wrap its response in the standard error envelope. Rather than bloat the root file with a server-only rule, you do the tidy thing and create a second AGENTS.md inside cmd/sync/ with just that one rule in it. feedmill has no such file yet; you add it here.

Then you stop, because the stakes are not obvious. Whether that nested file adds to the root rules or replaces them for work in that subtree depends entirely on how the tool discovers instruction files - and tools genuinely differ. Some combine every ancestor file they pass on the way up; some use only the nearest one. Before you trust the server task, you need to know which kind OpenCode is, and in OpenCode’s case, where you started it.

OpenCode walks up to the repo root and loads every AGENTS.md

Section titled “OpenCode walks up to the repo root and loads every AGENTS.md”

Here’s the mechanism. When OpenCode assembles the system prompt for a turn, it walks up from the directory you started it in to the root of the git worktree (the top of your checkout) and collects every AGENTS.md on that path. Filename precedence applies to the whole walk: if any AGENTS.md turns up, CLAUDE.md files are skipped, and only a walk that finds no AGENTS.md at all collects CLAUDE.md files instead. The rules documentation states the search order and the filename precedence. It does not say whether ancestors stack; the source does. Every AGENTS.md between the start directory and the repo root loads, not only the nearest one (instruction.ts at v1.18.32, as of October 2026).

So when you start OpenCode inside cmd/sync/, the walk finds cmd/sync/AGENTS.md, keeps climbing, and finds feedmill/AGENTS.md too. Both load: the envelope rule and the root UTC and rate-limit rules arrive together. The nested file adds to the root rules; it does not replace them.

Start OpenCode at the feedmill root instead, and the walk begins above cmd/sync/, so it never passes that file. The root rules load. The envelope rule is not in the system prompt.

The whole picture, for both start directories:

~/.config/opencode/AGENTS.md global rules, combined with project rules
(~/.claude/CLAUDE.md is used if this file is absent)
started in feedmill/cmd/sync/:
feedmill/cmd/sync/AGENTS.md loaded
feedmill/AGENTS.md loaded (the walk continues to the repo root)
started in feedmill/:
feedmill/AGENTS.md loaded
feedmill/cmd/sync/AGENTS.md not on the walk; attached later only if the
agent reads a file under cmd/sync/
result: global rules + every AGENTS.md from the start directory up to the repo root

The line in OpenCode’s own docs that people misread is this one:

The first matching file wins in each category.

It is tempting to read “first matching file wins” as “the nearest file wins and the ancestors drop out.” The source shows the other meaning: the rule picks a filename, not a directory. Within the project walk, the first filename that matches anywhere on the path wins, and every copy of that filename on the path loads. The docs’ own example pins down the filename half: if you have both AGENTS.md and CLAUDE.md, only AGENTS.md is used. Global rules are a separate category. ~/.config/opencode/AGENTS.md, or ~/.claude/CLAUDE.md when that file is absent, is combined with the project files.

So “first match wins” is real, and so is “every AGENTS.md on the path loads.” They answer two different questions: which filename counts versus which directories contribute.

Because the walk adds files, a nested AGENTS.md cannot hide the root rules. The traps sit elsewhere, and other tools differ on each of them, so verify their current rules documentation before transferring this mental model.

  • The start directory decides the set. The same repo gives the agent a different rules layer depending on where you launched OpenCode. Start at the root and the cmd/sync/ rule is missing from the system prompt, even for a task that only touches the server; it arrives only if the agent reads a file there, as the last section shows.
  • One AGENTS.md silences every CLAUDE.md on the startup walk. A repo that keeps its rules in CLAUDE.md files and then gains a single AGENTS.md anywhere between the start directory and the root stops loading those CLAUDE.md files.

There is no command that confirms the intended rule set for you: OpenCode’s public docs document cwd walk-up plus explicit instructions paths, globs, or URLs, and promise no debug command or stable system-prompt header, so asking the model to self-report its rules is not verification either. What you can do is prevent the ambiguity - when the exact set must be location-independent, put it in opencode.json’s instructions field (opencode.json is OpenCode’s project config file).

Walk-up discovery covers the simple case, but it has two limits worth naming. First, it only climbs; a file in internal/feed/ won’t be in the turn’s base context when you’re working from cmd/sync/, because the walk goes up, not sideways. Second, the set of files the walk-up loads depends on where you started OpenCode, which is implicit and easy to misjudge.

When you want an explicit, location-independent set of rule files - load these every turn, no matter which directory the task touches - the tool isn’t directory nesting at all. It’s the instructions field in opencode.json, which takes explicit file paths and glob patterns and loads every match additively:

{
"$schema": "https://opencode.ai/config.json",
"instructions": ["AGENTS.md", "cmd/sync/AGENTS.md"]
}

Every matched instructions file is loaded and combined with the discovered project and global rules; a file that both routes find loads once. The difference from the walk-up is implicit-by-location versus explicit-by-config. The walk-up loads what sits on the path from where you started; the instructions list gathers exactly the set you name, every turn. OpenCode resolves a relative entry against the start directory and each parent up to the repo root, so cmd/sync/AGENTS.md matches whether you start at the root or inside cmd/sync/ (instruction.ts at v1.18.32, as of October 2026). Entries can also be globs, ~/-rooted paths, or remote https:// URLs (fetched with a short timeout).

That reliability has a price. A listed file loads on every turn, whether the task touches the server or not, so a subtree rule listed here spends context on work that never goes near that subtree. List a subtree file when its rule must be present from the first message; leave it to the walk when it matters only for work inside that directory and you start OpenCode there, or rely on the undocumented read-time attach that the last section describes.

That explicit additive set is a topic of its own - which files to name, how the globs behave, and Claude Code’s @path import habit, which OpenCode does not expand and which fails silently - and the next page covers it in full.

Is reading into the subtree a second path?

Section titled “Is reading into the subtree a second path?”

You started at the root, but the task sends the agent reading into cmd/sync/. Does the nested AGENTS.md down there apply? In the current source, yes, although the public rules documentation describes only two paths: the walk-up from the start directory and explicit instructions entries. When the read tool opens a file, OpenCode walks up from that file’s directory toward the start directory and attaches any instruction file on the way that the session has not already received. This read-time walk stops at the start directory, so it covers only files beneath it; in a session started in cmd/sync/, a read of internal/feed/parser.go attaches nothing. The file arrives inside that read result, not in the system prompt (instruction.ts and read.ts at v1.18.32, as of October 2026).

So a root-started session that asks for a handler change gets the envelope rule once the agent reads a file under cmd/sync/. Two limits follow. The rule is absent until that read, so a task that creates a new file there without reading a neighbor first may never see it. And the behavior is undocumented, so a release can change it without notice; the closed issues #6316 and #7576 discuss nested-context behavior, and they are feature discussions, not a documented contract. If a nested rule must load from the first message no matter where you start, list it in opencode.json’s instructions field.

The loading paths answer one question: which files reach the agent, from where. That question is bigger than rules. Skills, commands, custom agents, and opencode.json itself all draw the same project-versus-home line - a project scope rooted at the repo, and a home scope under ~/.config/opencode/ - and it’s worth holding the whole layout once rather than rediscovering it a surface at a time.

One warning before you use the explorer: its AGENTS.md cards still describe nearest-file selection. The walk described above, checked against the source, is the current behavior. Its global agents/ card also lists a scout subagent that the current release no longer ships.

committedin git, shared with your teamgitignoredlocal only, never sharedauto-generatedwritten by the tool; do not hand-edit
your-project/
~ (home directory)
Select a file or folder

Every entry shows what the file does, when OpenCode reads it, whether it belongs in git, and a working example.

One gap is still open. Everything above loads because OpenCode went looking for it by name, in a place it already knew to check. That covers the conventions that belong in AGENTS.md, but feedmill’s parser notes and its rate-limit policy are long enough to deserve files of their own - and OpenCode won’t find those on its own. Next: point at more instruction files.