I once asked an agent to reword a single line on a settings screen in one of my wallpaper apps.
It was a handful of words. Yet there was an odd pause before the reply started, and I could see the agent re-reading the structure of files that had nothing to do with the change.
My first instinct was that I had the wrong model, so I switched to a faster one. It felt almost identical.
What actually helped was leaving the model alone and dropping the reasoning effort by one step.
If you only take one thing from this page, take the order of checks: the effort level, where that setting actually applies, and slowness that has nothing to do with reasoning at all. Walk through those three and you usually have your answer before touching the model. Further down there is a small shell function for keeping score. The version I published in August had a hole that broke it on the default macOS shell, so I re-tested and replaced it in November.
Reasoning effort changes how much thinking happens, not how good the answer is
Antigravity lets you set reasoning effort per model. In the editor it sits next to the model picker; in the CLI you change it with the /effort slash command. Sitting there in the settings panel, it reads like a quality dial — turn it up and the answers get smarter.
What it actually controls is how much deliberation happens before you get an answer.
The names of the levels, and which models offer them, change between versions. I write Low / Medium / High here, but for the exact list as of November 2026, please check the official Choose a model page and the /effort command page against the version you have installed.
On work that genuinely needs deliberation, that time is an investment. On work that does not, it is waiting time and credits draining away.
My one-line rewording was firmly in the second category. The agent was dutifully re-verifying its surroundings for a change that had exactly one correct outcome. Nothing was broken except the setting.
Three misreadings I see most often
The first is "High makes the answer correct." Effort buys more deliberation, not better premises. If the premise you handed over is wrong, you get a carefully reasoned answer built on that wrong premise.
The second is "slow means the model is heavy." That was me at the start of this page. A light model at a high effort level still makes you wait, and if the model you switch to is also set high, nothing changes.
The third is "set it once and it applies everywhere." Effort is stored per model, and the editor and the CLI each keep their own value. Changing it in one place does not mean it reached the work that is running right now. The triage table below goes through this.
Assign the three levels by the shape of the task
I do not sort tasks by difficulty. Difficulty collapses into "did this feel hard to me," which moves around depending on the day. I sort by whether the answer is uniquely determined.
| Example task | Level that fits | Why |
|---|---|---|
| Copy and constant replacement, formatting, reordering imports | Low | There is one correct outcome and nothing to weigh |
| Small edits to an existing function, adding tests, fixing a bug you already understand | Medium | Several options exist, but the scope is closed |
| Design forks, bugs you cannot reproduce, migrations spanning many files | High | The work starts by building premises, and the deliberation shapes the result |
One more thing this table encodes: the first row is where most day-to-day requests actually land. Renaming a variable across a file, tidying an import block, replacing a string in three places — that is the bulk of what I hand off in a normal afternoon. If your default sits at High, every one of those small errands carries deliberation it will never use.
The row people hesitate over is always the middle one. The test is whether you can honestly say you already understand the bug. If you cannot, it belongs in the third row.
Compare it with the other levers first
When something feels slow, or the result falls short, effort is not the only thing you can touch. These are the four levers I actually reach for, side by side.
| Lever | Helps when | Does not help when | How easy to undo |
|---|---|---|---|
| Lower the effort | You are waiting on a task with one correct outcome | The task starts by building premises; rework goes up | Back to normal on the next request |
| Raise the effort | The scope is right but shallow deliberation keeps getting rejected | The request is ambiguous; it gets elaborated as-is | Easy to forget, so later small errands stay heavy |
| Switch models | Context size or strengths do not fit the work | The cause is the effort level or slowness outside reasoning | In the CLI, switching can change your default too |
| Rewrite the request | Rejections have a different reason each time; scope is not landing | It is purely a waiting-time problem | Cheapest of the four, no side effects |
My rule in one line: if you are waiting, lower the level; if you are missing, fix the request; raise the level only when you still miss after that. Switching models comes after all three.
I wrote up how a CLI model switch can quietly change your saved default in the one small catch with /model switching.
Start at Medium and look for a reason to move
Pinning everything to High means giving up your own headroom before you start. I keep Medium as the default and move only when I find a reason.
As an indie developer, every minute of waiting comes straight out of my own working time. With no colleague reviewing alongside me, whether I can place the next decision the moment my hands are free is what sets the pace of the day.
The clearest reason to go down is that the waiting is breaking the rhythm of the work. If the request fits in a single sentence, Low is usually enough.
The reason to go up is two rejected attempts in a row. The first miss might be my writing. Two in a row suggests the deliberation, not my phrasing, is what ran short.
Even then, I try rewriting the instruction first. Raising the effort does not make an ambiguous request unambiguous.
Triage table: when changing the level makes no difference
If you lower the level and nothing changes, the cause is somewhere else. These are in the order I check them.
| Symptom | Likely cause | How to check | Fix |
|---|---|---|---|
| You lowered it, but the wait is the same | You switched models mid-session and another model's value took over | Compare the model currently selected with that model's effort level | Lower the level on the model you are actually using |
| Light in the editor, heavy only for work launched from a terminal | The editor and the CLI keep separate settings | Open /effort in the CLI and look at the current value | Set the level on the CLI side; if that gets tedious, make routing automatic |
| Heavy right after opening, regardless of level | The workspace is still indexing, or you opened a conversation with a huge output in it | Send the same request in a fresh conversation and compare | Let indexing finish; split long conversations |
| In the log, High failures cluster on one task | The request or the context is short, not the effort | Re-read that request and check it states scope and a done condition | Add one sentence each for scope and done condition before raising the level |
For the editor-versus-CLI row, I logged how routing by task class played out in six weeks of /effort routing. For slowness outside reasoning, the order in what to check when Antigravity feels slow separates those cases.
If none of the rows explains it, stop guessing and make the comparison deliberate. Pick one small, repeatable errand — replacing a fixed string, say — and run it twice in a row, changing only the level between the two runs. Two back-to-back runs of the same request tell you more than a week of impressions gathered across different tasks, because everything else is held still.
Correct your judgment with records, not memory
The feeling that "Medium was enough for this one" is completely gone a week later. So I wrap the command in a small function and keep the evidence.
Two holes in the first version
The version I published here in August was written in bash and only ever tested in bash. When I sourced it from zsh in November, it stopped before recording a single row. zsh is the default shell on macOS, so plenty of readers likely hit the same wall.
| Hole | What happened (reproduced locally) | Cause |
|---|---|---|
| Broken under zsh | It prints agy_run:4: read-only variable: status and stops before the wrapped command runs | In zsh, status is a read-only variable holding the last exit code, so local status cannot be declared |
| A tab in the label shifts the columns | Logging a label that contains a tab produces six columns; the summary then reads the duration column as the exit code and counts a successful run as "failed 1" | The label was written out with the same tab used as the field separator |
The first one was serious. Because the function stops partway, you lose not just the log but the wrapped agy call itself. I had tested it only in bash and written "this works" — that was my own gap in checking.
Since then, I source any shell function I publish in the shell my readers are most likely to use before it goes out.
The re-tested version
It now rejects misspelled levels (hgih and friends) at the door, lowercases Medium, and turns tabs and newlines in the label into spaces. The exit-code variable is now rc.
# put this in ~/.zshrc or ~/.bashrc (works in both zsh and bash)
agy_run() {
if [ "$#" -lt 3 ]; then
echo "usage: agy_run <low|medium|high> <label> <command...>" >&2
return 2
fi
local effort label t0 t1 rc
effort=$(printf '%s' "$1" | tr '[:upper:]' '[:lower:]')
case "$effort" in
low|medium|high) ;;
*) echo "agy_run: level must be low, medium, or high (got: $1)" >&2
return 2 ;;
esac
label=$(printf '%s' "$2" | tr '\t\n' ' ')
shift 2
t0=$(date +%s)
"$@"; rc=$?
t1=$(date +%s)
printf '%s\t%s\t%s\t%s\t%s\n' \
"$(date +%Y-%m-%dT%H:%M:%S)" "$effort" "$label" "$((t1 - t0))" "$rc" \
>> "${AGY_EFFORT_LOG:-$HOME/effort-log.tsv}"
return "$rc"
}To use it, wrap the call and add the level plus a label for the task. The example uses -p, which sends a single request non-interactively.
agy_run medium "typo-fix" agy -p "reword the settings screen copy"The medium here is a label for the log. The function does not change the effort level for you; set the real level with /effort in the CLI first. If the label and the actual setting drift apart, the log stops meaning anything, so the one rule I keep is: when I change the level, I write the same word in the label.
One more caution. There is a report on the official repository, issue #76, that agy -p output goes missing when stdout is a pipe or a redirect. The function itself leaves output alone, but if you capture it from the outside, as in agy_run ... | tee, please check that it behaves on your version.
Looking at it by level
After a week or so, summarize by level. I also list the labels of tasks that failed at High.
awk -F'\t' '
NF != 5 { bad++; next }
{ n[$2]++; s[$2] += $4
if ($5 != 0) { f[$2]++; if ($2 == "high") hf[$3]++ } }
END {
split("low medium high", order, " ")
for (i = 1; i <= 3; i++) { k = order[i]; if (!(k in n)) continue
printf "%-7s %3d runs avg %5.1fs failed %d\n", k, n[k], s[k] / n[k], f[k] + 0 }
for (l in hf) printf " failed at high: %s x%d\n", l, hf[l]
if (bad) printf " rows with the wrong column count: %d (skipped)\n", bad
}' "${AGY_EFFORT_LOG:-$HOME/effort-log.tsv}"I checked it on Ubuntu 24.04, sourcing it from both zsh 5.9 and bash 5.2. With sleep and false standing in for agy, I ran five cases — a success with a tab in the label and the level written as Low, another success, a failure, a misspelled level, and missing arguments — then appended one malformed row like the old version used to write. The summary came out like this.
low 1 runs avg 1.0s failed 0
medium 1 runs avg 2.0s failed 0
high 1 runs avg 0.0s failed 1
failed at high: migrate-billing x1
rows with the wrong column count: 1 (skipped)
The misspelled level and the missing arguments are not logged and return exit code 2. The tabbed label was turned into a space and counted as a success.
The number to care about is not the average duration. It is the rows that failed while set to High. Those are the tasks reasoning effort did not solve, which means the next thing to fix is the instruction or the context you handed over.
Once I started keeping this log, I noticed I had been translating "this looks hard" into High without thinking. In practice, the ambiguity of my request mattered far more than the level did.
The next place you will get stuck
After two weeks of logging, I suspect the next annoyance will be typing the label by hand every time. That is exactly where I stalled too. A small router that picks the level from the wording of the request, plus six weeks of numbers, lives in the /effort routing write-up mentioned above.
The other one is cost. Raising effort increases not only the wait but also the tokens spent on thinking. If you run Gemini 4 Carbon day to day, I walked through estimating the cost of a single session from thinking level and cache rate in estimating a Carbon session's cost. Doing that arithmetic once before you raise the level gives the decision some weight.
What I run today
Medium is my default. I keep exactly three cases reserved for Low: copy replacement, formatting, and reordering dependencies. High comes out a few times a week, for migrations or bugs I cannot yet reproduce.
I am not claiming this split is correct. It should depend on the kind of code you work with, and I keep nudging mine as the log tells me things.
If you try one thing, recall the last task you handed to an agent and find its row in the first table. If a first-row task was running at High, that is the easiest waiting time you will ever get back.
Thank you for reading.