ANTIGRAVITY LABJP
Articles/Antigravity Basics
Antigravity Basics/2026-08-02Advanced

I Copied the Same agent.md Into Another Repo and It Quietly Did a Different Job

CLI 1.1.6 lets you carry agent definitions around as files. I dropped one definition into eight repos, built a preflight that resolves its declared capabilities before the agent runs, and measured it against a naive checker.

Antigravity347Antigravity CLI19Agent Design7MCP20Measurement3Indie Development7

Premium Article

Just past 11 pm, I copied a release-notes agent definition from one app's repo into another. I liked the version I had written the week before, so reusing it felt obvious.

The next morning I stopped reading halfway through the output.

The tag diff was correct. The prose was fine. But the draft had never reached Slack. Digging through the logs, I found no trace of the agent even attempting to post. A tool it could not reach had simply stopped existing, and the run continued as if nothing were missing.

CLI 1.1.6 lets you define a custom agent as a Markdown file. Roles and procedures become text, so keeping the definition in the repo under version control falls out naturally. Being portable, though, also means depending on wherever you land.

The definition travels. The environment the definition assumes does not.

As an indie developer I keep each app in its own repository — eight of them. The whole point of putting the workflow in a file was to avoid writing it eight times. If the file quietly does a different job in each place, half of that benefit disappears.

This is a record of the mechanism I built to get that half back, and of a misreading I only found while implementing it.

What agent.md Assumes Without Saying So

In 1.1.6, an agent.md declares its behavior through YAML frontmatter. mainAgent and subagent set its position, inheritMcp controls MCP configuration inheritance, and commandExecutionPolicy governs command execution.

Here is the definition used throughout this article.

---
name: release-notes
mainAgent: false
subagent: true
hidden: false
inheritMcp: true
model: inherit
commandExecutionPolicy: allowlist
allowedCommands:
  - "git log"
  - "git tag"
  - "npm run build"
requiredMcpTools:
  - "github.list_releases"
  - "github.create_release"
  - "slack.post_message"
---
 
# Draft the release notes
 
Read the diff since the most recent tag and turn it into store-ready copy.

requiredMcpTools is not a specification key. It is a declaration I added myself, and it is the entry point for the whole design.

inheritMcp: true means "inherit the MCP configuration from the target environment." It guarantees nothing about whether the servers you need exist there. Inheritance covers configuration, not capability.

allowedCommands has the same shape. Permitting npm run build does not mean npm is installed. Permission and existence are separate questions.

So an agent.md carries two layers of assumption: the ones written down, and the ones that only surface at runtime.

If the agent halted the moment it discovered a missing tool, that would be recoverable. In practice it tends to look for a substitute and keep going. On an unattended nightly run, that difference stays invisible until the next morning.

Which is why I want the check to happen before the run, not during it.

Building a Test Bed

I cannot publish my actual repositories, so the measurements come from eight synthetic repos generated with a fixed seed, matching the structure and scale of the real ones. The generator is included, so you can reproduce the same numbers locally.

Four things vary: the spelling of the URL key in mcp_config.json, which MCP servers exist, how each server names its tools, and which commands are installed.

#!/usr/bin/env python3
"""mkfixture.py — build the test repos and agent.md files from a fixed seed"""
import json, os, random, shutil, textwrap
random.seed(20260802)
ROOT = "/tmp/exp/fixture"
shutil.rmtree(ROOT, ignore_errors=True)
 
AGENT_MD = textwrap.dedent("""\
---
name: release-notes
mainAgent: false
subagent: true
hidden: false
inheritMcp: true
model: inherit
commandExecutionPolicy: allowlist
allowedCommands:
  - "git log"
  - "git tag"
  - "npm run build"
requiredMcpTools:
  - "github.list_releases"
  - "github.create_release"
  - "slack.post_message"
---
 
# Draft the release notes
 
Read the diff since the most recent tag and turn it into store-ready copy.
""")
 
# (repo name, URL key spelling, {server: [tool names]}, available commands)
SPECS = [
    ("wallpaper-ios",     "serverUrl", {"github": ["list_releases", "create_release"],
                                        "slack": ["post_message"]}, ["git", "npm"]),
    ("wallpaper-android", "url",       {"github": ["list_releases", "create_release"],
                                        "slack": ["post_message"]}, ["git", "npm"]),
    ("calm-sounds",       "serverUrl", {"github": ["list_releases", "create_release"]},
                                        ["git", "npm"]),
    ("calm-sounds-web",   "url",       {"github": ["list_releases"],
                                        "slack": ["post_message"]}, ["git", "npm"]),
    ("photo-frame",       "serverUrl", {"github": ["listReleases", "createRelease"],
                                        "slack": ["post_message"]}, ["git", "npm"]),
    ("photo-frame-tv",    "url",       {"github": ["list_releases", "create_release"],
                                        "slack": ["post_message"]}, ["git"]),
    ("sleep-timer",       "serverUrl", {"gh": ["list_releases", "create_release"],
                                        "slack": ["post_message"]}, ["git", "npm"]),
    ("sleep-timer-mac",   "url",       {"github": ["list_releases", "create_release"],
                                        "slack": ["post_message"]}, ["git", "npm"]),
]
 
for repo, url_key, servers, cmds in SPECS:
    d = os.path.join(ROOT, repo)
    os.makedirs(os.path.join(d, ".antigravity", "agents"), exist_ok=True)
    with open(os.path.join(d, ".antigravity", "agents", "release-notes.md"), "w") as f:
        f.write(AGENT_MD)
    cfg = {"mcpServers": {}}
    for name, tools in servers.items():
        cfg["mcpServers"][name] = {
            url_key: f"https://mcp.internal.example/{name}",
            "timeoutMs": 15000,
            "tools": tools,
        }
    with open(os.path.join(d, ".antigravity", "mcp_config.json"), "w") as f:
        json.dump(cfg, f, indent=2)
    with open(os.path.join(d, "commands.json"), "w") as f:
        json.dump(sorted(cmds), f)
 
print(f"created {len(SPECS)} repos under {ROOT}")

commands.json stands in for the set of commands available in each repo. In production that role belongs to shutil.which. I wrote it to a file here so the experiment stays deterministic.

The spread mirrors my actual situation fairly closely. The repos were created at different times, so their config files were written in slightly different styles.

Thank you for reading this far.

Continue Reading

What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.

WHAT YOU'LL LEARN
A working preflight that resolves requiredMcpTools and allowedCommands against the target repo, with measured runtimes of 10.64 ms across 8 repos and 318 ms across 240
Why 14 of the naive checker's 17 findings (82.4%) were noise once url and serverUrl were normalized, and how to collapse the spelling drift
The one real blocker the noisier checker never saw: it lived in allowedCommands, not in MCP
Secure payment via Stripe · Cancel anytime

Unlock This Article

Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.

or
Unlock all articles with Membership →
Share

Thank You for Reading

Antigravity Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • Copy-paste ready implementation code
  • New advanced guides published daily
  • $5/mo or $10 for lifetime access
View Membership →

Related Articles

Antigravity2026-07-01
When Your MCP Servers Vanish From the Chat App in Antigravity 2.0
After Antigravity 2.0 split into an IDE and a separate chat-style agent app, an MCP server I had set up in the IDE stopped showing up on the chat side. Here is why the settings scopes are separate, and how to fix it by making a single workspace-level source of truth that both apps read.
Antigravity2026-06-15
As Tools Consolidate Into Antigravity, Measure Your Lock-In and Keep an Exit Path
As Google consolidates its AI coding tools into Antigravity and the personal Gemini CLI sunsets on June 18, here is a practical design for quantifying vendor lock-in and keeping a configuration you can step away from at any time.
Antigravity2026-05-23
Wiring Antigravity 2.0 to Chrome DevTools for agents 1.0: Lighthouse Audits, Extension Debugging, Memory Hunts, and an Operational Plan
Chrome DevTools for agents 1.0 went stable and now ships bundled with Antigravity 2.0. Here is the practical setup I run across my 50M-download indie app business: Lighthouse audits, extension QA, memory leak triage, and auto-connect rules.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links
See all →