Skip to content

Antigravity: Support root envelope field 2 for token counts - #3266

Open
chid wants to merge 2 commits into
steipete:mainfrom
chid:agy-token-fix
Open

Antigravity: Support root envelope field 2 for token counts#3266
chid wants to merge 2 commits into
steipete:mainfrom
chid:agy-token-fix

Conversation

@chid

@chid chid commented Aug 29, 2026

Copy link
Copy Markdown
Contributor

In recent versions of Antigravity, session SQLite databases structure events with outer root envelopes containing:

  • Field 2: Turn coordination and state message.
  • Field 4: Step UUID string identifying the execution step.
  • Field 1: Nested chat turn and usage payload (tokens, model name, cache reads).

Unlike earlier layouts where turn timestamps were always embedded in field 9 (timestamp), newer sessions may omit field 9 from gen_metadata and instead record the timestamp inside the steps table (steps. metadata protobuf: field 1 timestamp, field 12 stepUUID).

Two-Phase Scan Workflow:

  1. Primary Pass (gen_metadata):
    • Streams and validates usage events.
    • If a turn includes an embedded field 9 timestamp, it is converted to an Event immediately.
    • If a turn lacks an embedded timestamp but contains a stepUUID, it is buffered into pendingTimestampRows.
  2. Secondary Pass (steps):
    • If pendingTimestampRows is non-empty and the database contains the steps table, the reader queries steps.metadata only for the required stepUUIDs.
    • Timestamps are matched sequentially per stepUUID to correctly handle multi-turn step reuse across midnight boundaries.
  3. Fail-Closed Guarantees:
    • If any pending rows cannot be resolved from steps, the reader sets coverage = .partial and discards un-timestamped events to prevent date skew or ledger corruption.
    • Budget limits for the secondary step scan operate within independent row/byte limits so that step lookups never exhaust budgets for valid primary usage rows.

Detailed Changes

1. Protobuf Parser (Sources/CodexBarCore/Providers/Antigravity/AntigravityProtoReader.swift)

  • Updated parseTurn to process root message field 2 (traversing into turn coordination payload) and field 4 (storing turn.stepUUID).
  • Added parseStepMetadata to extract (stepUUID: String?, timestampMs: Int64?) from steps.metadata protobuf blobs.
  • Refactored parseTimestampField into a reusable sub-parser for variable-length timestamp records.

2. SQLite Schema & Query Engine (Sources/CodexBarCore/Providers/Antigravity/)

  • AntigravityLocalSQLiteSchema.swift: Replaced hasGenMetadataTable with supportedSQLiteTables, querying sqlite_master in a single pass to check for both gen_metadata and steps.
  • AntigravityLocalSQLite.swift:
    • Added readStepTimestamps helper to stream steps rows, extract timestamps for required UUIDs, and enforce memory/byte constraints.
    • Added appendRecoveredEvents helper to join recovered timestamps with pending turns in first-in-first-out order per UUID.
    • Maintained snapshot transaction safety by finalizing the active gen_metadata cursor before invoking the steps scan.

3. Provider Descriptor (Sources/CodexBarCore/Providers/Antigravity/AntigravityProviderDescriptor.swift)

  • Set supportsCostCommand: true on ProviderCLIConfig.

Testing & Verification

Unit Tests Added (Tests/CodexBarTests/)

  • AntigravityLocalReaderTests.swift:
    • field 2 root envelope with step table timestamps aggregates complete coverage: Validates end-to-end token counting, model breakdown (gemini-3.7-flash), and date attribution via step table timestamps.
    • field 2 root envelope with embedded timestamp in field 9 aggregates complete coverage: Validates fallback to embedded field 9 timestamps when present.
    • field 2 root envelope without timestamp in either gen_metadata or steps fails closed with partial coverage: Ensures unresolvable timestamps mark history coverage incomplete rather than misattributing.
    • field 2 root envelope with reused step UUID consumes step timestamps in order: Validates sequential timestamp assignment when a single step UUID is referenced across midnight day boundaries.
  • AntigravityLocalScanTests.swift:
    • optional steps scan does not exhaust embedded timestamp usage rows: Validates that secondary step scanning respects budget boundaries without dropping legitimate usage records.
  • AntigravityLocalFixture.swift:
    • Added blobWithRootEnvelope(...) and stepMetadataBlob(...) protobuf generators.
    • Added stepBlobs support to fixture database builder.

Automated Test Runs

# 1. Run all Antigravity parser & reader tests
swift test --filter Antigravity
# Result: 33/33 tests passed (0 failures)

### Manual Verification

• Verified CLI command codexbar cost antigravity against local Antigravity session storage.
• Verified daily aggregate token bucketing across mixed legacy and field 2 root envelope sessions.

---

## Review follow-ups (`fcae699b2`)

- **Charge step rows against the shared scan budget** — the secondary `steps` pass now calls
`budget.chargeRow()` / `budget.chargeBytes()`, so it counts against the job-wide 50,000-row and
128 MiB limits instead of only its own `StepScanProgress` ceilings.
- **Reject schema scans that terminate with a SQLite error** — the `sqlite_master` loop distinguishes
`SQLITE_DONE` from error codes and returns no supported tables on an error, so a failed schema scan
can no longer publish rows with complete coverage.
- New test `optional steps scan charges its rows and bytes against the shared job budget` covers the
first fix (it fails on all three expectations without it). The schema guard is not reachable from a
fixture — an oversized `sqlite_master` value fails at `sqlite3_prepare_v2`, which already fails
closed, and forging a bad schema row is blocked by defensive mode — so it ships without a test.

## Real-session verification

Run against real local Antigravity sessions (37 databases in `~/.gemini/antigravity-cli/conversations`,
every one of which carries a `steps` table). Paths and session UUIDs redacted.

$ sqlite3 ~/.gemini/antigravity-cli/conversations/.db ".tables"
battle_mode_infos parent_references trajectory_metadata_blob
executor_metadata steps
gen_metadata trajectory_meta

37/37 databases expose a steps table; e.g. gen_metadata=145 rows / steps=295 rows


### After (this branch) — timestamps recovered from `steps.metadata`

$ codexbar cost --provider antigravity --refresh
Antigravity Cost (API-rate estimate)
Today: — · 162M tokens
Last 30 days: — · 178M tokens
Estimated from local logs · may differ from your bill

$ codexbar cost --provider antigravity --refresh --format json
historyCoverageIsEstablished: true | historyDays: 30
2026-08-27 total= 1,477,948 in= 276,587 out= 0 reasoning= 32,648 cacheRead= 1,168,713 [claude-opus-4-6-thinking]
2026-08-28 total= 14,781,961 in= 2,385,841 out= 25,745 reasoning=143,689 cacheRead= 12,226,686 [gemini-3.7-flash-tiered, claude-opus-4-6-thinking, unknown, gemini-3.7-flash]
2026-08-29 total= 161,748,040 in=10,139,100 out=131,379 reasoning=495,438 cacheRead= 150,982,123 [gemini-3.7-flash, unknown]


### Control — same sessions with the `steps` table dropped

Copies of the same 37 databases with `DROP TABLE steps`, so the secondary pass has nothing to resolve.
This isolates the recovery path: on this data every turn stores its timestamp in `steps.metadata`
rather than `gen_metadata` field 9, so with no `steps` table the reader must fail closed.

$ HOME= codexbar cost --provider antigravity --refresh
Antigravity Cost (API-rate estimate)
Today: —
Last 30 days: —

$ HOME= codexbar cost --provider antigravity --refresh --format json
{"provider":"antigravity","historyCoverageIsEstablished":false,"daily":[],"source":"local", ...}


The unresolved turns are discarded and history coverage stays unestablished — no date skew, no
partially-attributed ledger. With `steps` present the same sessions aggregate to complete coverage.

…d CLI cost command

- Update AntigravityProtoReader to parse protobuf root envelopes starting with field 2 (turn coordination) and field 4 (step UUID).
- Add support for resolving turn timestamps via the steps table (steps.metadata field 1 timestamp and field 12 step UUID) when gen_metadata turns omit embedded field 9 timestamps.
- Update AntigravityLocalSQLiteSchema to discover both gen_metadata and steps tables in a single pass of sqlite_master.
- Enable supportsCostCommand on Antigravity's ProviderCLIConfig in AntigravityProviderDescriptor.
- Add test cases covering root field 2 decoding, steps timestamp joins, and fail-closed validation for unresolvable timestamps.
@clawsweeper

clawsweeper Bot commented Aug 29, 2026

Copy link
Copy Markdown

🦞👀
ClawSweeper picked this up.

Pull request received. I will update this pull request when review starts.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: be9214f72b

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +243 to +247
stepProgress.rows += 1
let count = Int(sqlite3_column_int64(statement, 1))
let attemptedBytes = max(count, payload.byteCount)
guard stepProgress.rows <= stepProgress.rowLimit,
attemptedBytes <= stepProgress.byteLimit - stepProgress.bytes

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Charge step rows against the shared scan budget

When timestamp recovery scans steps, each row and its payload are counted only in StepScanProgress; neither budget.chargeRow() nor budget.chargeBytes() is called. With multiple databases containing missing-timestamp turns, these secondary scans can therefore process far beyond the configured 50,000-row and 128 MiB job-wide limits while statistics.rows and attemptedBytes remain unchanged, defeating the bounded-work guarantees used for hostile or unexpectedly large local databases.

Useful? React with 👍 / 👎.

while true {
try budget.check()
guard sqlite3_step(statement) == SQLITE_ROW else { return false }
guard sqlite3_step(statement) == SQLITE_ROW else { break }

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reject schema scans that terminate with a SQLite error

If sqlite3_step returns SQLITE_CORRUPT, SQLITE_IOERR, or another error after gen_metadata (and possibly steps) has already been observed, this guard treats it identically to SQLITE_DONE and returns the accumulated tables as supported. The reader can then publish rows with complete coverage from a database whose schema scan actually failed; distinguish SQLITE_DONE from all other non-row results and fail closed on the latter.

Useful? React with 👍 / 👎.

@clawsweeper clawsweeper Bot added P2 Normal priority bug or improvement with limited blast radius. rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. labels Aug 29, 2026
@clawsweeper

clawsweeper Bot commented Aug 29, 2026

Copy link
Copy Markdown

Codex review: needs changes before merge. Reviewed August 29, 2026, 7:27 AM ET / 11:27 UTC.

ClawSweeper review

What this changes

This branch recovers Antigravity token-history timestamps from local SQLite steps.metadata when the main event record lacks one, and enables the Antigravity cost command.

Merge readiness

⚠️ Needs maintainer review before merge - 3 items remain

Keep open: the supplied real-session trace now proves the intended recovery path, but the new bounded steps scan can miss needed timestamps behind unrelated rows and discard valid history.

Priority: P2
Reviewed head: fcae699b29cc421549b6b22a284819e2cd09ecdf

Review scores

Measure Result What it means
Overall readiness 🦐 gold shrimp (3/6) The intended behavior has credible real-session proof, but the bounded lookup has a concrete correctness gap before merge.
Proof confidence 🐚 platinum hermit (4/6) Sufficient (terminal): The changed local SQLite reader and protobuf parser feed codexbar cost; the PR body provides redacted real-session terminal output for 37 databases showing established daily totals after recovery and an unestablished fail-closed control after removing steps. It does not cover the separate sparse-row cap finding.
Patch quality 🦐 gold shrimp (3/6) 1 actionable review finding remain.

Verification

Check Result Evidence
Real behavior Verified Sufficient (terminal): The changed local SQLite reader and protobuf parser feed codexbar cost; the PR body provides redacted real-session terminal output for 37 databases showing established daily totals after recovery and an unestablished fail-closed control after removing steps. It does not cover the separate sparse-row cap finding.
Evidence reviewed 5 items Introduced premature lookup cap: The introduced secondary query scans all non-null steps.metadata rows and filters matching UUIDs only after decoding, but limits its traversal to max(needed UUIDs × 32, 256). A needed timestamp after that cap is never considered.
Failure consequence: When the capped scan cannot find every pending timestamp, the introduced recovery path marks the source incomplete and omits the pending events, so valid usage can disappear from the history report.
Real behavior proof supplied: The PR body contains a redacted terminal trace against 37 real local session databases: the branch reports established daily history with steps present, while copies with steps dropped report unestablished history. This directly exercises the local reader through codexbar cost.
Findings 1 actionable finding [P1] Scan beyond unrelated step metadata before failing closed
Security None None.

How this fits together

CodexBar reads local Antigravity session databases, decodes usage protobufs, and produces date-bucketed token totals for the app and CLI. Missing timestamps are now resolved by a second lookup in the same database snapshot.

flowchart LR
A[Antigravity session database] --> B[Usage-row scan]
B --> C[Protobuf usage parser]
C --> D{Timestamp present}
D -->|Yes| E[Token event]
D -->|No| F[Steps metadata scan]
F --> E
E --> G[Cost totals and CLI output]
Loading

Before merge

  • Scan beyond unrelated step metadata before failing closed (P1) - Late finding: this query limits an unfiltered steps table traversal before neededStepUUIDs is applied in Swift. With one pending turn and more than 256 unrelated rows before its metadata, recovery never reaches the match and valid token history is discarded as partial. Continue until the needed occurrence counts are satisfied or the existing shared budget is exhausted, and cover that sparse layout.
  • Resolve merge risk (P1) - Merging as written can cause sessions with a small number of missing timestamps and more than 256 earlier unrelated steps rows to show partial or unavailable token history despite containing the required timestamp records.
  • Complete next step (P2) - A focused mechanical repair can remove the premature lookup cap and add the missing sparse-session regression coverage.

Findings

  • [P1] Scan beyond unrelated step metadata before failing closed — Sources/CodexBarCore/Providers/Antigravity/AntigravityLocalSQLite.swift:228
Agent review details

Security

None.

Review metrics

Metric Value Why it matters
Production versus test delta production +299/-40, tests +163/-1 The seven-file change substantially extends a bounded local-data parser, so its sparse-session behavior needs regression coverage before merge.

Merge-risk options

Maintainer options:

  1. Complete sparse timestamp lookup (recommended)
    Remove the arbitrary secondary-scan cap or make it stop only after all required timestamp occurrences are found while preserving the shared safety budget.
  2. Pause the recovery path
    Do not merge the timestamp recovery until sparse, long-session lookup behavior is defined and covered.
Copy recommended automerge instruction
@clawsweeper automerge

Special instructions:
Scan until each pending step UUID has its required occurrence count or the existing database/job budget is reached; add a regression test with the matching step after more than 256 unrelated rows.

Technical review

Best possible solution:

Continue scanning within the existing job-wide safety budget until all required UUID occurrences are found or the database is exhausted, and add a regression case with the needed step beyond the former cap.

Do we have a high-confidence way to reproduce the issue?

Yes (source-reproducible): one pending usage row plus more than 256 preceding unrelated steps rows makes the introduced query stop before its required UUID and mark valid history partial.

Is this the best way to solve the issue?

No: the two-phase recovery design is appropriate, but its arbitrary scan cap must not prevent locating required metadata that remains within the existing global budget.

Full review comments:

  • [P1] Scan beyond unrelated step metadata before failing closed — Sources/CodexBarCore/Providers/Antigravity/AntigravityLocalSQLite.swift:228
    Late finding: this query limits an unfiltered steps table traversal before neededStepUUIDs is applied in Swift. With one pending turn and more than 256 unrelated rows before its metadata, recovery never reaches the match and valid token history is discarded as partial. Continue until the needed occurrence counts are satisfied or the existing shared budget is exhausted, and cover that sparse layout.
    Confidence: 0.93
    Late finding: first raised on code an earlier review cycle already covered.

Overall correctness: patch is incorrect
Overall confidence: 0.93

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against ce44713574f9.

Labels

Label justifications:

  • P2: This is a normal-priority provider-history correctness repair with limited scope.
  • merge-risk: 🚨 other: The new bounded lookup can suppress valid local usage history when matching step metadata appears after unrelated rows.
  • rating: 🦐 gold shrimp: Overall readiness is 🦐 gold shrimp; proof is 🐚 platinum hermit and patch quality is 🦐 gold shrimp.
  • status: ⏳ waiting on author: ClawSweeper has contributor-facing work open and is waiting for author action. Sufficient (terminal): The changed local SQLite reader and protobuf parser feed codexbar cost; the PR body provides redacted real-session terminal output for 37 databases showing established daily totals after recovery and an unestablished fail-closed control after removing steps. It does not cover the separate sparse-row cap finding.
  • proof: sufficient: Contributor real behavior proof is sufficient. The changed local SQLite reader and protobuf parser feed codexbar cost; the PR body provides redacted real-session terminal output for 37 databases showing established daily totals after recovery and an unestablished fail-closed control after removing steps. It does not cover the separate sparse-row cap finding.

Evidence

Acceptance criteria:

  • [P1] swift test --filter AntigravityLocalReaderTests.
  • [P1] swift test --filter AntigravityLocalScanTests.
  • [P1] make test.
  • [P1] make check.

What I checked:

Likely related people:

  • Yuxin Qiao: Introduced the bounded Antigravity local token-history work that this parser extension modifies. (role: introduced local-history behavior; confidence: high; commits: d94c71acfc00; files: Sources/CodexBarCore/CostUsageFetcher.swift)
  • Peter Steinberger: Recent main-branch history includes ownership of local cost-refresh and cost-cache behavior adjacent to this reader. (role: recent cost-system contributor; confidence: medium; commits: f2b2b5ea14bb, 27a89fb2d518; files: Sources/CodexBarCore/CostUsageFetcher.swift)

Rank-up moves

Optional improvements that raise the rating; they are not merge blockers.

  • Repair the premature secondary-scan cap and add a sparse steps regression test.

Rating scale

Score Internal tier Crab rank Meaning
6/6 S 🦀 challenger crab Exceptional readiness
5/6 A 🦞 diamond lobster Very strong readiness
4/6 B 🐚 platinum hermit Good normal PR; ordinary maintainer review
3/6 C 🦐 gold shrimp Useful, but confidence is limited
2/6 D 🦪 silver shellfish Proof or implementation needs work
1/6 F 🧂 unranked krab Not merge-ready
N/A NA 🌊 off-meta tidepool Rating does not apply

Overall follows the weaker of proof and patch quality.
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

Workflow

  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

History

Review history (1 earlier review cycle)
  • reviewed 2026-08-29T08:33:49.812Z sha fcae699 :: needs real behavior proof before merge. :: none

@clawsweeper clawsweeper Bot added merge-risk: 🚨 other 🚨 Merging this PR has meaningful risk outside the owned taxonomy. proof: sufficient Contributor real behavior proof is sufficient. rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. and removed status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. labels Aug 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

merge-risk: 🚨 other 🚨 Merging this PR has meaningful risk outside the owned taxonomy. P2 Normal priority bug or improvement with limited blast radius. proof: sufficient Contributor real behavior proof is sufficient. rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant