ANTIGRAVITY LABJP
Articles/Tips & Best Practices
Tips & Best Practices/2026-09-07Beginner

When the few lines you quoted stop making sense in the next prompt

Antigravity 2.12.0 lets you quote part of a response into your next prompt, but the definitions those lines depend on stay behind. Here is a small script that counts what got left out, plus the widths I actually measured.

Antigravity365context management3prompt design2local LLM15

The first night I used quoting in Antigravity 2.12.0, I picked four lines of a function out of a long response and handed them to the next prompt. I thought I had trimmed the context nicely.

What came back was a fix built around a constant I had never sent — invented, plausibly named, and wrong. I ran the same exchange three times before my hands stopped.

The cause was not subtle. The names those four lines relied on had their definitions outside the quote.

What I want to say first is this: the thing to trim when you quote is not length.

What quoting carries, and what it leaves behind

Quoting, added in 2.12.0, lets you highlight part of a response and pass it directly as context for your next prompt. Before that, the choice was copying by hand or referencing the whole response. When only one passage matters, neither option sits well.

In practice, though, what the feature carries is a run of consecutive lines. The definitions those lines lean on do not come along.

Here is what I quoted first:

def run_agent(settings):
    timeout = settings.get("timeout", AGENT_TIMEOUT_MS)
    budget = min(timeout, AGENT_TIMEOUT_MS)
    return dispatch(budget)

It looks self-contained. But neither the value of AGENT_TIMEOUT_MS nor what dispatch returns appears anywhere in those four lines. A human reader fills the gap with "that was defined further up." The next prompt receives only the selection.

If you pair Antigravity with a local model, this bites harder. Trimming the range to fit a narrow context window is exactly when you drop a dependency you needed.

Counting what got left behind, before you send

To stop repeating the mistake, I wrote a small script that runs the moment I have picked a range. Save the response as text, hand it the line range, and it lists the names that range touches whose definitions sit outside it.

#!/usr/bin/env python3
"""Find names whose definitions get left behind when you quote part of a
   response into your next prompt.
   Usage: quote_check.py <response.md> <start> <end> ["follow-up text"]
   Exit code: 0 = nothing left behind / 1 = something left behind
"""
import re
import sys
 
# Lines we treat as definitions (Python / JS / TS / constants)
DEF_PATTERNS = [
    re.compile(r"^\s*(?:async\s+)?def\s+([A-Za-z_]\w*)"),
    re.compile(r"^\s*class\s+([A-Za-z_]\w*)"),
    re.compile(r"^\s*(?:export\s+)?(?:const|let|var|function)\s+([A-Za-z_]\w*)"),
    re.compile(r"^\s*([A-Z][A-Z0-9_]{2,})\s*[:=]"),
]
BACKTICK = re.compile(r"`([A-Za-z_][\w.]*)`")
BARE = re.compile(r"\b([A-Za-z_]\w{2,})\b")
 
STOPWORDS = {
    "def", "class", "return", "import", "from", "for", "if", "else", "elif",
    "None", "True", "False", "self", "and", "not", "get", "min", "max", "len",
    "sorted", "list", "dict", "str", "int", "encoding", "utf",
}
 
 
def read_lines(path):
    with open(path, encoding="utf-8") as fh:
        return fh.read().splitlines()
 
 
def collect_defs(lines, offset=0):
    """Collect defined names and the line each one appears on."""
    found = {}
    for i, line in enumerate(lines, start=offset + 1):
        for pat in DEF_PATTERNS:
            m = pat.match(line)
            if m and m.group(1) not in found:
                found[m.group(1)] = i
    return found
 
 
def collect_refs(text, known):
    """Keep only names this text touches that are defined somewhere in the response."""
    hits = set()
    for tok in BACKTICK.findall(text):
        hits.add(tok.split(".")[0])
    for tok in BARE.findall(text):
        if tok not in STOPWORDS:
            hits.add(tok)
    return {t for t in hits if t in known}
 
 
def report(path, start, end, followup=""):
    lines = read_lines(path)
    if not 1 <= start <= end <= len(lines):
        print(f"Invalid range (use 1..{len(lines)})")
        return 2
 
    quoted_lines = lines[start - 1:end]
    quoted = "\n".join(quoted_lines)
    whole = "\n".join(lines)
 
    all_defs = collect_defs(lines)
    in_quote = collect_defs(quoted_lines, offset=start - 1)
    referenced = collect_refs(quoted + "\n" + followup, all_defs)
 
    dangling = sorted(t for t in referenced if t not in in_quote)
 
    ratio = len(quoted) * 100 // max(len(whole), 1)
    print(f"whole response    : {len(whole)} chars / {len(lines)} lines")
    print(f"quote L{start}-L{end}      : {len(quoted)} chars / {end - start + 1} lines ({ratio}% of whole)")
    print(f"names touched     : {len(referenced)}")
    print(f"defined in quote  : {len(in_quote)}")
    if dangling:
        print(f"defined outside   : {len(dangling)}")
        for tok in dangling:
            print(f"   - {tok} (defined at L{all_defs[tok]})")
        lo = min(start, min(all_defs[t] for t in dangling))
        hi = max(end, max(all_defs[t] for t in dangling))
        print(f"-> widening to L{lo}-L{hi} clears this")
        return 1
    print("defined outside   : 0")
    return 0
 
 
if __name__ == "__main__":
    if len(sys.argv) < 4:
        print(__doc__)
        sys.exit(2)
    tail = sys.argv[4] if len(sys.argv) > 4 else ""
    sys.exit(report(sys.argv[1], int(sys.argv[2]), int(sys.argv[3]), tail))

Two notes on why it is written this way.

First, reference extraction does not rely on backtick notation alone. Prose in a response wraps dispatch in backticks, but the code body uses it bare. Watching only the wrapped form skips exactly the dependency you most wanted to catch.

Second, the check is limited to names defined somewhere in the same response. Pull in every library function and the output becomes unreadable. What matters here is only what was present in the response and missing from the quote.

Four quoted lines, two definitions left behind

I prepared a 45-line, 1,039-character response and ran the check on the same four lines of run_agent I had quoted originally.

$ python3 quote_check.py response.md 33 36 "fix how budget is chosen"
whole response    : 1039 chars / 45 lines
quote L33-L36      : 152 chars / 4 lines (14% of whole)
names touched     : 3
defined in quote  : 1
defined outside   : 2
   - AGENT_TIMEOUT_MS (defined at L6)
   - dispatch (defined at L38)
-> widening to L6-L38 clears this
$ echo $?
1

I had trimmed to 14% of the response, and two of the three names in that slice had their definitions left behind. The answer missed not because the model was weak, but because what I handed over was incomplete.

Widening once was not enough

I assumed that obeying the suggestion and jumping to L6-L38 would settle it. It did not.

$ python3 quote_check.py response.md 6 38 "fix how budget is chosen"
quote L6-L38      : 807 chars / 33 lines (77% of whole)
names touched     : 8
defined in quote  : 7
defined outside   : 1
   - CONFIG_ROOT (defined at L5)
-> widening to L5-L38 clears this

Widening pulls in new lines, and those new lines lean on names of their own. Dependencies form a chain, and one pull does not reach the end. That part genuinely surprised me.

So I added a short wrapper that widens until the check comes back clean.

#!/bin/bash
# Usage: widen.sh <response.md> <start> <end> "<follow-up text>"
FILE="$1"; S="$2"; E="$3"; MSG="$4"
for i in 1 2 3 4 5; do
  OUT=$(python3 quote_check.py "$FILE" "$S" "$E" "$MSG")
  if ! grep -q 'defined outside   : [1-9]' <<< "$OUT"; then
    echo "converged on pass $i: L$S-L$E"
    grep -E '^quote L' <<< "$OUT"
    exit 0
  fi
  RANGE=$(grep -o 'L[0-9]*-L[0-9]*' <<< "$OUT" | tail -1)
  S="${RANGE%-*}"; S="${S#L}"
  E="${RANGE#*-}"; E="${E#L}"
  echo "pass $i: widening to L$S-L$E"
done
echo "did not converge in 5 passes"; exit 1

Running it gave this:

$ ./widen.sh response.md 33 36 "fix how budget is chosen"
pass 1: widening to L6-L38
pass 2: widening to L5-L38
converged on pass 3: L5-L38
quote L5-L38      : 838 chars / 34 lines (80% of whole)

A quote that should have been 14% ended up at 80%. On the numbers that looks like quoting lost its point, and yet I was relieved. I now knew in advance that this passage needs 80% to make sense at all.

Passages that suit quoting, and passages that do not

Within the same response, different ranges behaved nothing alike. The section handling the scan depth cutoff converged on a single pass.

Quoted passageStarting rangeAfter convergenceWidening passes
Timeout decision (run_agent)4 lines, 14% of whole34 lines, 80% of whole2
Scan cutoff (walk_guarded)7 lines, 19% of whole8 lines, 21% of whole1
Whole response as-is45 lines, 100%45 lines, 100%0

One passage was fine at 21%; the other demanded 80%. The gap has nothing to do with line count or complexity — it tracks how much the passage leans on names defined elsewhere.

What you trim by quoting is not length. It is everything outside the dependency.

Standing on that line makes the call easy. A passage that widens to 80% was never a good candidate for quoting; referencing the whole response, or first extracting that part into its own file, gets you there faster. A passage that settles at 21% can be quoted without a second thought.

Quoting and whole-response referencing are not competing options. I keep both and choose by the converged ratio.

When the goal is reading part of a file rather than part of a response, the page range and resolution floor in view_file is the better instrument. And what actually shrinks when you split rule files with @path/to/file is something I wrote down in what splitting rules actually shrinks.

What I do now

There is only one step. Once you have selected a range, run quote_check.py before you hit send. If nothing was left behind, pass it along as-is.

I still leave something behind on roughly one attempt in three. Even so, noticing it before sending rather than after reading the reply saves an amount of time that is hard to overstate.

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

Tips2026-08-30
Before You Narrow the Page Range in Antigravity 2.11.0, Find Your Resolution Floor
I measured the new page range and MediaResolution options in view_file against a 240-page document. Resolution moved the numbers more than the page range did, and setting it too low drops words without raising a single error.
Tips2026-08-25
What I Line Up in PowerShell Before Letting Antigravity Agents Run Commands on Windows
Antigravity agents tend to write bash-shaped commands, and on Windows those do not survive the trip. I ran PowerShell 7.6.5 locally and measured four things: chaining operators, aliases, exit codes, and output encoding. Here is where Windows PowerShell 5.1 diverges, and what belongs in a rules file.
Tips2026-08-24
Four Reasons Your .antigravityignore Rules Are Not Taking Effect
Rules that quietly do nothing, and rules that swallow the whole repository. Here are four causes behind both, each checked one at a time against a gitignore-style matcher.
📚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