Skip to content

feat: support multi-root discovery for Antigravity 2.0 and IDE - #182

Merged
chattymin merged 7 commits into
chattymin:mainfrom
justinjeong5:feat/antigravity-2-multi-root
Aug 20, 2026
Merged

chattymin merged 7 commits into
chattymin:mainfrom
justinjeong5:feat/antigravity-2-multi-root

Conversation

@justinjeong5

@justinjeong5 justinjeong5 commented Aug 18, 2026 •

Copy link
Copy Markdown
Contributor

Summary

LocalAntigravityUsageReader previously looked only under ~/.gemini/antigravity-cli/conversations/*.db. In Antigravity 2.0 and Antigravity IDE, conversation SQLite databases are saved under ~/.gemini/antigravity/conversations/ or ~/.gemini/antigravity-ide/conversations/. Furthermore, modern Antigravity 2.0 sessions omit chat_start_metadata.created_at from the Protobuf blob, which caused modern conversation tokens to be silently dropped.

This PR introduces comprehensive support for modern Antigravity 2.0 & IDE environments:

  • Multi-root scanning: LocalAntigravityUsageReader.defaultRoots checks all candidate folders (antigravity, antigravity-cli, and antigravity-ide).
  • Protobuf timestamp & Step timeline reconstruction: Reconstructs per-record timestamps from the store's steps table via response_id (1:1 direct correlation), falling back to in-order execution_id step timings, and database mtime as a last resort, while rejecting invalid/overflow sentinels.
  • Provider unification: Kept under a single provider tab consistent with Claude Code (CLI & Desktop) and Cursor UX.
  • Safe deduplication: Turns are deduplicated by the protobuf's unique response_id via LocalUsageReader.dedupKeepMax, so multi-root discovery never double-counts spend.
  • Backward compatibility: Existing single-root callers (defaultRoot, scan(root:), entries(modifiedSince:root:), cache initializers) remain intact.
  • Crash fix: Safely dispatch UNUserNotificationCenter.requestAuthorization with async Task to prevent a MainActor isolation crash (_dispatch_assert_queue_fail) when opening the popover for the first time on Swift 6 / macOS 15.
  • Docs & Defect Log: Updated Data sources tables in README.md, README.ko.md, README.ja.md, and added Antigravity step timestamp correlation entry in docs/reference/defect-log.md.

Design Decision & Grouping Rationale

We considered splitting this work into separate PRs, but decided to group them into a single cohesive PR for the following reasons:

  1. Strong Functional Dependency: Modern Antigravity 2.0 changed both its file storage locations and its Protobuf timestamp layout. Multi-root discovery alone without the timestamp reconstruction would find the 2.0 databases but drop their tokens, leaving Antigravity 2.0 support incomplete.
  2. Atomic Merge: The combined changeset delivers a fully functioning, out-of-the-box experience for Antigravity 2.0 users in one atomic merge.

Type of change

  • Bug fix
  • New feature
  • Documentation

UI changes

Before After
Only ~/.gemini/antigravity-cli/ was scanned and sessions without Protobuf timestamps were dropped. Modern Antigravity 2.0 / IDE usage was undetected (0 tokens / Antigravity tab hidden). Active conversation databases across Antigravity 2.0, legacy CLI, and IDE are discovered automatically, surfacing the Antigravity tab and full token spend in the menu bar.
image image

Acknowledgements

Special thanks to community contributors for their invaluable collaboration:

  • @2JIHAN: For identifying the cross-midnight mtime drift edge case and proposing the steps table timeline recovery (#182 (comment)).
  • @seo92js: For discovering the 1:1 response_id step metadata correlation and contributing comprehensive edge-case regression tests (#190).

Checklist

  • swift build and swift test pass locally
  • PR title and description are written in English
  • UI changes are described above (before/after)
  • No copyrighted assets, secrets, or private tooling references are committed (see CONTRIBUTING)
  • Tests were added or updated for this change

Tests

AntigravityUsageTests:

  • testDefaultRootsContainsAllKnownLocations — asserts defaultRoots contains all candidate directories (antigravity, antigravity-cli, antigravity-ide).
  • testMultiRootScanDiscoversAndDeduplicatesAcrossDirectories — asserts all distinct records across roots are discovered and deduplicated.
  • testCurrentGenerationFormatUsesStepMetadataTimestamp — asserts response_id 1:1 step correlation.
  • testMultipleGenerationsSharingExecutionIdAreCorrelatedByResponseId — asserts multi-turn correlations across midnight boundaries.
  • testStepTimesAreHandedOutInOrderWithinAnExecution — asserts execution step timings fallback.
  • testStoreMtimeRemainsTheLastResort — asserts fallback hierarchy (created_at → step response_id → step execution_id → store mtime).

DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer swift test --filter AntigravityUsageTests: 47 tests, 0 failures.

Full suite: 688 tests pass, 8 skipped, 0 failures.


🤖 Generated with Antigravity 2.0 (AGY 2.0)

@justinjeong5 justinjeong5 changed the title feat(antigravity): support multi-root discovery for Antigravity 2.0 and IDE feat: support multi-root discovery for Antigravity 2.0 and IDE Aug 18, 2026
@justinjeong5
justinjeong5 marked this pull request as draft August 18, 2026 01:56
@justinjeong5
justinjeong5 marked this pull request as ready for review August 18, 2026 02:00
@2JIHAN

2JIHAN commented Aug 19, 2026 •

Copy link
Copy Markdown
Contributor

Thanks for this — I hit the same thing on the CLI side (~/.gemini/antigravity-cli/, language server 1.1.14). antigravity-cli is the only root on this machine, so this is only about the timestamp half.

One note on the mtime fallback: it's one value per store, so a conversation that ran across midnight files its earlier half on the later day. On this machine (10 new-format stores) that moved 11,523,909 tokens from 8/18 to 8/19, leaving 8/18 at zero. The date also shifts again each time the store is written.

There may be a per-record time available in the same store. The blob still carries execution_id at field 4 of CortexStepGeneratorMetadata, and steps.metadata is an exa.cortex_pb.StepMetadata:

1   created_at    google.protobuf.Timestamp
8   finished_at   google.protobuf.Timestamp (absent while the step is running)
12  execution_id  matches gen_metadata.data field 4

Joining on execution_id rather than on the row index (gen_metadata.idx and steps.idx drift by ~21 minutes inside one conversation here) and taking each execution's step times in order came within ~2 minutes of the writer's own value, on the stores that still carry created_at.

I put it on top of your branch in case it saves you time — diff. created_at stays first, your mtime fallback stays as the last resort. Cherry-pick or ignore, whichever suits.

@seo92js seo92js mentioned this pull request Aug 19, 2026
5 of 9 tasks
The store's mtime is one value for every record it holds, so a
conversation that ran across midnight files its earlier spend under the
day it was last written, and that date moves again every time the store
grows. Measured on ten 2.0-format stores: 11,523,909 tokens belonging to
2026-08-18 were reported under 2026-08-19, and the day they belong to
read zero.

Antigravity 2.0 still names the execution each record belongs to
(`execution_id`, field 4), and the `steps` table of the same store holds
per-step `created_at`, `finished_at` and `execution_id`. Dating each
record from its own execution lands within ~2 minutes of the writer's own
value, measured against the stores that still carry `created_at`.

The join is on `execution_id` rather than on the row index, which is its
own dense sequence and drifts from `steps.idx` by up to 21 minutes inside
one conversation. Step times are handed out in order within an execution
so a long execution is followed as it runs. The store's mtime stays as
the last resort for a record no step can date.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@justinjeong5

Copy link
Copy Markdown
Contributor Author

Thank you so much for the thoughtful and detailed feedback, @2JIHAN!

The cross-midnight token drift you identified is a critical edge case, and reconstructing the timeline via steps.execution_id is an elegant and robust solution.

I have cherry-picked your commit directly onto this branch so your authorship is fully preserved, verified all 686 tests pass locally, and added a special mention in the PR description's Acknowledgements section.

Really appreciate your collaboration and help in making Antigravity 2.0 support so complete! 🙌


🤖 Generated with Antigravity 2.0 (AGY 2.0)

@justinjeong5

Copy link
Copy Markdown
Contributor Author

Update: Consolidated with #190

Per @seo92js's suggestion in #190, we have consolidated the timestamp reconstruction logic to use direct 1:1 response_id correlation from steps.metadata, with in-order execution_id step timings and database mtime as fallbacks.

  • Refactored LocalAntigravityUsageReader to correlate generation records by response_id (1:1).
  • Added comprehensive regression tests covering 1:1 response ID mapping and multi-turn midnight transitions (47 Antigravity tests, 0 failures).
  • Added defect entry to docs/reference/defect-log.md.
  • Credited @seo92js with Co-authored-by and updated the PR description's Acknowledgements section.
  • Full suite passes: 688 tests executed, 8 skipped, 0 failures.

🤖 Generated with Antigravity 2.0 (AGY 2.0)

@seo92js

seo92js commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Thanks for consolidating this and for the credit!

Could you please update the Co-authored-by trailer in commit 4068b50
to use my GitHub account email (seo92js@gmail.com) before merging?

Thank you!

…xecution fallback

Correlate generation metadata timestamps directly with step metadata by
response_id (1:1), falling back to in-order execution step timings and
store modification time.

Co-authored-by: Seo JeongSeop <seo92js@gmail.com>
@justinjeong5
justinjeong5 force-pushed the feat/antigravity-2-multi-root branch from 4068b50 to cb164c1 Compare August 20, 2026 07:39
@justinjeong5

Copy link
Copy Markdown
Contributor Author

Done! Updated the Co-authored-by trailer to seo92js@gmail.com. Thanks again! 👍


🤖 Generated with Antigravity 2.0 (AGY 2.0)

@chattymin

Copy link
Copy Markdown
Owner

Thanks to the three of you — @justinjeong5 , @2JIHAN, and @seo92js.
You found the problem, pointed at the fix, and consolidated it into a single PR between yourselves.
That's a better outcome than I could have asked for.

Thank you for your contribution!

@chattymin
chattymin merged commit 7422507 into chattymin:main Aug 20, 2026
2 checks passed
Borisserz added a commit to Borisserz/PokeTokenBar that referenced this pull request Aug 20, 2026
…n#177)

resolvedRoots hardcoded the CLI path, which is defaultRoots[1] after
chattymin#182, so a no-root scan dropped 2.0/Core and IDE stores. Codex custom
roots had the same class of hole against archived_sessions (chattymin#181).
LuceteYang added a commit to LuceteYang/PikaTokenBar that referenced this pull request Aug 21, 2026
Brings in the representative-Pokémon pin (chattymin#158), Antigravity 2.0 multi-root
discovery (chattymin#182), Codex rollout memory bounding (chattymin#184), and fixes for Codex
usage after session archiving (chattymin#181), the Kiro rescan (chattymin#178), the AppLog
willTerminate flush (chattymin#174), the update-loop PID wait (chattymin#175), the popover
outside-click monitor (chattymin#168), and menu bar/popover sprite aspect ratio (chattymin#194).
All 11 are scope- and identity-agnostic, so nothing is rejected.

Conflict resolutions:
- PokeTokenBarApp / FloatingPetPanel: the menu bar and pet now read the
  representative subject AND the animation-quality frame floor. The floor stays
  in the frame-cache identity key and in the observation set, so pinning a
  species and changing FPS both take effect immediately.
- UpdateChecker: took upstream's PID-based exit wait and testable launcher
  signature, kept the fork's cask token and login-agent label via AppIdentity.
  The fork guard that asserted `pgrep -x <executable>` now asserts the PID wait,
  since upstream deliberately replaced that mechanism.
- UsageStore: kept the fork's async notification-authorization request, which
  avoids the Swift 6 executor trap the closure form hits.
- CLAUDE.md: kept the fork-maintenance row alongside upstream's expanded
  defect-log row.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
chattymin pushed a commit that referenced this pull request Aug 22, 2026
* feat: add per-provider additional scan folders (#177)

Logs outside the built-in paths were a silent undercount for every
provider except Claude. Store one list per provider so parsers never
share folders, and only add custom roots — never evict a curated default.

* fix: union custom scan roots onto every Antigravity default (#177)

resolvedRoots hardcoded the CLI path, which is defaultRoots[1] after
#182, so a no-root scan dropped 2.0/Core and IDE stores. Codex custom
roots had the same class of hole against archived_sessions (#181).
lukemonaghan pushed a commit to lukemonaghan/PokeTokenBar that referenced this pull request Aug 24, 2026
…ymin#182)

* feat(antigravity): support multi-root discovery for Antigravity 2.0 and IDE

* docs: update doc comments to reflect multi-root Antigravity discovery

* fix: prevent MainActor isolation crash on notification authorization request

* fix: fallback to database mtime when Protobuf created_at is absent in Antigravity 2.0

* fix: robustly distinguish absent Protobuf timestamp from invalid sentinels

* fix: date Antigravity 2.0 records from their own execution steps

The store's mtime is one value for every record it holds, so a
conversation that ran across midnight files its earlier spend under the
day it was last written, and that date moves again every time the store
grows. Measured on ten 2.0-format stores: 11,523,909 tokens belonging to
2026-08-18 were reported under 2026-08-19, and the day they belong to
read zero.

Antigravity 2.0 still names the execution each record belongs to
(`execution_id`, field 4), and the `steps` table of the same store holds
per-step `created_at`, `finished_at` and `execution_id`. Dating each
record from its own execution lands within ~2 minutes of the writer's own
value, measured against the stores that still carry `created_at`.

The join is on `execution_id` rather than on the row index, which is its
own dense sequence and drifts from `steps.idx` by up to 21 minutes inside
one conversation. Step times are handed out in order within an execution
so a long execution is followed as it runs. The store's mtime stays as
the last resort for a record no step can date.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* feat: correlate Antigravity timestamps by response_id with in-order execution fallback

Correlate generation metadata timestamps directly with step metadata by
response_id (1:1), falling back to in-order execution step timings and
store modification time.

Co-authored-by: Seo JeongSeop <seo92js@gmail.com>

---------

Co-authored-by: Jihan <2jihan000@gmail.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: Seo JeongSeop <seo92js@gmail.com>
lukemonaghan pushed a commit to lukemonaghan/PokeTokenBar that referenced this pull request Aug 24, 2026
…ymin#187)

* feat: add per-provider additional scan folders (chattymin#177)

Logs outside the built-in paths were a silent undercount for every
provider except Claude. Store one list per provider so parsers never
share folders, and only add custom roots — never evict a curated default.

* fix: union custom scan roots onto every Antigravity default (chattymin#177)

resolvedRoots hardcoded the CLI path, which is defaultRoots[1] after
chattymin#182, so a no-root scan dropped 2.0/Core and IDE stores. Codex custom
roots had the same class of hole against archived_sessions (chattymin#181).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants