ANTIGRAVITY LABJP
Articles/Antigravity Basics
Antigravity Basics/2026-09-13Intermediate

When You Cannot Decide Between Rules and Skills: Rethinking Where Instructions Live

Rules and Skills load at different moments in Antigravity. Rules go in full on every turn; Skills lead with a heading and load the rest on demand. After the same note drifted apart in two places on a client site, here are the questions I use to place an instruction, plus a small script to measure what you carry each turn.

Antigravity369RulesSkills2agent design12workspace configuration

I was going back through a review on a client site late one evening when I noticed the agent had written a raw color value straight into the stylesheet. The workspace conventions say to reference a token instead.

When I went looking, the same note existed in two places. One was the conventions file. The other was a pre-delivery checklist. I had rewritten the conventions a while back and left the old wording sitting in the checklist.

The agent read the old one.

The cause was not the content of the instruction. It was that I had never decided where instructions belong. Antigravity gives you two separate containers — one for things you want in force every time, and one for things you want walked through only when asked — and the two load at completely different moments.

The two containers load at different moments

Rules hold constraints you want the agent to follow. Global rules live in ~/.gemini/GEMINI.md and apply across every workspace. Workspace rules live in the .agents/rules folder of your workspace or git root. Each file is capped at 12,000 characters.

Each rule also carries an activation setting, and you have four to choose from: manual (you call it with @ in the input box), always on, model decision (the model reads a natural-language description and decides whether to apply it), and glob (applied to files matching a pattern such as *.js or src/**/*.ts).

Skills hold procedures and knowledge as a bundle. A workspace skill lives at .agents/skills/<name>/SKILL.md, a global one at ~/.gemini/config/skills/<name>/SKILL.md. Because it is a folder rather than a single file, you can keep scripts/ and examples/ alongside it.

The difference that matters most is how each one gets read. Skills follow progressive disclosure. When a conversation starts, the agent sees only the name and description. The body is read later, and only if the skill looks relevant to what you asked for.

 RulesSkills
Workspace path.agents/rules/<name>.md.agents/skills/<name>/SKILL.md
Global path~/.gemini/GEMINI.md~/.gemini/config/skills/<name>/SKILL.md
ShapeA single Markdown fileA folder (SKILL.md plus assets)
How it loadsFull text, every turn, when activeHeading first, body on demand
Invocation@ mention / always on / model decision / glob/name, or the agent decides
Size limit12,000 characters per fileNo practical ceiling — split it up

One footnote on paths. The current default is .agents, but the older .agent is still read for backward compatibility. If both exist in your workspace, it is worth confirming once which one is actually being picked up.

Things that break if ignored, and things that break if forgotten

The question I settled on is a single one.

If breaking it costs you, put it in Rules. If forgetting it costs you, put it in Skills.

The first kind has no sequence to it. "Do not add a new configuration that connects to the production database." "Do not write raw color values." "Never run git push --force — propose it instead." None of these is about when; they are about always. An always-on rule fits.

The second kind is a procedure. Sweep for broken links before delivery, confirm every image carries width and height, write the report to the agreed folder. There is an order, skipping a step hurts, and yet you do not need any of it most of the time. That belongs in a skill.

Some cases genuinely sit between the two. A typographic convention you only want in force while touching CSS, for example. Always on means it rides along on unrelated work; a skill means it may never get invoked.

Glob activation is what stands in the gap here. Scope the rule to *.css and it applies only when a matching file is in play, without adding to what you carry the rest of the time. Once I stopped treating this as a binary between Rules and Skills and started including the activation setting as a third axis, the hesitation dropped away.

Measure what you carry each turn

Understanding this in the abstract is not the same as seeing the numbers. I wrote a small script to look at it.

#!/usr/bin/env bash
# instruction-budget.sh — lists what loads every turn vs. what loads only on demand.
# Usage: ./instruction-budget.sh [workspace root]
set -euo pipefail
ROOT="${1:-.}"
RULES_DIR="$ROOT/.agents/rules"
SKILLS_DIR="$ROOT/.agents/skills"
 
# Fall back to the legacy .agent layout when .agents is absent.
[ -d "$RULES_DIR" ]  || RULES_DIR="$ROOT/.agent/rules"
[ -d "$SKILLS_DIR" ] || SKILLS_DIR="$ROOT/.agent/skills"
 
rules_total=0
echo "== Rules (active ones ride along every turn) =="
while IFS= read -r -d '' f; do
  n=$(wc -m < "$f" | tr -d ' ')
  printf '  %-28s %6s chars\n' "$(basename "$f")" "$n"
  rules_total=$((rules_total + n))
  # 12,000 characters is the per-file cap; anything past it may not be read.
  [ "$n" -gt 12000 ] && echo "     WARNING: over the 12,000-character cap"
done < <(find "$RULES_DIR" -maxdepth 1 -name '*.md' -print0 2>/dev/null | sort -z)
 
meta_total=0
body_total=0
echo "== Skills (only the heading loads up front) =="
while IFS= read -r -d '' f; do
  body=$(wc -m < "$f" | tr -d ' ')
  # The block between the first --- and the next --- is what gets indexed.
  meta=$(awk '/^---[[:space:]]*$/{c++; next} c==1{print} c>1{exit}' "$f" | wc -m | tr -d ' ')
  printf '  %-28s heading %4s chars / body %6s chars\n' \
    "$(basename "$(dirname "$f")")" "$meta" "$body"
  meta_total=$((meta_total + meta))
  body_total=$((body_total + body))
done < <(find "$SKILLS_DIR" -mindepth 2 -maxdepth 2 -name 'SKILL.md' -print0 2>/dev/null | sort -z)
 
echo "---"
echo "Carried every turn : Rules ${rules_total} chars + Skill headings ${meta_total} chars"
echo "Only when invoked  : Skill bodies ${body_total} chars"

Run against a small workspace holding two rule files and two skills, it prints something like this.

== Rules (active ones ride along every turn) ==
  house-style.md                  156 chars
  no-destructive-ops.md           104 chars
== Skills (only the heading loads up front) ==
  imageset-build               heading  101 chars / body    266 chars
  release-check                heading  101 chars / body    313 chars
---
Carried every turn : Rules 260 chars + Skill headings 202 chars
Only when invoked  : Skill bodies 579 chars

At that size the gap is negligible. What matters is not the totals but the split: carried every turn on one line, only when invoked on the other. Add a rule file and the first line grows by the whole thing. Add a skill and it grows only by the heading. That asymmetry compounds as the files grow.

For a long time I put everything into Rules, because I wanted certainty that it would apply. That did not work out well. The conventions rode along on work they had nothing to do with, and the one line I most wanted respected got buried among the rest.

I did try splitting the conventions file itself. What I learned is that splitting reduces some costs and leaves others untouched — I wrote that up with measurements in What actually shrinks when you split rule files with @ imports.

Workflows retire on November 1

There is a second reason to revisit this now. Legacy Workflows are deprecated, and the official documentation states they will be retired on November 1, 2026. They keep working until then, after which the directories are no longer indexed or callable as slash commands.

The migration itself is light. Running the command below in Antigravity 2.0 scans both the global directory (~/.gemini/config/workflows/) and the workspace one (.agents/workflows/).

/migrate-workflows

Each workflow is scaffolded into .agents/skills/<name>/SKILL.md, and the original file is archived with a .bak suffix. If a workflow and a skill share a name, the skill takes precedence.

There is one thing a mechanical migration will drop, though. The description.

Workflows capped out at 12,000 characters, so I had trimmed my descriptions down to make room for the steps. In Skills, that description is exactly what the agent uses to decide. The name and description are all it sees at the start of a conversation, so a thin description means the skill never gets picked up.

After migrating, rewrite them. State what the skill does and when to use it, in the third person, with specifics.

# Rarely picked up
description: Checks the release.
 
# Actually picked up
description: Inspects a site before delivery and lists broken links, missing images, and absent meta tags. Use before handing off a client site or promoting to production.

If you check only one thing right after migrating, check that the .bak files are still there. As long as they are, a rewrite that goes wrong is recoverable.

Three questions when you are stuck

These days I ask myself three things before writing any instruction at all.

  1. Does it have a sequence? If so, it is a skill.
  2. Would breaking it force me to redo work? If so, it is an always-on rule.
  3. Does it only matter while touching a certain kind of file? If so, it is a glob rule.

Looking back, anything that answered no to all three usually did not need writing down anywhere. The same held on the indie developer side of my work, where I run the asset pipeline for my wallpaper apps: notes I added without deciding where they belonged were, six months later, going almost entirely unread.

Start by counting the files in .agents/rules. The script above works, and so does a single wc -m .agents/rules/*.md. Once you can see the numbers, which ones to move into Skills tends to decide itself.

Decide where an instruction lives before writing it. The more instructions accumulate, the more that order seems to pay off. Thank you for reading this far.

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 $15 for lifetime access
View Membership →

If you found this article helpful, a small tip ($1.50) would mean a lot to us. Your support helps keep this site ad-free and covers server and hosting costs.

Related Articles

Antigravity2026-09-02
Why Filename Search Misses Every Error String in Your Repo
Antigravity 2.12 improved partial filename search. Measuring name lookups against full-text search across a 2,088-file repository, the reading cost differed by 67x and error strings hit filenames only 4.5% of the time. Here is where the line actually falls.
Antigravity2026-08-10
Guarding the Turn Boundary in Non-Interactive Runs: A Command Table and a Fail-Closed Gate
A scheduled job meant only to record usage was calling the model on every run. Here is how I split commands into ones that start a turn and ones that do not, built a gate that refuses anything unclassified, and measured it against real scripts.
Antigravity2026-08-02
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.
📚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