Why your kilo.jsonc is not the complete list
Kilo's current rules model is explicit and, on its own, well-behaved. Project rules are "configured via the instructions key in your project's kilo.jsonc file," where "each entry points to a file path or glob pattern." Global rules use the same key in the global config, "typically at ~/.config/kilo/kilo.jsonc."
Load order follows the array: "Rules are loaded in the order they appear in the instructions array in kilo.jsonc," first the global config, then the project's. Precedence resolves the other way — "project-level instructions take precedence over global instructions for conflicting directives." You can even park a rule without deleting it, since "JSONC supports // comments."
That is a manifest, and a manifest is a good design. Everything below is what sits outside it.
Glob entries give up ordering. The documentation is direct: "Files matched by glob patterns are loaded in filesystem order." Since the array position is your only precedence control, a single entry like .kilo/rules/*.md converts your ordered list into an unordered one for everything it matches. The example config in the docs pairs a specific file with a glob covering the same directory, which reads as convenient and quietly removes your ability to say which rule wins.
The legacy directory loads without being listed. This is the one that costs people days:
"If.kilocode/rules/directories exist in your project, their contents are automatically included for backward compatibility. To fully migrate, move your rule files and reference them inkilo.jsonc."
Read that as a live behaviour, not a note. Kilo renamed its directory from .kilocode/ to .kilo/, and the old one still loads — automatically, with no entry in your array and no position in the priority table. A repository that passed through an earlier version has a second rule tree running in parallel, and the kilo.jsonc you are reading does not mention it.
Per-directory AGENTS.md files are injected on file access. Kilo supports AGENTS.md in subdirectories, and describes the mechanism precisely: "Per-directory AGENTS.md files are dynamically loaded when the agent reads files in that directory - they are not pre-loaded at session start. When the agent reads a file in src/backend/, the corresponding AGENTS.md is discovered and its contents are injected into the conversation as <system-reminder> tags."
That is a good feature. It also means your effective instruction set changes mid-conversation depending on which files the agent happened to open, and the priority table — which covers only the root file — does not describe where those injections rank.
Root AGENTS.md cannot be switched off. "AGENTS.md itself cannot be individually disabled — it is always loaded if present. To override its instructions, use higher-priority sources like the instructions config key or agent-specific prompts." Both AGENTS.md and AGENT.md are also "write-protected files in Kilo Code," so "the AI agent cannot modify these files without explicit user approval." Good for safety, and worth knowing before you ask the agent to tidy them.
The filename is case-sensitive in a way that surprises people. Kilo warns: "The filename must be uppercase (AGENTS.md), not lowercase (agents.md)." Supported names in order of precedence are AGENTS.md then AGENT.md. Other agents differ here — some treat the two cases as equivalent — so a shared repository should standardize on uppercase.
The old memory bank is still live. Kilo's deprecation notice says "the Kilo Code memory bank feature has been deprecated in favor of AGENTS.md," and immediately adds: "Existing memory bank rules will continue to work." The status indicators are the part to watch — "legacy Memory Bank status indicators such as [Memory Bank: Active] and [Memory Bank: Missing] can still appear, but they are not guaranteed across all clients or modes." That caveat is doing real work: the badge is scoped to the clients and modes where it is supported, so treat it as a hint rather than as the answer, and verify by behaviour instead.
Together these are five sources beyond your array. That is the actual cause of most "my rule isn't applying" reports, and it is a configuration problem rather than a model problem — the distinction we drew in why agents ignore your instruction files.
What people try instead
Reading kilo.jsonc and trusting it. The most common approach, and it is wrong by construction. The array is one of several inputs, and the two silent ones — the legacy directory and per-directory injections — are exactly the ones a config file cannot show you.
Adding a broad glob to catch everything. ".kilo/rules/**/*.md" guarantees nothing is missed and gives up ordering for the whole set, since glob matches load "in filesystem order." You have traded an incomplete list for an unordered one.
Deleting .kilocode/ immediately. Correct destination, risky first move. Those files have been in effect, possibly for months, and some of the behaviour you rely on may come from them. Move them out of the load path and into review, then decide.
Putting everything in AGENTS.md. It ranks third, it cannot be disabled, and it is the format other tools read. But it takes no frontmatter and no conditional loading, so a single root file becomes always-on context for every task. The always-on budget question is the same one we examined in what coding agents actually read.
Turning rules into skills. Skills are loaded on demand, which sounds like free conditionality, and Kilo is unusually candid about how selection works: "The agent (LLM) decides whether to use a skill based on the skill's description field. There's no keyword matching or semantic search—the agent evaluates your request against all available skill descriptions and determines if one 'clearly and unambiguously applies.'" A convention that must always hold does not survive a relevance judgement — which is why a skill is not a substitute for a rule, and not a substitute for memory either, as we argued in why agent skills aren't memory.
The Fix: Build one ordered list, then prove it is the whole list
Three steps. The third is the one people skip, and it is the only one that catches the silent sources.
Step 1: Inventory every path Kilo can load from
Walk the repository and write down what exists at each of these locations, before changing anything.
Start with the declared set: the instructions array in the project kilo.jsonc, and the same key in ~/.config/kilo/kilo.jsonc. Expand every glob by hand into the actual filenames it matches, in filesystem order, so you can see the order Kilo will use.
Then the undeclared set. Check for .kilocode/rules/ anywhere in the project — the notice says "directories," plural, so check subdirectories too. Check for .kilo/rules/memory-bank/ and the legacy .kilocode/rules/memory-bank/. Find every AGENTS.md and AGENT.md in the tree, not just the root one, and note which directory each governs. If you use the CLI, check .claude/ and .agents/ as well, since those are read "for compatibility with other tools"; external skill directories can be turned off with the KILO_DISABLE_EXTERNAL_SKILLS environment variable if you want them out of the picture.
You now have a list. It will be longer than your kilo.jsonc, and the difference is the thing you did not know was running.
Step 2: Collapse the list into one declared, ordered array
Decide a destination for each item, then make the array match reality.
Repository-wide conventions that must always apply go into root AGENTS.md — it cannot be disabled, other tools read it, and it is write-protected against accidental agent edits. Keep it short; it is always-on.
Rules that need explicit ordering go into .kilo/rules/ as individual files, each listed by full path in the project instructions array. List them one per line in the order you want. Do not use a glob here — a glob is precisely the thing that discards your ordering.
Directory-specific guidance goes into per-directory AGENTS.md files, which is the only mechanism Kilo documents for content that loads based on where the agent is working.
Personal preferences go into the global kilo.jsonc array, remembering that global sits at priority 4, below both the project array and root AGENTS.md.
Then empty the legacy path. Move .kilocode/rules/ contents somewhere outside the load path — a docs/ subdirectory works — and re-add only what you actually want, by explicit path in the array. Same for the memory bank: Kilo's own migration instruction is to "examine the contents in .kilo/rules/memory-bank/ (or the legacy .kilocode/rules/memory-bank/)" and "move that content into your project's AGENTS.md file." Move the parts that are still true; delete the parts that describe an older version of the codebase.
Step 3: Prove the list by contradiction, not by reading
A config file cannot verify itself against files it does not mention. So test the boundary directly.
Put a deliberately unusual, harmless instruction at the bottom of your intended lowest-priority source — something like an unmistakable variable-naming convention you never otherwise use. Ask the agent to write a small function. If the convention appears, the source is loading and nothing above it contradicts it.
Then invert the test. Put a directly contradicting instruction into a source you believe is not loading — the legacy .kilocode/rules/ path, before you empty it. Ask again. If the contradicting version wins, that path is live and outranks what you thought was authoritative. Kilo does not surface conflicts between instruction sources, so a contradiction test is the only way to see the resolution; the general pattern is covered in detecting memory conflicts.
Repeat once from a subdirectory that has its own AGENTS.md, since per-directory files are injected only when the agent reads a file there. A test run from the repository root will not exercise them.
Setting this up in MemoryLake
Consolidation gets you an ordered list. It does not tell you why any line is on it — and a rule with no recorded reason is the first thing dropped during the next cleanup.
MemoryLake holds the reasons: which rule exists because of which incident, what was tried and rejected, and which constraints came from a person rather than the code. It sits outside the instructions array, so reordering, renaming, and directory migrations cannot lose it. Get started here.
Step 1: Create an API key
Create a workspace for the repository and generate an API key. Scope it to the repository, not to Kilo, so the next tool migration is a configuration change rather than a rewrite.

Step 2: Upload your first memories
Do this during Step 1 of the consolidation, while you are reading files you did not know were loading. For each rule you find in .kilocode/rules/, record why it was written before you decide whether to keep it — that judgement is much harder to make later. Add the memory bank contents that are still accurate, and note which parts you deleted and why.

Step 3: Connect your AI & agents
Connect Kilo Code across the surfaces you use; the VS Code extension and the CLI share the priority table but differ on external directories, so both should read the same decision set. If you still run Cline on the same repository, connect it too — the two share rule-file heritage, and a shared layer keeps them from drifting.

What this changes in practice
"My rule isn't applying" becomes a five-minute check instead of an afternoon. You have an inventory, so the question is which of the known sources is winning, not whether some unknown file exists.
Ordering becomes real. Replacing globs with explicit paths means the array position actually determines precedence, so a conflict has a predictable answer you can point at.
The legacy directory stops being a second source of truth. Once .kilocode/rules/ is empty, the priority table plus your per-directory AGENTS.md map is the complete picture.
And the reasons survive the cleanup. Consolidation deletes files by design. Deleting a rule is fine; deleting the only record of why it existed is how the same argument gets had again in six months.
Best practices for Kilo Code rule files
List paths explicitly, not globs. One line per file in the instructions array is more verbose and it is the only way to control order. Reserve globs for directories where order genuinely does not matter.
Comment out instead of deleting. JSONC comments let you park a rule with a note about why it is parked, which is more informative than a missing line.
Keep root AGENTS.md short. It cannot be disabled and it is always loaded, so every line is a permanent context cost. Push anything area-specific into a per-directory file.
Standardize on uppercase AGENTS.md. Kilo requires it, and tools disagree on case sensitivity, so uppercase is the zero-cost choice for a shared repository.
Do not trust the memory bank status indicators. Kilo says they "can still appear, but they are not guaranteed across all clients or modes." Verify by contradiction instead.
Re-run the contradiction test after any dependency or version bump. Backward-compatibility paths are exactly the kind of thing that changes between releases, and a silent change here looks like the model got worse — the misdiagnosis behind a lot of what we covered in Cline forgetting project context, on the tool Kilo forked from. If you are still planning that move, migrating from Cline to Kilo Code covers the file mapping; this guide is about what to do once you are there.
Conclusion
Kilo Code's instructions array is a real manifest, and manifests are the right idea. The catch is that three of Kilo's loading behaviours sit outside it: the legacy .kilocode/rules/ directory loads automatically, per-directory AGENTS.md files are injected when the agent reads files near them, and the CLI reads compatibility directories from other tools.
Consolidating means naming every path explicitly, emptying the ones you did not name, and then testing by contradiction — because a configuration file cannot audit sources it never mentions. Do that once and the priority table becomes an accurate description of your project instead of a partial one.