Discovery and the nesting walk
The root AGENTS.md from the last lesson is working. UTC at ingest, fetches through the limiter - OpenCode names both rules back to you on every relevant task, and you’ve stopped reviewing the same two mistakes. So you reach for the obvious next step. feedmill’s HTTP sync server lives under server/ 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 drop a second AGENTS.md inside server/ with just that one rule in it.
Then you stop, because you’ve been bitten by exactly this before - in a different tool. There, a rules file only ever came from the nearest directory: the moment you nested one, it took over and the root rules silently stopped applying to that subtree. You’ve learned to distrust nesting. So before you trust the server task, you ask OpenCode what it’s actually reading.
It turns out OpenCode does not work the way that other tool does.
OpenCode walks up to the first project match
Section titled “OpenCode walks up to the first project match”Here’s the mechanism, and it’s worth holding precisely because most people arrive expecting the opposite. When OpenCode builds its context for a turn, it walks up from the current working directory toward the worktree root looking for project instruction files. The first matching filename category wins: a nearer AGENTS.md takes precedence over an ancestor AGENTS.md, and AGENTS.md takes precedence over the Claude-compatible fallback CLAUDE.md. It does not automatically concatenate every ancestor file. This is the behavior described in OpenCode’s rules documentation.
So when you’re working from inside server/, the walk starts there and finds server/AGENTS.md. It stops using the project-level discovery result there; the root feedmill/AGENTS.md is not automatically added to that turn. Your server-only rule wins, but the root UTC and rate-limit rules no longer arrive through the same implicit path. If both sets are required, make that composition explicit with instructions.
The whole walk, from the global file down to the subtree:
~/.config/opencode/AGENTS.md global rules (combined with project rules) │ ▼ feedmill/server/AGENTS.md nearest project match (wins) │ ╰── feedmill/AGENTS.md used when no nearer project match exists
result: global rules + the selected project instruction fileThe 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 “only the filename matters, while every directory-level copy is loaded.” The safer reading is the one to teach: the nearest project instruction file selected by the upward walk is the implicit project rules source. The docs’ own example also pins down the filename precedence: if you have both AGENTS.md and CLAUDE.md, only AGENTS.md is used. Global rules are a separate category and are combined with the selected project file.
So “first match wins” is real, and so is “project and global rules combine.” They’re answers to two different questions: which project file do I pick in this directory versus which separate global and explicit instruction sources are added.
Why people expect the opposite
Section titled “Why people expect the opposite”If you’ve come from a tool that combines every ancestor rules file, OpenCode’s nearest-project-file behavior is the important difference: dropping a file in server/ can hide root project rules from implicit discovery. Other tools differ, so verify their current rules documentation before transferring this mental model.
OpenCode makes the boundary explicit, but it introduces a different trap: a rule you place three directories deep can silently replace the root project’s implicit rules for work below that directory. The failure mode is “I forgot this subtree selects a different project file.” If the root rules must remain active too, name both files in instructions instead of relying on directory discovery.
You can confirm the intended rule set without asking the model to self-report it. OpenCode’s public docs document cwd walk-up plus explicit instructions paths, globs, or URLs; they do not promise a debug command or a stable system-prompt header. When the exact set must be location-independent, put it in opencode.json’s instructions field.
The point of looking is the same either way: confirm the rules layer is what you think it is, before a wrong answer tells you it wasn’t.
When you do want per-subtree files
Section titled “When you do want per-subtree files”Walk-up discovery covers the simple case, but it has two limits worth naming. First, it selects one project instruction file based on where you start; a file in parsers/json/ won’t be in the turn’s base context when you’re working from server/, because the walk goes up, not sideways. Second, the set of files the walk-up loads depends on where you happen to be, 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", "packages/*/AGENTS.md"]}Every matched instructions file is loaded and combined with the selected project and global rules. The difference from the walk-up is implicit-by-location versus explicit-by-config. The walk-up selects one project file based on cwd; the instructions glob gathers exactly the set you name, from anywhere in the tree, every turn. Entries can be relative paths, globs, ~/-rooted paths, or even remote https:// URLs (fetched with a short timeout).
That’s the next lesson, in full. You’ve seen how discovery actually behaves - the walk climbs toward the worktree root and selects the nearest matching project instruction file, while global rules are combined separately - and where the explicit additive alternative lives. Now wire it up on purpose: split feedmill’s long-lived conventions into their own files and load them by name.
One more way nested files load: when you read into the subtree
Section titled “One more way nested files load: when you read into the subtree”Do not treat a below-cwd read as a second documented discovery path. The public rules documentation documents walk-up from the current directory and explicit instructions paths or globs. The closed issues #6316 and #7576 are feature discussions, not release evidence. If a nested rule must load no matter where you start, list it in opencode.json’s instructions field.
The documented loading paths answer one question: which files reach the agent, from where. That question is bigger than rules. Skills, commands, agents, and opencode.json itself all draw the same project-versus-home line, and it’s worth holding the whole layout once rather than rediscovering it a surface at a time:
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.