Skip to content

feat: Integrate Antigravity harness with fallback in Controller V2#22

Draft
anj-s wants to merge 1 commit into
mainfrom
u/anj/harness-interface-2
Draft

feat: Integrate Antigravity harness with fallback in Controller V2#22
anj-s wants to merge 1 commit into
mainfrom
u/anj/harness-interface-2

Conversation

@anj-s
Copy link
Copy Markdown
Collaborator

@anj-s anj-s commented May 22, 2026

This change implements the integration of the Antigravity agent as a harness in AX Controller V2 as a first step to validate the surface. We are using a subprocess with input/output message as a first step.

Key changes:

  1. Modified the example Antigravity agent in examples/antigravity_agent to accept prompts via command-line arguments, enabling dynamic interaction.
  2. Implemented AntigravityHarness in Go (internal/harness/antigravity.go) which runs the Python agent as a subprocess, captures stdout, and streams the response. Added a TODO to transition this to a gRPC server to avoid subprocess overhead in the future.
  3. Created BuildHarness in internal/controller2/builder.go to construct the appropriate harness. It performs environment checks (python3 availability and script existence) and falls back to the test harness (harnesstest) if the environment is not ready.
  4. Updated Controller V2 (internal/controller2/controller.go) to use BuildHarness based on the requested AgentId, and made the Antigravity script path configurable.
  5. Added a unit test in internal/controller2/controller_test.go to verify the fallback behavior when a non-existent script path is configured.

@anj-s anj-s force-pushed the u/anj/harness-interface-2 branch 2 times, most recently from fb6dd6c to e5d6fee Compare May 22, 2026 18:57
…ntric design

This PR implements the integration of the Antigravity agent as the first built-in harness in AX Controller V2, satisfying the first item on the AX roadmap. It also refactors the architecture to make `Registry` the Single Source of Truth (SSOT) and introduces robust fallback mechanisms.

### Goals
1. **Built-in Harness Integration**: Enable AX Controller V2 to execute Python-based Antigravity agents.
2. **Registry-Centric Architecture (SSOT)**: Centralize the management of both agents and harnesses in the `Registry`.
3. **Inverted Control (Dependency Injection)**: Decouple the `Controller` from harness creation by passing `Registry` as an input config.
4. **Resilient Fallbacks**: Implement build-time (script check) and runtime (registration check) fallbacks to a Test Harness to prevent failures in unconfigured environments.
5. **End-to-End Verification**: Provide a comprehensive E2E demonstration script.

### Key Changes
- **Registry Updates (`internal/controller2/registry.go`)**: Added support for registering and retrieving Go `Harness` instances in `Registry`.
- **Controller V2 Refactoring (`internal/controller2/controller.go`)**:
  - Received `Registry` as an input config in `New`.
  - Updated `Exec` to retrieve the harness from the registry using `AgentId` at runtime, falling back to the test harness if not found.
- **Antigravity Go Harness (`internal/harness/antigravity.go`)**:
  - Implemented `AntigravityHarness` which executes the Python agent as a subprocess, passing the prompt as an argument.
  - Captured stdout and returned it as a streamed message.
  - Added a TODO to migrate to a gRPC server in the next step to avoid subprocess overhead.
- **Python Agent Modification (`examples/antigravity_agent/agent.py`)**: Modified the script to accept dynamic prompt inputs from command line arguments.
- **Verification (`internal/controller2/controller_test.go`, `fork_test.go`, `e2e.go`)**:
  - Updated unit tests to adapt to the new `Registry` injection.
  - Added unit tests for both build-time (implemented manually in test setup) and runtime fallbacks.
  - Created `e2e.go` in the root to demonstrate all 3 execution paths (Runtime Fallback, Build-time Fallback, and actual Antigravity Happy Path).
@anj-s anj-s force-pushed the u/anj/harness-interface-2 branch from e5d6fee to 7b7d8bd Compare May 22, 2026 20:38

// Run implements Execution.Run.
// It executes the Python agent as a subprocess, passing the last user message as an argument.
func (e *antigravityExecution) Run(ctx context.Context, handler Handler) error {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

For discussion: I am wondering what is the desired call sequence for Execution.Queue(...) and Execution.Run(...)?

If each call of Queue(...) is followed by exactly one call of Run(...), shall we merge them into one?

If multiple calls of Run(...) are needed to consume pending messages in a queue, how the Execution should notify the caller if more calls to run is needed or not?


h := harnesstest.New()
// Retrieve harness from registry
h, err := d.registry.GetHarness(req.AgentId)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Looks like we are retrieving an existing harness instance given an agent id. Are we mixing the two concepts here?

return firstErr
}
// RegisterHarness registers a harness.
func (r *Registry) RegisterHarness(id string, h harness.Harness) {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

As mentioned in the other thread, we can call it harnessId to be more explicit.

mu sync.RWMutex
agents map[string]agent.Agent
agentInfo map[string]*agent.AgentInfo
harnesses map[string]harness.Harness
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

We are using the registry for both agents and harnesses. (ok to be not in this pr) - do we plan to let the registry in the controller package to only manage harnesses? I think the agents managed by a harness is a harness implementation detail.

func (e *antigravityExecution) Run(ctx context.Context, handler Handler) error {
e.mu.Lock()
inputs := e.queued
e.queued = nil
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

The conversation history is lost here. For a poc implementation I think it's OK. But we should clarify if the controller is responsible to queue all history for the execution, or if the execution class itself is responsible to maintain the history.

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.

2 participants