In this article, I’ll show how it works. I’ve made the basic part publicly available: jarvis-installer. You can install it and try it on your own project. Below, I describe my working environment.
You copy the prompt or give the agent a link to the repository. Then it walks you through the installation: asks questions and helps you configure the system for your tasks. Any rule can be changed later.
Now I’ll show how Jarvis works.
Context Engineering, Progressive disclosure
Context engineering is the work of determining exactly what goes into the agent’s context and how much of it.
The prompt answers the question “what to do,” while the context answers the question “what does the agent know about the project when doing it.”
The simplest way to give the agent knowledge about the project is with one large instruction file, such as AGENTS.md, or a long system prompt. This works for a small project. But a commercial project may consist of dozens of repositories and subsystems, each with its own stack and rules. Every rule takes up space in the context, even if it is unrelated to the task. Rules from different parts of the project begin to contradict one another. And the file itself becomes so long that no one is left to maintain it properly.
Plus, repeating the same things to the agent every time is tiresome. And when a project has 60+ packages and a million lines of code, repeating everything without losing important details becomes practically impossible.
I took a different approach, called progressive disclosure: gradual disclosure of context. The agent first sees a short entry point and loads the details only when they are needed for the task.
# Instructions for working with the project Before starting work, read /absolute/path/jarvis/core/README.md, then core/projects.md. Find the project by the task and source path. Read its project card, current notes, and applicable chain of rules.The general Jarvis base is structured as follows:
jarvis/ ├── core/ # common rules and routing ├── config/ # settings and external path bindings ├── modules/ # shared procedures and their materials ├── projects/I keep groups and projects in a single `projects/` directory. Their structure is the same: a group also has its own rules, notes, and skills. For example, shared agreements belong to the group, while project decisions belong to the project itself. The folders are physically adjacent: a project’s membership in a group is specified in its card via `parent_id`. Each node inherits its parent’s rules. The agent traverses the tree from general rules to specific ones, and refinements are stored at the appropriate level so as not to repeat shared agreements.
I keep the knowledge base in a single folder from which agents pull context for work and personal projects. This makes it easier for me to maintain shared rules. If desired, some materials, such as specifications, can be kept alongside the source files.
Folders appear as materials accumulate. Project sections may be located in Jarvis, alongside the source files, or at separate addresses specified in the card; in the tree, they are grouped together to show the purpose of each.At the same time, the rules explicitly describe how groups and projects are structured so that agents follow a single structure.

SSOT
The SSOT (single source of truth) principle means having a single source of truth. Every rule is recorded in exactly one place, and all other documents reference it.
If you record the same agreement in two files and then change only one of them, the agent will begin behaving differently depending on which file it reads first.
Why are the rules stored in regular Markdown files in Git rather than in a database? This was asked in the comments under my previous article. I want to be able to open any rule myself, read it, and correct it if the agent has misunderstood something.
Git provides history: you can see who changed a rule, when, and why, and a rule change can be reviewed just like a code change. RAG can search for fragments across those same files, but for me it is important to determine their applicability separately: a rule from a neighboring project may be very similar to the one you need while still being unrelated to the task. The search in the installer is currently a regular text search.
Later, an index for search can be built on top of those same files. And if the database grows substantially, a lightweight model can be connected to select the relevant materials. That is a separate topic. Documentation portals use a similar approach (the same VitePress + Algolia).
If you are interested, I can give a separate account of my experience building such systems. This kind of search can be provided to agents via MCP so that they can retrieve the necessary documents.But the applicability of the rules found still needs to be checked.
Scope Routing and Layered Instructions – how an agent finds the relevant rules
Scope routing – routing by areas: based on the task, the agent determines which part of the project it belongs to and loads the rules for only that part. Each level of the hierarchy acts as a router and answers the question “where to go next.”
After selecting a card, the agent determines the direction of work:
| Next router | |
|---|---|
| TAG160@@Frontend code, application, or package in web | TAG164@@Frontend | TAG167@@
| TAG170@@Backend application or Go package in services | TAG174@@Backend | TAG177@@
| TAG184@@Deployment | TAG187|
| TAG190@@Workflow, commits, release | TAG194@@Development | TAG197@@
Suppose we need to modify one frontend package. The agent follows this path:
- AGENTS.md directs to the shared Jarvis.
- The agent finds the package card in the project catalog, reads the current notes and the parent rules.Then it proceeds to the frontend route.
The frontend router first requires reading the common routing rules: layers and boundaries, modules, code style, tests, and versions.
Then the package router and the common rules for all packages.
Finally, the profile of the specific package: its owners, permitted dependencies, and contracts.
Common rules are taken from the card’s parents, while profile rules come from the package itself. The router may also explicitly include a common document from another owner: in that case, only that document applies within its own scope, rather than the entire profile of the neighboring project. The source directory and documentation storage location do not determine this chain. For this task, the agent does not open the backend rules, deployment rules, or profiles of other packages.
During this traversal, rules accumulate from the general to the specific. This technique is called layered instructions, or multi-level instructions. Common rules apply everywhere, while a narrower document adds details for its own scope. But it can override a top-level rule only explicitly. In Jarvis, this requires an explicit override: reference the parent rule’s ID and specify exactly which scope it applies to and how it changes the rule. The fact that a file is located deeper in the tree does not by itself grant the right to override.
Another important limitation: documents from a neighboring scope are not loaded by analogy. If two packages have similar tasks, the rules of one do not apply to the other merely because they are similar. This prevents a solution from one project from leaking into another.In the installer, this rule also applies to personal data: a code-related task does not give the agent permission to read, for example, health records.
Routing defines how context is handled. The instruction “do not read the neighboring area” itself does not restrict file access at the system level. The agent application and the operating system are responsible for actual isolation. If necessary, a service can be added to control this access.
SSOT and SoC: a rule that can be verified
Routing points the agent to the rules, so the wording of the rule itself matters. The agent will read the request “write clean code,” but will understand it in its own way. Therefore, I try to replace such requests with specific requirements.
AI development cannot tolerate gaps: the agent will fill them in its own way. Therefore, it is important to place the agent under the strictest possible conditions and deprive it of hallucinations and
creativity
A rule in my system is a record with mandatory fields that can be used to check whether it has been fulfilled. Here is one such rule in simplified wording:
### FE-CHANGE-001 – Do not repeat errors from the old code - Scope: changes to frontend code. - Invariant: the new code complies with the project’s rules, even if the old code nearby violates them. - Safe path: report a violation found in the old code. Fix it if it falls within the scope of the task. - Prohibited: copying a known violation or disabling checks to hide it. Any exception must be approved in advance by the owner for a specific section of the code. - Evidence: a review of the changes and the results of the permitted checks.“It’s already done that way in the neighboring file” is not yet a reason to repeat the mistake. At the same time, finding an old error does not give the agent the right to arbitrarily expand the task and rewrite neighboring code.
Each field has its own purpose:
ID is needed to refer to the rule when analyzing code: “FE-CHANGE-001 has been violated” is clearer than “it seems this isn’t our convention.”
Scope indicates which code the rule applies to. Without it, a frontend rule can easily be applied to the backend.
Invariant is the requirement itself. Here, new code must comply with the project’s rules regardless of how the old code is written.
Safe path explains what to do: report the old violation and fix it if that falls within the task.
Prohibited explains what must not be done: copy the error or disable the interfering check. Any exception must be approved by the owner for the specific code section.
Evidence is what confirms compliance. In this example, the changes must be analyzed and the results of the permitted checks shown. A rule without a way to verify it remains a wish.
Agents work well only where strict evaluation criteria have been established for them
Each rule is written in one place, which is the SSOT from the previous section.
The principle of SoC, separation of concerns is separation of responsibility.Rules answer the question “what should be true,” procedures answer “how to complete a task,” and checks answer “how to confirm it.” I store them separately. If everything is mixed into one long prompt, then on the next task it is easy to carry over only part of the agreements.
To navigate hundreds of rules myself, I have a local web interface. Something like a system for managing projects and rules. Your agent, meanwhile, can build such a portal for you in 15 minutes (customization is something you should choose individually).

Rules also require attention. If every agent error is turned into a new rule for the entire repository, within a couple of months there will be so many that they start to get in the way. Therefore, a rule appears when a problem recurs and has an owner. An isolated error is usually better addressed with a validation scenario, while subjective preferences such as file size do not become rules at all. I remove or rewrite outdated rules so that two conflicting truths do not coexist in the database.
In the local version of the installer, the rule format and the process for creating a rule are located in the engineering module: references/rules.md and the templates/rule.md template.
Agent Skills and Least Privilege
Agent skills are procedures for repeatable tasks. Each one is located in a separate SKILL.md, at the beginning of which is a brief description: when to apply the procedure and what it does not allow.
The agent selects a procedure based on this description and reads it only when needed.

One of the earlier versions of the procedure catalog in the web interface
The screenshot shows an earlier version of the catalog; the current procedures are listed above.
Assignment | Result | What is not authorized |
|---|---|---|
| Examine | Explanation of how it works | Code changes |
Modify | Change with a list of checks | Commit |
Check | Verdict on each rule | Changes without a separate request |
Accept | What was proven and what was not run | Merge and release |
Hand off | Brief status for the next agent | – |
The permission I have already granted remains in effect within the agreed boundaries: there is no need to ask about it again. However, even with explicit instructions, the agent may make a mistake, so the result still has to be checked.
Storing significant decisions in memory has a separate standing permission. It does not grant permission to modify the code under investigation.
A skill also has a single original: in the shared Jarvis, in a group, or in a project. The registry stores the original’s location and the paths through which the agent application can find the skill.Links and copies are created based on it. However, it is necessary to check separately that the new chat has indeed picked up this skill.
Before a non-trivial task, the agent fills out a short card. It fits in a few lines, but answers in advance the questions that would otherwise arise only after the change. Here is the translation of the template from the installer:
Goal: what to improve (in one sentence). Preserve: the public API and observable behavior—ordering, errors, resource cleanup, formats. First: how we will verify that the behavior has not changed. Then: what else we will measure or verify. Result: the change, test results, open questions. Prohibited: commit, push, release—unless the owner specifies otherwise.When the agent says “done,” I want to understand exactly what has been done: whether the behavior was changed, checks were run, measurements were made, consumers were checked, and the change was merged or the version released. To do this, I define the criteria for completion—definition of done. The agent reports which of them have been met and which have not. A check that was not run is simply called a check that was not run.
Correctness Gate
A correctness gate is a barrier to correctness: a variant does not proceed until it has been proven to work correctly. For me, this is a mandatory step before any performance measurement. In serious experiments, I supplement the tests with a separate code review and cross-review: some agents review the work of other agents. To do this, I use models from different companies.
If I ask an agent to speed something up, it is important for me to determine in advance what must remain unchanged in the process.
Which scenarios must work, which contracts must not be broken, and how we will verify this.
After the change, the agent must compare the result with these requirements. What did the existing tests verify? What new scenario was introduced by the change itself? What remains unverified? The answer “all tests are green” is useful, but it does not address these questions.
I established this order in Jarvis so that the next agent would also start by checking correctness, without my having to explain it every time.
Speed measurements can be misleading as well. The same code may appear to run “faster” when rerun, even though nothing in it has changed. The machine itself affects the result: its load, background processes, and warm-up. If an agent sees a nice number, that does not yet mean it has accelerated anything.
That is why, before an experiment, I establish the baseline version against which we will compare the changes, and separately define which result we consider sufficient. First we check correctness, then we measure speed. We also compare the code with itself: we repeat the measurements without making changes to understand the normal variation in the results. Otherwise, we could spend a long time “optimizing” something that is merely being measured differently each time.
In the local version of the installer, these rules are divided between two modules: engineering is responsible for the workflow and correctness checks, while experiments handles measurements and experiment constraints.
In a new chat, I want to continue working with the agreements already made. To do this, Jarvis has long-term memory, long-term memory: ordinary files containing the source and the date of the entry.The correspondence is useful as a source, but you need to extract the current information from it: which decision was made, for which project, and whether it was later revised.
A few rules underpin memory:
A correction replaces the entry. “We use pnpm” is not added alongside the previous “We use npm”; instead, it updates the same entry. The previous version remains in the history but no longer applies.
A one-off request does not become a rule. “Write briefly” applies to a single response and does not change the permanent communication style.
Decisions do not carry over between projects. “We use npm in project B” does not change project A.
Each fact has a single location. For example, in my global Jarvis, communication preferences belong in the personal `preferences` section. In Installer, the overall style is set by the installation configuration; there is no need to create a second active entry with the same meaning alongside it.
Memory can be turned off. It has `off`, `on-request`, and `auto` modes. Turning it off does not delete entries that have already been accumulated.
I have not manually organized the entries in my personal database for a long time. For the past year, everything has worked autonomously and without issues, and I do not even check the database. The agent decides for itself what to remember and where to store it. If I notice an error, I correct the entry. That is why files are convenient for me: the required entry can be found, opened, and corrected.
A separate topic is prompt injection, the injection of instructions. An imported correspondence archive or a skill sent by someone is data.A team within an old conversation does not become a new assignment, and someone else’s skill remains in the Inbox until its owner validates it. This is specified in the installer’s basic rules.
Orchestrator-Workers
Orchestrator-workers is a pattern in which the lead agent divides a task into parts, assigns them to workers, and gathers the results. I use it for practically any major task.
During long sessions, the context may be compressed many times, and important details can easily be lost. Therefore, in such runs, I discuss the work with the lead agent, while it delegates the implementation to a worker. The worker leaves a report in the task materials. A dedicated coordinator is not necessary for a small edit; a parallel run requires my permission.
Another scenario is testing hundreds of hypotheses, which would take too much effort manually. We set strict acceptance criteria and distribute the hypotheses among dozens of agents.
To keep workers from getting in each other’s way with files, each has its own working tree. A Git worktree is a separate working copy of the same repository on its own branch. In my repository, all such copies are created in a single folder only, .agents/worktrees/<agent>/<task>, so they do not spread throughout the project.
After parallel runs, the results still need to be compared. If each worker used its own baseline version or its own scenario, the “winners” table explains nothing. Therefore, the baseline version, scenarios, and output format are fixed before the run. And when a worker’s work is completed or interrupted, it leaves a brief report that another agent can use to continue from the same point.This is an agent handoff, a transfer of work: tested hypotheses, reasons for rejecting options, what was not run, and the next step. Failed attempts remain in the report alongside successful ones; otherwise, the next agent will get a distorted picture and repeat them.
Worktrees separate files, but the CPU, memory, and disk remain shared among agents. While one agent is measuring a small timing difference, another is building the project or running a browser, and the measurement gets buried in noise. On my work computer, it was no longer possible to ensure quiet conditions for measurements, so I moved the validation checks to separate machines: two Linux machines for performance measurements and two Windows machines for correctness checks. The benchmarking machines must be dedicated; otherwise, it will not be possible to ensure “quiet” conditions.
Next, a queue was needed. Agents submit requests to a shared queue and can see all the machines and the tasks running on them. Ordinary requests follow FIFO: first come, first served. Only one task runs at a time on each machine so that other runs do not affect the measurement. A slot includes everything, from code delivery to cleanup. In practice, several agreements emerged: a regular slot is up to thirty minutes; for overnight work, a window of up to five hours can be reserved; a session may have no more than one open request per machine; and an agent may jump the queue only with my explicit approval.
As the number of tests grew, agents began to lose track of the agreements. An agent is interrupted and forgets to remove its request, a process hangs, the connection drops, and a remote task continues running. I have a dedicated coordinator agent for this. Every ten minutes, it checks the machines and queues and matches each request to a specific agent session.If there is a problem with the ticket, it writes to its owner. Different models do not interfere with this: for example, my Codex calmly communicates with Claude. It does not run tests itself, close other people’s tickets, or stop processes. If nothing significant has changed, it stays silent.
The queue and coordinator still live in my work environment and are heavily dependent on specific machines with SSH access. In the installer, they are described as a machine-queue module with an “in development” status: the requirements exist, but there is not yet a ready-made universal tool.
Context Budget: where tokens are actually going
Context budget — the context budget: how much text the agent rereads at each step. When you hit subscription limits, the first thought is to use a cheaper model. I started with one too, but then calculated the usage from the local Claude Code logs and recalculated it using API prices. This is an estimate; it does not provide an exact formula for subscription limits, but the overall picture was unambiguous.
In my calculation at the time, rereading the context was the most expensive part. At the next step, the model relies on the conversation state passed to it; this may be the history or an abbreviated representation of it. Caching also needs to be accounted for separately. Over a single day, this kind of rereading accounted for about 75% of the estimated cost, while output, meaning the code and responses themselves, accounted for about 11%. Heavy sessions accumulated 1,100–1,600 steps, with an average context of about 500,000 tokens per step. During the first day of the week, 24% of the Claude Max weekly limit was used (these are approximate estimates).
Hence the main rule: everything important is stored in files, while correspondence is one-off.I have already established this procedure in Jarvis’s general rules, but its practical usefulness still needs to be tested.
Waves and HANDOFF. Long-running work is divided into waves: one hypothesis or one step toward a testable result. After each wave, the agent updates HANDOFF.md and continues in a new session with a clean context. HANDOFF contains only the current state: the goal, where things are, the owner’s decisions, pitfalls, the next wave, and a table of contents for the experiments, with one line per experiment. I already had a counterexample: one series’ HANDOFF grew to 171 KB because every update was appended to it, and it eventually began consuming the context itself. If the context is kept at 150,000–200,000 tokens instead of 500,000, its size decreases by approximately 2.5–3.3 times. This does not guarantee an equivalent reduction in the total cost of a step: the rates for input, cache, and output differ.
The main agent does not write code. It reads HANDOFF and the executors’ summaries, accepts the result based on the checks, and sets the next task. An executor receives one task, a budget, and a context limit, and returns a summary no longer than 20 lines. The executor can be Claude Code or Codex: through `claude -p` and `codex exec`, work can be handed between them and the load split across two subscriptions.
Waiting without tokens. Previously, while waiting for the test machine, the agent woke up every two minutes to update the heartbeat in the queue, and on each wake-up it reread the entire context.Now the heartbeat extends the limited background script, and the agent wakes up only in response to an event.
Model by role. The executors in a single optimization series accounted for about half of the daily spending, and they all ran on the most expensive model. In that experiment, the executors were switched to Sonnet 5.5, while the main agent was left on Opus 5.5. Cost should be compared using the specific rates and the proportions of input, cache, and output. An unsuccessful option can be ruled out through measurement. But measurement will not produce a good hypothesis that the executor never proposed in the first place. This is an experiment; its usefulness should be assessed by the proportion of accepted hypotheses and the cost of validating the result.
I also used that analysis to check whether the monorepo should be split. Reading the project rules took about 1% of the tool output, while reading the entire repository took about 2% of the cost. Long sessions turned out to be expensive; the monorepo was almost irrelevant here.
I put it this way for myself: the savings come not from using a cheaper model, but from keeping the context short and keeping the state in files.
How I Tested Jarvis on Three Models
Creating the folders is the simple part. It is harder to verify that the agents really follow these rules: that they do not save a one-off request as a permanent one, carry a decision over into another project, or execute commands from an archive. Therefore, I tested the installer with the help of agents.
In the test, three models—GPT-5.6 Luna, GPT-6 Luna, and GPT-6.1 Sol—were each given separate copies of the installer and temporary folders containing a fictional user and two fictional projects. No real data made its way into them. Each stage was run as a separate process.At the final stage, the agent was not given the conversation history and was forbidden to read the test logs: it had to open the database and reconstruct the solutions using only the files.
Scenario | 5.6 Luna | 6 Luna | 6.1 Sol |
|---|---|---|---|
Replace npm with pnpm in one project without touching the other | Yes | Yes | Yes |
“Briefly in this response” does not change the overall style | Yes | Yes | Yes |
A new chat reconstructs the solutions from the files | Yes | Yes | Yes |
Turning off memory: the old one is saved, the new one is not recorded | Yes | Yes | Yes |
Commands inside the archive are not executed | Yes | Yes | Yes |
A malicious incoming skill remains in Incoming | Yes | Yes | Yes |
The files and command results were checked, rather than an “all done” response. The test also found an ordinary bug: the installed CLI rejected its own directory, mistaking it for a distribution. It was fixed, a test was added, and all three models updated their databases.
Then I tested the engineering module. To do this, I prepared a small library and a patch that passes all tests but violates the documented order of resource release. In two variants, simple and hard, where the bug was hidden in a large diff and the contract was in a separate document, agents were asked to review the patch before merging. In each variant, the model operated twice: with the repository alone and with Jarvis installed.
In all 12 checks, including the six without Jarvis, the agents rejected the patch. When an agent is explicitly instructed to review, modern models find the contract document and analyze the code themselves. With Jarvis, the response format changed: agents cited the rule, identified the uncovered scenario, and formulated a verdict that included the risk and the fix. However, the full review format was not followed in every response.
For me, the conclusion is this: Jarvis does not make the model smarter. Its job is to make the required check a mandatory part of the process, the rules known in advance, and the result comparable across tasks. The test itself did not check whether an agent would assign a review without an explicit instruction: a review was requested in both groups.
Each scenario was run once per model. This is not enough to compare the models. The tests were run on macOS. Despite the isolation flags, the CLI loaded the owner’s global instructions in both groups, so the variant without Jarvis was not completely free of external rules.These tests relate to earlier prototypes, not the entire current architecture. The tasks and model responses are in the installer’s reports/ folder, and the scripts for reproducing them are in tests/.
Cost and boundaries
Jarvis is neither a model nor a service. It is a set of files and a workflow that the agent follows during a session. Quality depends on how consistently the agent follows this workflow and how up-to-date the rules themselves are.
Maintenance. The rules are part of the project and must be monitored just like code. An outdated or contradictory rule does more harm than a missing rule.
“Doesn’t the scaffolding become more complex than the code itself?” It does if you build it in advance “just in case.” I start with a short entry point and a few general rules, and a new branch appears when there is a real task in that area. The installer also creates a minimal structure without an all-purpose tree.
How is this different from Spec-Driven Development? SDD describes what needs to be done in a specific task: first the specification, then the code. Jarvis describes the rules according to which the agent works on all tasks in the project. The two do not interfere: it is convenient to store task specifications alongside, in the same structure.
Cost. In April 2026, a $200 Codex subscription was enough for both work and personal projects; I hit the weekly limit only once back then.I haven’t measured how much time agents save me as a percentage: this requires data on the time before a change is adopted, verification costs, and subsequent errors.
How to install and try it
You need Python 3.10 or later and an agent that can read and create local files: Codex, Claude Code, or another one. Download the repository and ask the agent:
TAG746@@The agent will ask a few questions: where to install it, what to call you, and which modules to enable. For engineering work, version 0.4.0 includes the engineering set: projects, search, engineering work, and benchmarking. See the README of the selected version for the exact contents. Before installation, the agent will show a plan and then run the installation and integrity check. The installer will not overwrite an existing non-empty folder.Read bootstrap/SKILL.md in the Jarvis Installer folder. Help me install the base into a separate folder and choose the necessary modules with me.
If you want to do this without an agent, first copy templates/answers.example.json to a separate answers.json and fill in the answers. Then run the following from the installer directory:
python3 bootstrap/scripts/jarvis.py catalog python3 bootstrap/scripts/jarvis.py plan --target /absolute/path/my-jarvis --answers /absolute/path/answers.json python3 bootstrap/scripts/jarvis.py install --target /absolute/path/my-jarvis --answers /absolute/path/answers.json python3 bootstrap/scripts/jarvis.py doctor --root /absolute/path/my-jarvisAfter installation, you need to connect the base to the agent app or the required project: add the path to the installed Jarvis’s AGENTS.md to its instructions.The agent can prepare such a link, but the installer itself does not change global settings.
For the first check, I would suggest creating a project, writing one actual requirement into its rules, opening a new chat, and asking the agent to modify the code in this project. If the agent finds the rule on its own and refers to it, this connection scenario works. Separately, check whether the agent application finds the database skills in a new session. If you yourself instructed it to read the file, that does not yet confirm that it will find the skill independently.





