Skip to content

fix(engram): prevent duplicate Antigravity MCP registration#1561

Closed
dnlrsls wants to merge 1 commit into
Gentleman-Programming:mainfrom
dnlrsls:fix/797-linuxbrew-antigravity-mcp
Closed

fix(engram): prevent duplicate Antigravity MCP registration#1561
dnlrsls wants to merge 1 commit into
Gentleman-Programming:mainfrom
dnlrsls:fix/797-linuxbrew-antigravity-mcp

Conversation

@dnlrsls

@dnlrsls dnlrsls commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

🔗 Linked Issue

Closes #797


🏷️ PR Type

  • type:bug — Bug fix (non-breaking change that fixes an issue)
  • type:feature — New feature (non-breaking change that adds functionality)
  • type:docs — Documentation only
  • type:refactor — Code refactoring (no functional changes)
  • type:chore — Build, CI, or tooling changes
  • type:breaking-change — Breaking change (fix or feature that changes existing behavior)

📝 Summary

  • Makes the Antigravity plugin the sole owner of Engram MCP registration in the selected CLI or Desktop variant.
  • Transactionally migrates legacy global registration, preserves unrelated configuration, and activates the plugin only when its manifest is semantically valid.
  • Canonicalizes stable Engram commands and mcp --tools=agent, with recoverable rollback or fix-forward handling for ambiguous write failures.

📂 Changes

File / Area What Changed
internal/components/engram/inject.go Selects the active CLI/Desktop variant, stages plugin MCP/hooks/settings before migrating the global registration, semantically activates the manifest last, and reconciles failed atomic writes through rollback or plugin-only fix-forward convergence.
internal/components/engram/inject_test.go Covers variant selection, lossless migration, stable command selection, idempotence, invalid input, pre/post-replacement failures, reread ambiguity, manifest semantics, rollback, and fix-forward recovery.
internal/components/golden_test.go Reads the Antigravity MCP golden from the plugin-owned configuration.
testdata/golden/engram-antigravity-mcp.golden Records the canonical mcp --tools=agent contract.

🧪 Test Plan

Focused unit tests

go test ./internal/components/engram
go test ./internal/components
  • Focused Engram package tests passed
  • Focused components package tests passed
  • Go source was formatted
  • Full unit suite (go test ./...) — deferred to CI
  • E2E tests — not run locally; no Docker/runtime boundary changed
  • Transactional migration, semantic activation, rollback, and fix-forward behavior are covered by tests

✅ Contributor Checklist

  • PR is linked to an issue with status:approved
  • Exactly one type:* label is present
  • Maintainer action requested: apply exactly size:exception
  • Focused package tests pass
  • Documentation is not required for this internal bug fix
  • My commits follow Conventional Commits format
  • My commits do not include Co-Authored-By trailers

📏 Size Exception

The exact GitHub diff is 469 additions + 52 deletions = 521 changed lines across four tightly coupled files. Splitting would separate migration and activation behavior from rollback regressions and the golden contract, so maintainers are asked to apply size:exception.


💬 Notes for Reviewers

Review installAntigravityEngramPlugin first for the transaction boundary, then antigravityConfigs for migration and selected-variant configuration. Verify failure recovery in TestInjectAntigravityRecoversWriteFailures and the semantic recovery cases in TestInjectAntigravityManifestWriteFailureUsesSemanticActivity.

The intended boundary is limited to Antigravity MCP ownership, migration, activation, and recovery; behavior for other agents is unchanged.

Summary by CodeRabbit

  • New Features
    • Added Antigravity desktop-plugin-based Engram installation via plugin MCP configuration.
    • Migrates existing global Engram MCP settings into the plugin while preserving unrelated fields.
    • Generates consistent MCP invocation arguments and prefers an existing merged plugin command when available.
  • Bug Fixes
    • Avoids mutating configurations when the global config is invalid or unreadable.
    • Improves atomic, idempotent behavior to prevent partial/corrupt intermediate outputs.
    • Removes legacy global Engram registration when plugin-only behavior is expected.
    • Treats /home/linuxbrew/.linuxbrew/bin/engram as a stable Linuxbrew path.
  • Tests
    • Updated Antigravity and golden tests to validate the plugin-owned mcp_config.json location.

@coderabbitai

coderabbitai Bot commented Jul 20, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Antigravity Engram injection now uses plugin-owned MCP registration, migrates legacy global entries, preserves canonical agent arguments, supports Linuxbrew paths, and adds convergence, rollback, malformed-config, and golden-fixture coverage.

Changes

Antigravity Engram integration

Layer / File(s) Summary
Plugin registration and command contract
internal/components/engram/inject.go
Antigravity uses a dedicated installer, canonical ["mcp", "--tools=agent"] arguments, reconciled command selection, and recognizes the stable Linuxbrew binary path.
Global migration and atomic reconciliation
internal/components/engram/inject.go
Existing global and plugin MCP JSON is reconciled, legacy global Engram registration is removed when appropriate, and related file writes support verification, rollback, and roll-forward recovery.
Migration and failure validation
internal/components/engram/inject_test.go, internal/components/golden_test.go
Tests cover plugin-scoped configuration, convergence, idempotency, command selection, write recovery, malformed configuration rejection, canonical arguments, and the plugin-owned golden fixture location.

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related PRs

Suggested labels: size:exception

Suggested reviewers: alan-thegentleman

Sequence Diagram(s)

sequenceDiagram
  participant injectWithOptions
  participant installAntigravityEngramPlugin
  participant GlobalConfig
  participant PluginConfig
  participant PluginManifest
  injectWithOptions->>installAntigravityEngramPlugin: install Antigravity Engram plugin
  installAntigravityEngramPlugin->>GlobalConfig: read and inspect legacy Engram entry
  installAntigravityEngramPlugin->>PluginConfig: select command and write canonical Engram config
  installAntigravityEngramPlugin->>GlobalConfig: remove legacy registration when migrating
  installAntigravityEngramPlugin->>PluginManifest: activate plugin and recover on failure
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The changes address #797 by consolidating Antigravity registration, preserving --tools=agent, and recognizing the Linuxbrew path.
Out of Scope Changes check ✅ Passed The modified files stay focused on Antigravity Engram registration, migration, and tests, with no unrelated feature work visible.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: preventing duplicate Antigravity MCP registration for Engram.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@Alan-TheGentleman Alan-TheGentleman added the type:bug Bug fix label Jul 20, 2026

@Alan-TheGentleman Alan-TheGentleman left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks for the focused migration work. I am blocking this because the desktop Antigravity variant can lose its only Engram registration: migration removes the desktop global entry, but the replacement plugin is always written under the CLI path. The operation is also not failure-atomic because the plugin is activated before the global removal, so a failed global write leaves both registrations active. Route the replacement to the selected Antigravity variant and add a write-failure rollback or ordering test, then push an update and I will re-review.

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
internal/components/engram/inject.go (1)

270-298: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Registration write happens before its supporting files — partial failure re-creates the duplicate-registration bug.

installAntigravityEngramPlugin writes mcp_config.json (the actual Engram MCP registration) first, then hooks.json, then plugin.json. If either later write fails, the caller (injectWithOptions) returns an error before reaching the global-migration step, but the plugin's mcp_config.json has already been committed to disk with engram registered — while the legacy global entry is still untouched (migration never runs). That leaves Engram registered in both the global config and the plugin config simultaneously, which is precisely the duplicate-registration defect this PR fixes (issue #797), until a subsequent successful run heals it.

Writing the registration file last (after the static hooks.json/plugin.json content) would ensure a failure never leaves a partial duplicate: no registration exists anywhere until the operation fully completes.

♻️ Suggested reordering
 func installAntigravityEngramPlugin(pluginDir, engramCommand string) (bool, []string, error) {
 	files := make([]string, 0, 3)
 	changed := false
 
-	pluginMCPPath := filepath.Join(pluginDir, "mcp_config.json")
-	mcpWrite, err := mergeJSONFile(pluginMCPPath, engramOverlayJSON(model.AgentAntigravity, engramCommand))
-	if err != nil {
-		return false, nil, fmt.Errorf("write Antigravity Engram plugin MCP config: %w", err)
-	}
-	changed = changed || mcpWrite.Changed
-	files = append(files, pluginMCPPath)
-
 	hooksPath := filepath.Join(pluginDir, "hooks.json")
 	hooksWrite, err := writeFileAtomic(hooksPath, antigravityEngramHooksJSON(), 0o644)
 	if err != nil {
 		return false, nil, fmt.Errorf("write Antigravity Engram hooks: %w", err)
 	}
 	changed = changed || hooksWrite.Changed
 	files = append(files, hooksPath)
 
 	pluginPath := filepath.Join(pluginDir, "plugin.json")
 	pluginWrite, err := writeFileAtomic(pluginPath, []byte(antigravityEngramPluginJSON), 0o644)
 	if err != nil {
 		return false, nil, fmt.Errorf("write Antigravity Engram plugin manifest: %w", err)
 	}
 	changed = changed || pluginWrite.Changed
 	files = append(files, pluginPath)
 
+	pluginMCPPath := filepath.Join(pluginDir, "mcp_config.json")
+	mcpWrite, err := mergeJSONFile(pluginMCPPath, engramOverlayJSON(model.AgentAntigravity, engramCommand))
+	if err != nil {
+		return false, nil, fmt.Errorf("write Antigravity Engram plugin MCP config: %w", err)
+	}
+	changed = changed || mcpWrite.Changed
+	files = append(files, pluginMCPPath)
+
 	return changed, files, nil
 }

As per path instructions, "Install/sync operations must be idempotent (running twice equals running once)" — reruns still converge, but the transient duplicate-registration window during a failed run undercuts the PR's stated goal of registering Engram exactly once.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/components/engram/inject.go` around lines 270 - 298, Update
installAntigravityEngramPlugin so the static hooks.json and plugin.json files
are written before mcp_config.json. Move the mergeJSONFile call for
pluginMCPPath and its error handling after both writeFileAtomic calls, while
preserving changed tracking, file ordering, and returned errors.

Source: Path instructions

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@internal/components/engram/inject_test.go`:
- Around line 770-814: The hooks.json failure branch in
TestInjectAntigravityMigrationWriteFailuresRemainAtomic does not verify the
transient plugin configuration. Add assertions there to read
pluginDir/mcp_config.json and confirm it contains the expected engram command
and args, matching the existing global-failure branch, while preserving the
current global-file and manifest checks.

---

Outside diff comments:
In `@internal/components/engram/inject.go`:
- Around line 270-298: Update installAntigravityEngramPlugin so the static
hooks.json and plugin.json files are written before mcp_config.json. Move the
mergeJSONFile call for pluginMCPPath and its error handling after both
writeFileAtomic calls, while preserving changed tracking, file ordering, and
returned errors.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 5b00a1db-3d2f-4926-855f-420b6230e5c2

📥 Commits

Reviewing files that changed from the base of the PR and between aa71ec4 and 666d02f.

📒 Files selected for processing (2)
  • internal/components/engram/inject.go
  • internal/components/engram/inject_test.go

Comment thread internal/components/engram/inject_test.go Outdated

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
internal/components/engram/inject_test.go (1)

578-589: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

cliMCPPath now duplicates pluginMCPPath. After this change, cliMCPPath (Line 578) resolves to the exact same file as pluginMCPPath (Line 596) — .gemini/antigravity-cli/plugins/gentle-ai-engram/mcp_config.json — so lines 579-604 read and assert against the same file twice under two different variable names. The distinct naming implies two separate configs and can mislead future maintainers. Consider collapsing into a single read/assert block.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/components/engram/inject_test.go` around lines 578 - 589, The test
duplicates the same Antigravity MCP configuration read and assertions through
cliMCPPath and pluginMCPPath. Consolidate the duplicate read/assert block into
one shared path and variable, removing the redundant block while preserving all
existing configuration validations.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@internal/components/engram/inject.go`:
- Around line 381-390: Restore the Antigravity settings bootstrap before the
staging flow in the relevant injection function, ensuring a missing
settings.json inherits the user’s ~/.gemini/settings.json defaults rather than
only falling back to "{}". Reuse the existing ensureAntigravitySettings helper
and preserve the current files/readImage staging behavior.

---

Outside diff comments:
In `@internal/components/engram/inject_test.go`:
- Around line 578-589: The test duplicates the same Antigravity MCP
configuration read and assertions through cliMCPPath and pluginMCPPath.
Consolidate the duplicate read/assert block into one shared path and variable,
removing the redundant block while preserving all existing configuration
validations.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: acfe133e-d6b2-4e5c-afa4-04c6eae1499f

📥 Commits

Reviewing files that changed from the base of the PR and between 666d02f and d821fd0.

⛔ Files ignored due to path filters (1)
  • testdata/golden/engram-antigravity-mcp.golden is excluded by !testdata/**
📒 Files selected for processing (3)
  • internal/components/engram/inject.go
  • internal/components/engram/inject_test.go
  • internal/components/golden_test.go

Comment on lines +381 to +390
files := []string{mcpPath, filepath.Join(pluginDir, "hooks.json"), adapter.SettingsPath(homeDir), manifestPath}
changed := false
for i, content := range [][]byte{pluginMCP, antigravityEngramHooksJSON(), []byte("{}\n")} {
before, readErr := readImage(files[i])
if readErr != nil {
return false, nil, fmt.Errorf("read staging target %q: %w", files[i], readErr)
}
if i == 2 && before.exists {
continue
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find all call sites of ensureAntigravitySettings (excluding its definition)
rg -nP '\bensureAntigravitySettings\s*\(' --type=go -C2

# Confirm whether the ~/.gemini/settings.json bootstrap source is still referenced anywhere
rg -nP '\.gemini/settings\.json|filepath\.Join\([^)]*\.gemini' --type=go -C2

Repository: Gentleman-Programming/gentle-ai

Length of output: 169


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Map the file and surrounding symbols first
ast-grep outline internal/components/engram/inject.go --view expanded || true

# Show the relevant region around the staging loop and helper definition
sed -n '340,780p' internal/components/engram/inject.go | cat -n

# Find all references to the helper and the gemini bootstrap source
rg -n --hidden -S 'ensureAntigravitySettings|\.gemini/settings\.json|filepath\.Join\([^)]*\.gemini' internal/components/engram -C 2 || true

# Check whether the helper is called anywhere else in the repo
rg -n --hidden -S '\bensureAntigravitySettings\s*\(' . -g '*.go' -C 2 || true

Repository: Gentleman-Programming/gentle-ai

Length of output: 32556


Keep the Antigravity settings bootstrap

settings.json now falls back to {} when missing, and ensureAntigravitySettings has no production call sites, so Antigravity no longer inherits ~/.gemini/settings.json defaults on first run. Restore that bootstrap before this staging path.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/components/engram/inject.go` around lines 381 - 390, Restore the
Antigravity settings bootstrap before the staging flow in the relevant injection
function, ensuring a missing settings.json inherits the user’s
~/.gemini/settings.json defaults rather than only falling back to "{}". Reuse
the existing ensureAntigravitySettings helper and preserve the current
files/readImage staging behavior.

@dnlrsls
dnlrsls force-pushed the fix/797-linuxbrew-antigravity-mcp branch from d821fd0 to 42a9787 Compare July 21, 2026 08:21
@dnlrsls

dnlrsls commented Jul 21, 2026

Copy link
Copy Markdown
Contributor Author

Maintainer action requested: please apply exactly size:exception. The 521-line, four-file diff is one inseparable transactional boundary across migration, semantic activation, rollback regressions, and the golden contract. The full four-file target passed focused package tests and pre-pr validation.

@dnlrsls

dnlrsls commented Jul 22, 2026

Copy link
Copy Markdown
Contributor Author

Superseded by #1643, which preserves the same issue scope in a smaller, reviewable transaction with the updated recovery coverage. Closing this PR to keep the review queue focused.

@dnlrsls dnlrsls closed this Jul 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

type:bug Bug fix

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(engram): prevent double MCP registration in Antigravity and support Linuxbrew path

2 participants