+ Generate an HTTP password in Gerrit Settings > HTTP Credentials
+
+
+ >
+ ) : (
+
+
+
+ )}
+
+
+
+
+
+
+
+ )}
+
+ {/* Action buttons */}
+
+ {!showForm && (
+
+ )}
+
+
+
+ )
+}
diff --git a/components/frontend/src/services/api/gerrit-auth.ts b/components/frontend/src/services/api/gerrit-auth.ts
new file mode 100644
index 000000000..b0641704d
--- /dev/null
+++ b/components/frontend/src/services/api/gerrit-auth.ts
@@ -0,0 +1,63 @@
+import { apiClient } from './client'
+
+export type GerritAuthMethod = 'http_basic' | 'git_cookies'
+
+export type GerritConnectRequest = {
+ instanceName: string
+ url: string
+ authMethod: GerritAuthMethod
+ username?: string
+ httpToken?: string
+ gitcookiesContent?: string
+}
+
+export type GerritTestRequest = {
+ url: string
+ authMethod: GerritAuthMethod
+ username?: string
+ httpToken?: string
+ gitcookiesContent?: string
+}
+
+export type GerritTestResponse = {
+ valid: boolean
+ message?: string
+ error?: string
+}
+
+export type GerritInstanceStatus = {
+ connected: boolean
+ instanceName: string
+ url: string
+ authMethod: GerritAuthMethod
+ updatedAt: string
+}
+
+export type GerritInstancesResponse = {
+ instances: GerritInstanceStatus[]
+}
+
+/** Connect a Gerrit instance by validating and storing credentials. */
+export async function connectGerrit(data: GerritConnectRequest): Promise {
+ await apiClient.post('/auth/gerrit/connect', data)
+}
+
+/** Test Gerrit credentials without storing them. */
+export async function testGerritConnection(data: GerritTestRequest): Promise {
+ return apiClient.post('/auth/gerrit/test', data)
+}
+
+/** List all connected Gerrit instances for the current user. */
+export async function getGerritInstances(): Promise {
+ return apiClient.get('/auth/gerrit/instances')
+}
+
+/** Get connection status for a specific Gerrit instance. */
+export async function getGerritInstanceStatus(instanceName: string): Promise {
+ return apiClient.get(`/auth/gerrit/${instanceName}/status`)
+}
+
+/** Disconnect a Gerrit instance and remove stored credentials. */
+export async function disconnectGerrit(instanceName: string): Promise {
+ await apiClient.delete(`/auth/gerrit/${instanceName}/disconnect`)
+}
diff --git a/components/frontend/src/services/api/integrations.ts b/components/frontend/src/services/api/integrations.ts
index 60d6addd2..b328609a4 100644
--- a/components/frontend/src/services/api/integrations.ts
+++ b/components/frontend/src/services/api/integrations.ts
@@ -34,6 +34,15 @@ export type IntegrationsStatus = {
updatedAt?: string
valid?: boolean
}
+ gerrit: {
+ instances: {
+ connected: boolean
+ instanceName: string
+ url: string
+ authMethod: 'http_basic' | 'git_cookies'
+ updatedAt: string
+ }[]
+ }
mcpServers?: Record
}
diff --git a/components/frontend/src/services/queries/use-gerrit.ts b/components/frontend/src/services/queries/use-gerrit.ts
new file mode 100644
index 000000000..38d9706c2
--- /dev/null
+++ b/components/frontend/src/services/queries/use-gerrit.ts
@@ -0,0 +1,43 @@
+import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
+import * as gerritAuthApi from '../api/gerrit-auth'
+
+/** Hook to fetch all connected Gerrit instances. */
+export function useGerritInstances() {
+ return useQuery({
+ queryKey: ['gerrit', 'instances'],
+ queryFn: () => gerritAuthApi.getGerritInstances(),
+ })
+}
+
+/** Hook to connect a new Gerrit instance with credential validation. */
+export function useConnectGerrit() {
+ const queryClient = useQueryClient()
+
+ return useMutation({
+ mutationFn: gerritAuthApi.connectGerrit,
+ onSuccess: () => {
+ queryClient.invalidateQueries({ queryKey: ['integrations', 'status'] })
+ queryClient.invalidateQueries({ queryKey: ['gerrit', 'instances'] })
+ },
+ })
+}
+
+/** Hook to disconnect a Gerrit instance and remove its credentials. */
+export function useDisconnectGerrit() {
+ const queryClient = useQueryClient()
+
+ return useMutation({
+ mutationFn: gerritAuthApi.disconnectGerrit,
+ onSuccess: () => {
+ queryClient.invalidateQueries({ queryKey: ['integrations', 'status'] })
+ queryClient.invalidateQueries({ queryKey: ['gerrit', 'instances'] })
+ },
+ })
+}
+
+/** Hook to test Gerrit credentials without storing them. */
+export function useTestGerritConnection() {
+ return useMutation({
+ mutationFn: gerritAuthApi.testGerritConnection,
+ })
+}
diff --git a/components/runners/ambient-runner/.mcp.json b/components/runners/ambient-runner/.mcp.json
index 66c145698..d6d66887f 100644
--- a/components/runners/ambient-runner/.mcp.json
+++ b/components/runners/ambient-runner/.mcp.json
@@ -23,6 +23,14 @@
"READ_ONLY_MODE": "true"
}
},
+ "gerrit": {
+ "command": "/opt/gerrit-mcp-server/.venv/bin/python",
+ "args": ["/opt/gerrit-mcp-server/gerrit_mcp_server/main.py", "stdio"],
+ "env": {
+ "PYTHONPATH": "/opt/gerrit-mcp-server/",
+ "GERRIT_CONFIG_PATH": "${GERRIT_CONFIG_PATH}"
+ }
+ },
"google-workspace": {
"command": "uvx",
"args": [
diff --git a/components/runners/ambient-runner/Dockerfile b/components/runners/ambient-runner/Dockerfile
index 515e536e0..57bcea078 100644
--- a/components/runners/ambient-runner/Dockerfile
+++ b/components/runners/ambient-runner/Dockerfile
@@ -62,6 +62,31 @@ RUN dnf install -y go-toolset && \
# Install uv (provides uvx for package execution) and pre-commit (for repo hooks)
RUN pip install --no-cache-dir uv pre-commit
+# Install Python 3.12 for Gerrit MCP server (requires >=3.12, runner uses 3.11)
+RUN dnf install -y python3.12 python3.12-pip && \
+ dnf clean all
+
+# Install Gerrit MCP server (code review integration for Gerrit-based communities)
+# Pinned to specific commit for reproducibility
+# Uses python3.12 for the venv since gerrit-mcp-server requires Python >=3.12
+RUN set -o pipefail && \
+ git clone https://gerrit.googlesource.com/gerrit-mcp-server /opt/gerrit-mcp-server && \
+ cd /opt/gerrit-mcp-server && \
+ git checkout 5666642afe1a5217e2529225d4bd9c9df6310bd6 && \
+ python3.12 -m venv .venv && \
+ . .venv/bin/activate && \
+ pip install --no-cache-dir --index-url https://pypi.org/simple -r uv-requirements.txt --require-hashes && \
+ uv pip compile pyproject.toml --generate-hashes --output-file requirements.txt --extra dev --extra-index-url https://pypi.org/simple && \
+ uv pip sync requirements.txt && \
+ uv build && \
+ WHEEL_FILE=$(find dist -maxdepth 1 -name '*.whl' -print -quit) && \
+ WHEEL_HASH=$(sha256sum "${WHEEL_FILE}" | awk '{print $1}') && \
+ echo "${WHEEL_FILE} --hash=sha256:${WHEEL_HASH}" > local-requirements.txt && \
+ uv pip install -r local-requirements.txt --no-deps --require-hashes && \
+ rm -f local-requirements.txt && \
+ chown -R 1001:0 /opt/gerrit-mcp-server && \
+ chmod -R g=u /opt/gerrit-mcp-server
+
# Create working directory
WORKDIR /app
diff --git a/components/runners/ambient-runner/ambient_runner/bridges/claude/mcp.py b/components/runners/ambient-runner/ambient_runner/bridges/claude/mcp.py
index 8bed29779..6f773d2e5 100644
--- a/components/runners/ambient-runner/ambient_runner/bridges/claude/mcp.py
+++ b/components/runners/ambient-runner/ambient_runner/bridges/claude/mcp.py
@@ -30,6 +30,81 @@
]
+def generate_gerrit_config(instances: list[dict]) -> None:
+ """Generate gerrit_config.json and gitcookies file from fetched credentials.
+
+ Creates /tmp/gerrit-mcp/ directory with:
+ - gerrit_config.json: Native Gerrit MCP server config
+ - .gitcookies: Combined gitcookies content (if any instances use git_cookies auth)
+
+ Sets GERRIT_CONFIG_PATH env var to point to the generated config.
+ """
+ config_dir = Path("/tmp/gerrit-mcp")
+
+ # Always clean up old config to prevent stale credentials
+ if config_dir.exists():
+ import shutil
+
+ shutil.rmtree(config_dir)
+
+ if not instances:
+ os.environ.pop("GERRIT_CONFIG_PATH", None)
+ return
+
+ config_dir.mkdir(parents=True, exist_ok=True)
+
+ gerrit_hosts = []
+ gitcookies_lines: list[str] = []
+
+ for inst in instances:
+ host_entry: dict = {
+ "name": inst.get("instanceName", ""),
+ "external_url": inst.get("url", ""),
+ }
+
+ auth_method = inst.get("authMethod", "")
+ if auth_method == "http_basic":
+ host_entry["authentication"] = {
+ "type": "http_basic",
+ "username": inst.get("username", ""),
+ "auth_token": inst.get("httpToken", ""),
+ }
+ elif auth_method == "git_cookies":
+ gitcookies_path = str(config_dir / ".gitcookies")
+ content = inst.get("gitcookiesContent", "")
+ if content:
+ gitcookies_lines.append(content.rstrip("\n"))
+ host_entry["authentication"] = {
+ "type": "git_cookies",
+ "gitcookies_path": gitcookies_path,
+ }
+
+ gerrit_hosts.append(host_entry)
+
+ # Write combined gitcookies file if any instances use git_cookies auth
+ if gitcookies_lines:
+ gitcookies_path = config_dir / ".gitcookies"
+ with open(gitcookies_path, "w") as f:
+ f.write("\n".join(gitcookies_lines) + "\n")
+ gitcookies_path.chmod(0o600)
+ logger.info("Wrote combined gitcookies file for Gerrit instances")
+
+ # Build gerrit_config.json
+ gerrit_config: dict = {
+ "gerrit_hosts": gerrit_hosts,
+ }
+ if gerrit_hosts:
+ gerrit_config["default_gerrit_base_url"] = gerrit_hosts[0].get("external_url", "")
+
+ config_path = config_dir / "gerrit_config.json"
+ with open(config_path, "w") as f:
+ json.dump(gerrit_config, f, indent=2)
+ config_path.chmod(0o600)
+
+ os.environ["GERRIT_CONFIG_PATH"] = str(config_path)
+ logger.info(f"Generated Gerrit config with {len(gerrit_hosts)} host(s) at {config_path}")
+
+
def build_mcp_servers(
context: RunnerContext,
cwd_path: str,
@@ -112,8 +187,7 @@ def build_mcp_servers(
logger.info(
f"Added backend API MCP tools ({len(backend_tools)}): "
"acp_list_sessions, acp_get_session, acp_create_session, "
- "acp_stop_session, acp_send_message, acp_get_session_status, "
- "acp_restart_session, acp_list_workflows, acp_get_api_reference"
+ "acp_stop_session, acp_send_message, acp_get_api_reference"
)
return mcp_servers
@@ -261,6 +335,42 @@ def check_mcp_authentication(server_name: str) -> tuple[bool | None, str | None]
return False, "Jira not configured - connect on Integrations page"
+ if server_name == "gerrit":
+ config_path = os.getenv("GERRIT_CONFIG_PATH", "")
+ if config_path and Path(config_path).exists():
+ return True, "Gerrit credentials configured"
+
+ # Fallback: check if backend has credentials available
+ try:
+ import urllib.request as _urllib_request
+
+ base = os.getenv("BACKEND_API_URL", "").rstrip("/")
+ project = os.getenv("PROJECT_NAME") or os.getenv(
+ "AGENTIC_SESSION_NAMESPACE", ""
+ )
+ session_id = os.getenv("SESSION_ID", "")
+
+ if base and project and session_id:
+ url = f"{base}/projects/{project.strip()}/agentic-sessions/{session_id}/credentials/gerrit"
+ req = _urllib_request.Request(url, method="GET")
+ bot = (os.getenv("BOT_TOKEN") or "").strip()
+ if bot:
+ req.add_header("Authorization", f"Bearer {bot}")
+ try:
+ with _urllib_request.urlopen(req, timeout=3) as resp:
+ data = json.loads(resp.read())
+ if data.get("instances"):
+ return (
+ True,
+ "Gerrit credentials available (not yet loaded in session)",
+ )
+ except Exception as e:
+ logger.debug(f"Gerrit credential probe failed: {e}")
+ except Exception as e:
+ logger.debug(f"Gerrit credential check setup failed: {e}")
+
+ return False, "Gerrit not configured - connect on Integrations page"
+
# Generic fallback: check if MCP_{SERVER_NAME}_* env vars are populated
sanitized = server_name.upper().replace("-", "_")
prefix = f"MCP_{sanitized}_"
diff --git a/components/runners/ambient-runner/ambient_runner/platform/auth.py b/components/runners/ambient-runner/ambient_runner/platform/auth.py
old mode 100755
new mode 100644
index 9a646df14..978ad7250
--- a/components/runners/ambient-runner/ambient_runner/platform/auth.py
+++ b/components/runners/ambient-runner/ambient_runner/platform/auth.py
@@ -266,6 +266,19 @@ async def fetch_gitlab_token(context: RunnerContext) -> str:
return data.get("token", "")
+async def fetch_gerrit_credentials(context: RunnerContext) -> list[dict]:
+ """Fetch all Gerrit instance credentials from backend API.
+
+ Returns list of instance dicts with: instanceName, url, authMethod,
+ username, httpToken, gitcookiesContent
+ """
+ data = await _fetch_credential(context, "gerrit")
+ instances = data.get("instances", [])
+ if instances:
+ logger.info(f"Fetched Gerrit credentials for {len(instances)} instance(s)")
+ return instances
+
+
async def fetch_token_for_url(context: RunnerContext, url: str) -> str:
"""Fetch appropriate token based on repository URL host."""
try:
@@ -288,11 +301,12 @@ async def populate_runtime_credentials(context: RunnerContext) -> None:
logger.info("Fetching fresh credentials from backend API...")
# Fetch all credentials concurrently
- google_creds, jira_creds, gitlab_creds, github_creds = await asyncio.gather(
+ google_creds, jira_creds, gitlab_creds, github_creds, gerrit_instances = await asyncio.gather(
fetch_google_credentials(context),
fetch_jira_credentials(context),
fetch_gitlab_credentials(context),
fetch_github_credentials(context),
+ fetch_gerrit_credentials(context),
return_exceptions=True,
)
@@ -363,6 +377,18 @@ async def populate_runtime_credentials(context: RunnerContext) -> None:
if github_creds.get("email"):
git_user_email = github_creds["email"]
+ # Gerrit credentials (generate config file for MCP server)
+ if isinstance(gerrit_instances, Exception):
+ logger.warning(f"Failed to fetch Gerrit credentials: {gerrit_instances}")
+ elif gerrit_instances:
+ try:
+ from ambient_runner.bridges.claude.mcp import generate_gerrit_config
+
+ generate_gerrit_config(gerrit_instances)
+ logger.info("Generated Gerrit MCP config from backend credentials")
+ except Exception as e:
+ logger.warning(f"Failed to generate Gerrit config: {e}")
+
# Configure git identity and credential helper
await configure_git_identity(git_user_name, git_user_email)
install_git_credential_helper()
@@ -399,6 +425,20 @@ def clear_runtime_credentials() -> None:
os.environ.pop(key, None)
cleared.append(key)
+ # Remove Gerrit config and gitcookies files
+ gerrit_config_path = os.environ.pop("GERRIT_CONFIG_PATH", None)
+ if gerrit_config_path:
+ cleared.append("GERRIT_CONFIG_PATH")
+ gerrit_dir = Path("/tmp/gerrit-mcp")
+ if gerrit_dir.exists():
+ try:
+ import shutil
+
+ shutil.rmtree(gerrit_dir)
+ cleared.append("gerrit_config_files")
+ except OSError as e:
+ logger.warning(f"Failed to remove Gerrit config directory: {e}")
+
# Remove Google Workspace credential file if present (uses same hardcoded path as populate_runtime_credentials)
google_cred_file = _GOOGLE_WORKSPACE_CREDS_FILE
if google_cred_file.exists():
@@ -605,3 +645,4 @@ async def configure_git_identity(user_name: str, user_email: str) -> None:
logger.warning(f"Failed to configure git identity: {e}")
except Exception as e:
logger.error(f"Unexpected error configuring git identity: {e}", exc_info=True)
+
diff --git a/docs/internal/integrations/README.md b/docs/internal/integrations/README.md
index e1ece3487..405d5b249 100644
--- a/docs/internal/integrations/README.md
+++ b/docs/internal/integrations/README.md
@@ -18,10 +18,19 @@ Documentation for integrating the Ambient Code Platform with external services.
- Clone, commit, and push operations
- Multi-provider projects (mix GitHub and GitLab)
+### Code Review
+
+**[Gerrit Integration](gerrit-integration.md)**
+- Multi-instance support (e.g., OpenStack, Android)
+- HTTP basic and gitcookies authentication
+- MCP server with 21 code review tools
+- Session-scoped credential injection
+
**Getting Started:**
- [GitHub Setup Guide](../GITHUB_APP_SETUP.md)
- [GitLab Token Setup](../gitlab-token-setup.md)
- [GitLab Self-Hosted Configuration](../gitlab-self-hosted.md)
+- [Gerrit Integration Guide](gerrit-integration.md)
---
@@ -53,6 +62,12 @@ Documentation for integrating the Ambient Code Platform with external services.
- **Personal Access Tokens** - Primary method
- **Instance URL** - Support for self-hosted
+### Gerrit
+
+- **HTTP Basic** - Username + HTTP password
+- **Gitcookies** - Cookie-based authentication
+- **Multi-instance** - Connect multiple Gerrit servers
+
### Google Workspace
- **OAuth 2.0** - User authorization
- **Session-scoped** - Credentials auto-removed after session
@@ -77,6 +92,10 @@ All integrations are configured per-project via:
- [GitLab Testing](../gitlab-testing-procedures.md) - Test procedures
- [GitLab API Endpoints](../api/gitlab-endpoints.md) - API reference
+### Gerrit
+
+- [Gerrit Integration](gerrit-integration.md) - Setup and usage guide
+
### Google Workspace
- [Google Workspace Integration](google-workspace.md) - Setup and usage
@@ -103,3 +122,4 @@ See [Contributing Guide](../../CONTRIBUTING.md) for development workflow.
---
**Questions?** Open a [GitHub Discussion](https://github.com/ambient-code/vTeam/discussions)
+
diff --git a/docs/internal/integrations/gerrit-integration.md b/docs/internal/integrations/gerrit-integration.md
new file mode 100644
index 000000000..8b7460c66
--- /dev/null
+++ b/docs/internal/integrations/gerrit-integration.md
@@ -0,0 +1,456 @@
+# Gerrit Integration for Ambient Code Platform
+
+Ambient Code Platform supports Gerrit code review instances, enabling AgenticSessions to interact with Gerrit changes through an MCP (Model Context Protocol) server. This guide covers connecting Gerrit instances, authentication methods, and using the 21 available code review tools.
+
+## Overview
+
+**What's Supported:**
+- Multiple Gerrit instances connected simultaneously (e.g., "openstack", "android")
+- HTTP Basic Auth (username + HTTP password)
+- Gitcookies-based authentication
+- 21 MCP tools for code review operations (browse changes, submit reviews, vote on changes, etc.)
+- Credential validation on connect via Gerrit's `/a/accounts/self` endpoint
+
+**Requirements:**
+- A Gerrit account with HTTP credentials or gitcookies
+- Network connectivity from runner pods to the Gerrit instance
+- Ambient Code Platform backend v1.2.0 or higher
+
+---
+
+## Quick Start
+
+### 1. Obtain Gerrit Credentials
+
+Choose one of the two supported authentication methods.
+
+**Option A: HTTP Basic Auth**
+
+1. Log in to your Gerrit instance (e.g., `https://review.opendev.org`)
+2. Navigate to **Settings** (gear icon or profile menu)
+3. Select **HTTP Credentials** (sometimes labeled "HTTP Password")
+4. Click **Generate Password** if you do not already have one
+5. Copy and save both your **username** and the generated **HTTP password**
+
+**Option B: Gitcookies**
+
+1. Follow your Gerrit instance's instructions for generating gitcookies (often found at `/new-password` on the instance)
+2. Open your local `~/.gitcookies` file
+3. Copy the line(s) corresponding to your Gerrit instance
+4. You will paste this content when connecting
+
+---
+
+### 2. Connect a Gerrit Instance
+
+Each Gerrit instance you connect requires a unique **instance name**. Instance names must be lowercase alphanumeric with hyphens, between 2 and 63 characters (regex: `^[a-z0-9][a-z0-9-]{0,61}[a-z0-9]$`).
+
+**Via API (HTTP Basic Auth):**
+```bash
+curl -X POST http://vteam-backend:8080/api/auth/gerrit/connect \
+ -H "Content-Type: application/json" \
+ -H "Authorization: Bearer " \
+ -d '{
+ "instanceName": "openstack",
+ "url": "https://review.opendev.org",
+ "authMethod": "http_basic",
+ "username": "john",
+ "httpToken": "abc123"
+ }'
+```
+
+**Via API (Gitcookies):**
+```bash
+curl -X POST http://vteam-backend:8080/api/auth/gerrit/connect \
+ -H "Content-Type: application/json" \
+ -H "Authorization: Bearer " \
+ -d '{
+ "instanceName": "android",
+ "url": "https://android-review.googlesource.com",
+ "authMethod": "git_cookies",
+ "gitcookiesContent": ".googlesource.com\tTRUE\t/\tTRUE\t...\to\tgit-user.cookies=..."
+ }'
+```
+
+**Success Response (200 OK):**
+```json
+{
+ "message": "Gerrit instance 'openstack' connected successfully",
+ "instanceName": "openstack",
+ "url": "https://review.opendev.org",
+ "authMethod": "http_basic"
+}
+```
+
+**What Happens on Connect:**
+1. Credentials are validated by calling `GET /a/accounts/self` on the Gerrit instance
+2. If validation passes, credentials are stored in a Kubernetes Secret
+3. The instance becomes available for use in AgenticSessions
+
+---
+
+### 3. Test Credentials (Optional)
+
+You can test credentials before storing them. This calls the same validation endpoint but does not persist anything.
+
+```bash
+curl -X POST http://vteam-backend:8080/api/auth/gerrit/test \
+ -H "Content-Type: application/json" \
+ -H "Authorization: Bearer " \
+ -d '{
+ "instanceName": "openstack",
+ "url": "https://review.opendev.org",
+ "authMethod": "http_basic",
+ "username": "john",
+ "httpToken": "abc123"
+ }'
+```
+
+Use this to verify your credentials are correct before committing to a connection.
+
+---
+
+### 4. Use Gerrit in an AgenticSession
+
+Once a Gerrit instance is connected, AgenticSessions automatically have access to the Gerrit MCP server and its 21 code review tools.
+
+**What Happens at Runtime:**
+1. The runner generates a `gerrit_config.json` file at `/tmp/gerrit-mcp/`
+2. The Gerrit MCP server launches in STDIO mode using a Python 3.12 virtual environment
+3. The agent can browse changes, submit reviews, vote, and perform other code review operations through the MCP tools
+
+No additional session configuration is needed -- connected Gerrit instances are available automatically.
+
+---
+
+## Managing Connections
+
+### List Connected Instances
+
+```bash
+curl -X GET http://vteam-backend:8080/api/auth/gerrit/instances \
+ -H "Authorization: Bearer "
+```
+
+This returns all Gerrit instances currently connected for your user.
+
+### Check Instance Status
+
+```bash
+curl -X GET http://vteam-backend:8080/api/auth/gerrit/openstack/status \
+ -H "Authorization: Bearer "
+```
+
+Replace `openstack` with your instance name.
+
+### Disconnect an Instance
+
+```bash
+curl -X DELETE http://vteam-backend:8080/api/auth/gerrit/openstack/disconnect \
+ -H "Authorization: Bearer "
+```
+
+This removes:
+- Stored credentials for the instance from the Kubernetes Secret
+- The instance from your list of connected instances
+- Access to that Gerrit instance in future AgenticSessions
+
+Your Gerrit account and credentials on the Gerrit server itself are not affected.
+
+### Update Credentials
+
+To update credentials for an existing instance (e.g., after an HTTP password rotation):
+
+1. Disconnect the instance
+2. Reconnect with the new credentials using the same instance name
+
+---
+
+## Multi-Instance Support
+
+You can connect multiple Gerrit instances simultaneously. Each instance is identified by its unique instance name.
+
+**Example: Connecting Two Instances**
+
+```bash
+# Connect OpenStack Gerrit
+curl -X POST http://vteam-backend:8080/api/auth/gerrit/connect \
+ -H "Content-Type: application/json" \
+ -H "Authorization: Bearer " \
+ -d '{
+ "instanceName": "openstack",
+ "url": "https://review.opendev.org",
+ "authMethod": "http_basic",
+ "username": "john",
+ "httpToken": "abc123"
+ }'
+
+# Connect Android Gerrit
+curl -X POST http://vteam-backend:8080/api/auth/gerrit/connect \
+ -H "Content-Type: application/json" \
+ -H "Authorization: Bearer " \
+ -d '{
+ "instanceName": "android",
+ "url": "https://android-review.googlesource.com",
+ "authMethod": "git_cookies",
+ "gitcookiesContent": ".googlesource.com\tTRUE\t/\tTRUE\t..."
+ }'
+```
+
+Both instances will be available to the MCP server during AgenticSessions.
+
+---
+
+## Security
+
+### Credential Storage
+
+- Credentials are stored in per-user Kubernetes Secrets named `gerrit-credentials-{userID}`
+- Each credential entry is keyed by `instanceName` within the per-user Secret
+- Stored in Kubernetes Secrets (encrypted at rest when cluster-level encryption is configured)
+- Credentials are never logged in plaintext
+- Credentials are not exposed in API responses after storage
+
+### Authentication Methods Compared
+
+| Method | Best For | Credential |
+|--------|----------|------------|
+| HTTP Basic Auth | Most Gerrit instances | Username + HTTP password from Gerrit Settings |
+| Gitcookies | Google-hosted instances (e.g., Googlesource) | Content from `~/.gitcookies` file |
+
+### Credential Rotation
+
+**Recommendation:** Rotate credentials whenever your Gerrit HTTP password changes or your gitcookies expire.
+
+**Process:**
+1. Generate new credentials in your Gerrit instance
+2. Test the new credentials using the `/api/auth/gerrit/test` endpoint
+3. Disconnect the existing instance
+4. Reconnect with the new credentials
+
+### Instance Name Rules
+
+Instance names serve as identifiers and must follow these rules:
+- Lowercase letters, digits, and hyphens only
+- Between 2 and 63 characters
+- Must start and end with a letter or digit (not a hyphen)
+- Regex: `^[a-z0-9][a-z0-9-]{0,61}[a-z0-9]$`
+
+**Valid examples:** `openstack`, `android`, `my-gerrit-01`
+
+**Invalid examples:** `-bad`, `a` (too short), `UPPER` (uppercase not allowed)
+
+---
+
+## Troubleshooting
+
+### Connection Issues
+
+**Problem:** "Authentication failed" when connecting
+
+**Solutions:**
+1. Verify your credentials are correct by testing them manually:
+ ```bash
+ # For HTTP Basic Auth
+ curl -u "john:abc123" https://review.opendev.org/a/accounts/self
+ ```
+2. Confirm you are using the correct auth method for your instance
+3. For HTTP Basic Auth: regenerate the HTTP password in Gerrit Settings > HTTP Credentials
+4. For gitcookies: confirm the cookie lines correspond to the correct Gerrit host
+
+**Problem:** "Invalid instance name" error
+
+**Solutions:**
+1. Check that the name is 2-63 characters long
+2. Use only lowercase letters, digits, and hyphens
+3. Do not start or end the name with a hyphen
+
+**Problem:** "Instance already connected" error
+
+**Solutions:**
+1. Choose a different instance name, or
+2. Disconnect the existing instance first, then reconnect
+
+---
+
+### Runtime Issues
+
+**Problem:** Gerrit MCP server fails to start in AgenticSession
+
+**Solutions:**
+1. Check session pod logs for Python version errors (Python 3.12 or higher is required):
+ ```bash
+ kubectl logs -n
+ ```
+2. Verify the instance is still connected:
+ ```bash
+ curl -X GET http://vteam-backend:8080/api/auth/gerrit/instances \
+ -H "Authorization: Bearer "
+ ```
+3. Check that the Gerrit instance is reachable from the runner pod:
+ ```bash
+ kubectl exec -it -n -- \
+ curl -s -o /dev/null -w "%{http_code}" https://review.opendev.org
+ ```
+
+**Problem:** MCP tools return authorization errors during a session
+
+**Solutions:**
+1. Your credentials may have expired or been revoked on the Gerrit server
+2. Disconnect and reconnect with fresh credentials
+3. Verify your Gerrit account still has the necessary permissions
+
+---
+
+### Network Issues
+
+**Problem:** Connection times out
+
+**Solutions:**
+1. Verify the Gerrit URL is correct and includes `https://`
+2. Check network connectivity from backend pods:
+ ```bash
+ kubectl exec -it -n vteam-backend -- \
+ curl -s -o /dev/null -w "%{http_code}" https://review.opendev.org
+ ```
+3. Check firewall rules allow traffic from the Kubernetes cluster to the Gerrit host
+4. Verify SSL/TLS certificates are valid
+
+---
+
+## API Reference
+
+### Connect Gerrit Instance
+
+```http
+POST /api/auth/gerrit/connect
+Content-Type: application/json
+Authorization: Bearer
+```
+
+**Request Body (HTTP Basic Auth):**
+```json
+{
+ "instanceName": "openstack",
+ "url": "https://review.opendev.org",
+ "authMethod": "http_basic",
+ "username": "john",
+ "httpToken": "abc123"
+}
+```
+
+**Request Body (Gitcookies):**
+```json
+{
+ "instanceName": "android",
+ "url": "https://android-review.googlesource.com",
+ "authMethod": "git_cookies",
+ "gitcookiesContent": ".googlesource.com\tTRUE\t/\tTRUE\t...\to\tgit-user.cookies=..."
+}
+```
+
+**Response (200 OK):**
+```json
+{
+ "message": "Gerrit instance 'openstack' connected successfully",
+ "instanceName": "openstack",
+ "url": "https://review.opendev.org",
+ "authMethod": "http_basic"
+}
+```
+
+---
+
+### Test Gerrit Credentials
+
+```http
+POST /api/auth/gerrit/test
+Content-Type: application/json
+Authorization: Bearer
+```
+
+Request body is identical to the connect endpoint. Validates credentials without storing them.
+
+---
+
+### List Connected Instances
+
+```http
+GET /api/auth/gerrit/instances
+Authorization: Bearer
+```
+
+**Response (200 OK):**
+Returns a list of all connected Gerrit instances for the authenticated user.
+
+---
+
+### Get Instance Status
+
+```http
+GET /api/auth/gerrit/:instanceName/status
+Authorization: Bearer
+```
+
+**Response (200 OK):**
+Returns the current status of the specified Gerrit instance.
+
+---
+
+### Disconnect Gerrit Instance
+
+```http
+DELETE /api/auth/gerrit/:instanceName/disconnect
+Authorization: Bearer
+```
+
+**Response (200 OK):**
+Removes stored credentials and disconnects the instance.
+
+---
+
+## FAQ
+
+**Q: Can I connect multiple Gerrit instances at the same time?**
+A: Yes. Each instance requires a unique instance name. You can connect as many instances as you need (e.g., "openstack", "android", "internal").
+
+**Q: Which authentication method should I use?**
+A: Use HTTP Basic Auth for most Gerrit instances. Use gitcookies for Google-hosted instances (such as those on `googlesource.com`) where gitcookies are the standard authentication mechanism.
+
+**Q: What happens if my HTTP password changes on the Gerrit server?**
+A: AgenticSessions will fail to authenticate with that instance. Disconnect the instance and reconnect with the new credentials.
+
+**Q: Do I need to configure anything in the AgenticSession to use Gerrit?**
+A: No. Connected Gerrit instances are automatically available to the MCP server during sessions. The runner handles configuration and MCP server startup.
+
+**Q: What tools does the Gerrit MCP server provide?**
+A: The MCP server provides 21 tools for code review operations, including browsing changes, submitting reviews, voting on changes, and other Gerrit workflow actions.
+
+**Q: Can I use Gerrit alongside GitHub and GitLab integrations?**
+A: Yes. The Gerrit integration is independent of GitHub and GitLab integrations. All three can be used simultaneously within the same Ambient Code Platform deployment.
+
+**Q: What Python version does the Gerrit MCP server require?**
+A: The Gerrit MCP server requires Python 3.12 or higher. The runner creates a dedicated virtual environment for it automatically.
+
+**Q: How do I know if my credentials are valid before connecting?**
+A: Use the `/api/auth/gerrit/test` endpoint. It validates credentials against the Gerrit instance without storing them.
+
+**Q: What is the instance name used for?**
+A: The instance name is a user-chosen identifier that distinguishes between multiple Gerrit instances. It is used in API paths (e.g., `/api/auth/gerrit/openstack/status`) and as part of the credential storage key.
+
+**Q: Can two users connect the same Gerrit instance with the same instance name?**
+A: Yes. Instance names are scoped per user. Two different users can both have an instance named "openstack" without conflict, as credentials are stored with a compound key of `instanceName.userID`.
+
+---
+
+## Support and Resources
+
+**Troubleshooting:**
+- Check backend logs: `kubectl logs -l app=vteam-backend -n vteam-backend`
+- Check session logs: `kubectl logs -n `
+- Verify Gerrit instance availability by accessing it directly in a browser
+
+**Gerrit Resources:**
+- [Gerrit REST API Documentation](https://gerrit-review.googlesource.com/Documentation/rest-api.html)
+- [Gerrit HTTP Credentials](https://gerrit-review.googlesource.com/Documentation/user-upload.html#http)
+- [Gitcookies Authentication](https://gerrit-review.googlesource.com/Documentation/user-upload.html#cookies)
diff --git a/specs/001-gerrit-integration/checklists/requirements.md b/specs/001-gerrit-integration/checklists/requirements.md
new file mode 100644
index 000000000..532f077a3
--- /dev/null
+++ b/specs/001-gerrit-integration/checklists/requirements.md
@@ -0,0 +1,37 @@
+# Specification Quality Checklist: Gerrit Integration Connector
+
+**Purpose**: Validate specification completeness and quality before proceeding to planning
+**Created**: 2026-03-24
+**Feature**: [spec.md](../spec.md)
+
+## Content Quality
+
+- [x] No implementation details (languages, frameworks, APIs)
+- [x] Focused on user value and business needs
+- [x] Written for non-technical stakeholders
+- [x] All mandatory sections completed
+
+## Requirement Completeness
+
+- [x] No [NEEDS CLARIFICATION] markers remain
+- [x] Requirements are testable and unambiguous
+- [x] Success criteria are measurable
+- [x] Success criteria are technology-agnostic (no implementation details)
+- [x] All acceptance scenarios are defined
+- [x] Edge cases are identified
+- [x] Scope is clearly bounded
+- [x] Dependencies and assumptions identified
+
+## Feature Readiness
+
+- [x] All functional requirements have clear acceptance criteria
+- [x] User scenarios cover primary flows
+- [x] Feature meets measurable outcomes defined in Success Criteria
+- [x] No implementation details leak into specification
+
+## Notes
+
+- All items pass validation. The spec is ready for `/speckit.plan`.
+- Clarification session on 2026-03-24 resolved 3 questions: gitcookies input method (paste), write operations scope (all enabled), multi-instance behavior (all auto-available).
+- The Assumptions section documents reasonable defaults for deployment mode (STDIO) and credential storage pattern (existing MCP server credentials).
+- Out of Scope section clearly bounds the feature to code review API integration, excluding Git transport and admin operations.
diff --git a/specs/001-gerrit-integration/contracts/frontend-types.ts b/specs/001-gerrit-integration/contracts/frontend-types.ts
new file mode 100644
index 000000000..db456f0fb
--- /dev/null
+++ b/specs/001-gerrit-integration/contracts/frontend-types.ts
@@ -0,0 +1,55 @@
+/**
+ * Frontend TypeScript types for the Gerrit Integration.
+ * These extend the existing IntegrationsStatus type.
+ */
+
+// --- API Request/Response Types ---
+
+export type GerritAuthMethod = 'http_basic' | 'git_cookies'
+
+export type GerritConnectRequest =
+ | {
+ instanceName: string
+ url: string
+ authMethod: 'http_basic'
+ username: string
+ httpToken: string
+ }
+ | {
+ instanceName: string
+ url: string
+ authMethod: 'git_cookies'
+ gitcookiesContent: string
+ }
+
+export interface GerritConnectResponse {
+ message: string
+ instanceName: string
+ url: string
+ authMethod: GerritAuthMethod
+}
+
+export interface GerritInstanceStatus {
+ connected: boolean
+ instanceName: string
+ url: string
+ authMethod: GerritAuthMethod
+ updatedAt: string
+}
+
+export interface GerritTestResponse {
+ valid: boolean
+ message?: string
+ error?: string
+}
+
+export interface GerritInstancesListResponse {
+ instances: GerritInstanceStatus[]
+}
+
+// --- Extension to existing IntegrationsStatus ---
+
+// Add to existing IntegrationsStatus type:
+// gerrit: {
+// instances: GerritInstanceStatus[]
+// }
diff --git a/specs/001-gerrit-integration/contracts/gerrit-api.yaml b/specs/001-gerrit-integration/contracts/gerrit-api.yaml
new file mode 100644
index 000000000..5c8f05509
--- /dev/null
+++ b/specs/001-gerrit-integration/contracts/gerrit-api.yaml
@@ -0,0 +1,266 @@
+openapi: 3.0.3
+info:
+ title: Gerrit Integration API
+ description: API endpoints for the Gerrit integration connector in Ambient Code Platform
+ version: 1.0.0
+
+security:
+ - bearerAuth: []
+
+paths:
+ /api/auth/gerrit/connect:
+ post:
+ summary: Connect a Gerrit instance
+ description: >
+ Validates and stores Gerrit credentials for the authenticated user.
+ Supports both HTTP basic auth and gitcookies authentication methods.
+ operationId: connectGerrit
+ tags: [Gerrit Integration]
+ requestBody:
+ required: true
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/GerritConnectRequest'
+ responses:
+ '200':
+ description: Successfully connected
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/GerritConnectResponse'
+ '400':
+ description: Invalid request (missing fields, invalid URL, etc.)
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+ '401':
+ description: Gerrit credentials are invalid (validation failed)
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+
+ /api/auth/gerrit/{instanceName}/status:
+ get:
+ summary: Get Gerrit instance connection status
+ operationId: getGerritStatus
+ tags: [Gerrit Integration]
+ parameters:
+ - name: instanceName
+ in: path
+ required: true
+ schema:
+ type: string
+ pattern: '^[a-z0-9][a-z0-9-]{0,61}[a-z0-9]$'
+ responses:
+ '200':
+ description: Connection status
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/GerritStatusResponse'
+
+ /api/auth/gerrit/{instanceName}/disconnect:
+ delete:
+ summary: Disconnect a Gerrit instance
+ operationId: disconnectGerrit
+ tags: [Gerrit Integration]
+ parameters:
+ - name: instanceName
+ in: path
+ required: true
+ schema:
+ type: string
+ responses:
+ '200':
+ description: Successfully disconnected
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/MessageResponse'
+ '404':
+ description: Instance not found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+
+ /api/auth/gerrit/test:
+ post:
+ summary: Test Gerrit credentials without storing
+ operationId: testGerritConnection
+ tags: [Gerrit Integration]
+ requestBody:
+ required: true
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/GerritConnectRequest'
+ responses:
+ '200':
+ description: Validation result
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/GerritTestResponse'
+
+ /api/auth/gerrit/instances:
+ get:
+ summary: List all connected Gerrit instances for the user
+ operationId: listGerritInstances
+ tags: [Gerrit Integration]
+ responses:
+ '200':
+ description: List of connected instances
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/GerritInstancesListResponse'
+
+ /api/projects/{projectName}/agentic-sessions/{sessionName}/credentials/gerrit:
+ get:
+ summary: Fetch Gerrit credentials for a session (runner use)
+ description: >
+ Returns all Gerrit credentials for the session owner. Used by the runner
+ at session startup to generate the gerrit_config.json file.
+ operationId: getGerritCredentialsForSession
+ tags: [Session Credentials]
+ parameters:
+ - name: projectName
+ in: path
+ required: true
+ schema:
+ type: string
+ - name: sessionName
+ in: path
+ required: true
+ schema:
+ type: string
+ responses:
+ '200':
+ description: Gerrit credentials for the session user
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/GerritSessionCredentialsResponse'
+ '403':
+ description: Access denied (RBAC check failed)
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+
+components:
+ securitySchemes:
+ bearerAuth:
+ type: http
+ scheme: bearer
+ description: JWT bearer token from platform authentication
+ schemas:
+ GerritConnectRequest:
+ type: object
+ required: [instanceName, url, authMethod]
+ properties:
+ instanceName:
+ type: string
+ description: User-assigned name for this instance
+ pattern: '^[a-z0-9][a-z0-9-]{0,61}[a-z0-9]$'
+ example: openstack
+ url:
+ type: string
+ format: uri
+ description: Gerrit instance base URL
+ example: https://review.opendev.org
+ authMethod:
+ type: string
+ enum: [http_basic, git_cookies]
+ description: Authentication method
+ username:
+ type: string
+ description: Gerrit username (required for http_basic)
+ httpToken:
+ type: string
+ description: HTTP password/access token (required for http_basic)
+ gitcookiesContent:
+ type: string
+ description: Raw gitcookies file content (required for git_cookies)
+
+ GerritConnectResponse:
+ type: object
+ properties:
+ message:
+ type: string
+ example: Gerrit instance 'openstack' connected successfully
+ instanceName:
+ type: string
+ url:
+ type: string
+ authMethod:
+ type: string
+
+ GerritStatusResponse:
+ type: object
+ properties:
+ connected:
+ type: boolean
+ instanceName:
+ type: string
+ url:
+ type: string
+ authMethod:
+ type: string
+ updatedAt:
+ type: string
+ format: date-time
+
+ GerritTestResponse:
+ type: object
+ properties:
+ valid:
+ type: boolean
+ error:
+ type: string
+ description: Error message if validation failed
+
+ GerritInstancesListResponse:
+ type: object
+ properties:
+ instances:
+ type: array
+ items:
+ $ref: '#/components/schemas/GerritStatusResponse'
+
+ GerritSessionCredentialsResponse:
+ type: object
+ properties:
+ instances:
+ type: array
+ items:
+ type: object
+ properties:
+ instanceName:
+ type: string
+ url:
+ type: string
+ authMethod:
+ type: string
+ username:
+ type: string
+ httpToken:
+ type: string
+ gitcookiesContent:
+ type: string
+
+ MessageResponse:
+ type: object
+ properties:
+ message:
+ type: string
+
+ ErrorResponse:
+ type: object
+ properties:
+ error:
+ type: string
diff --git a/specs/001-gerrit-integration/data-model.md b/specs/001-gerrit-integration/data-model.md
new file mode 100644
index 000000000..a204f88d2
--- /dev/null
+++ b/specs/001-gerrit-integration/data-model.md
@@ -0,0 +1,107 @@
+# Data Model: Gerrit Integration Connector
+
+**Branch**: `001-gerrit-integration` | **Date**: 2026-03-24
+
+## Entities
+
+### GerritCredentials
+
+Represents a user's connection to a single Gerrit instance.
+
+| Field | Type | Required | Description |
+|--------------------|-----------|----------|--------------------------------------------------------------|
+| userID | string | Yes | Platform user identifier (from auth context) |
+| instanceName | string | Yes | User-assigned name for this Gerrit instance (e.g., "openstack") |
+| url | string | Yes | Gerrit instance base URL (e.g., `https://review.opendev.org`) |
+| authMethod | enum | Yes | One of: `http_basic`, `git_cookies` |
+| username | string | Conditional | Required when authMethod is `http_basic` |
+| httpToken | string | Conditional | HTTP password/access token. Required when authMethod is `http_basic` |
+| gitcookiesContent | string | Conditional | Raw gitcookies file content. Required when authMethod is `git_cookies` |
+| updatedAt | timestamp | Yes | Last modification time (RFC3339) |
+
+**Identity**: Unique by `(instanceName, userID)`.
+
+**Validation rules**:
+- `url` must be a valid HTTPS URL (HTTP allowed for local development only)
+- `instanceName` must match `^[a-z0-9][a-z0-9-]{0,61}[a-z0-9]$` (lowercase, alphanumeric with hyphens, 2-63 chars)
+- When `authMethod` is `http_basic`: both `username` and `httpToken` must be non-empty
+- When `authMethod` is `git_cookies`: `gitcookiesContent` must be non-empty and contain at least one cookie line for the target host
+
+### GerritIntegrationStatus
+
+Read-only projection used for status reporting. Not stored separately.
+
+| Field | Type | Description |
+|---------------|---------|--------------------------------------------------|
+| connected | boolean | Whether credentials exist for this instance |
+| instanceName | string | User-assigned name |
+| url | string | Gerrit instance URL |
+| authMethod | string | Authentication method in use |
+| updatedAt | string | When credentials were last updated |
+
+## Storage
+
+### Kubernetes Secret: `gerrit-credentials-{userID}` (per-user)
+
+| Aspect | Value |
+|-----------------|----------------------------------------------|
+| Secret name | `gerrit-credentials-{userID}` |
+| Namespace | Platform namespace (same as other creds) |
+| Labels | `app: ambient-code`, `ambient-code.io/provider: gerrit` |
+| Data key format | `{instanceName}` |
+| Data value | JSON-marshaled `GerritCredentials` |
+
+**Per-user sharding**: Each user gets their own Secret, avoiding cross-user contention and unbounded Secret growth. A user with two Gerrit instances ("openstack" and "android") has two entries in their Secret `gerrit-credentials-user123`:
+- Key: `openstack` → Value: `{"userID":"user123","instanceName":"openstack","url":"https://review.opendev.org",...}`
+- Key: `android` → Value: `{"userID":"user123","instanceName":"android","url":"https://android-review.googlesource.com",...}`
+
+## Runtime Configuration
+
+### Generated `gerrit_config.json`
+
+At session startup, the runner generates this file from fetched credentials:
+
+```json
+{
+ "default_gerrit_base_url": "https://review.opendev.org/",
+ "gerrit_hosts": [
+ {
+ "name": "openstack",
+ "external_url": "https://review.opendev.org/",
+ "authentication": {
+ "type": "http_basic",
+ "username": "user",
+ "auth_token": "token"
+ }
+ },
+ {
+ "name": "android",
+ "external_url": "https://android-review.googlesource.com/",
+ "authentication": {
+ "type": "git_cookies",
+ "gitcookies_path": "/tmp/gerrit-mcp/.gitcookies"
+ }
+ }
+ ]
+}
+```
+
+For `git_cookies` auth, the runner also writes the gitcookies content to a temporary file and references its path in the config.
+
+## State Transitions
+
+```
+[Not Connected] --connect()--> [Connected]
+[Connected] --disconnect()--> [Not Connected]
+[Connected] --connect() with same instanceName--> [Connected] (credentials updated)
+```
+
+No intermediate states. Connection is synchronous (validate + store).
+
+## Relationships
+
+```
+User (1) ---has many---> GerritCredentials (N)
+GerritCredentials (N) ---generates at runtime---> gerrit_config.json (1 per session)
+gerrit_config.json (1) ---consumed by---> Gerrit MCP Server (1 per session)
+```
diff --git a/specs/001-gerrit-integration/plan.md b/specs/001-gerrit-integration/plan.md
new file mode 100644
index 000000000..1eabe24da
--- /dev/null
+++ b/specs/001-gerrit-integration/plan.md
@@ -0,0 +1,140 @@
+# Implementation Plan: Gerrit Integration Connector
+
+**Branch**: `001-gerrit-integration` | **Date**: 2026-03-24 | **Spec**: [spec.md](spec.md)
+**Input**: Feature specification from `/specs/001-gerrit-integration/spec.md`
+
+## Summary
+
+Add Gerrit as a first-class integration connector in Ambient Code Platform, enabling users to connect their Gerrit code review instances and use AI-assisted code review workflows through the open-source Gerrit MCP server. The implementation follows existing integration patterns (Jira, GitLab) with a dedicated backend handler, frontend connection card, and runner-side credential injection that generates the Gerrit MCP server's native `gerrit_config.json` at runtime.
+
+## Technical Context
+
+**Language/Version**: Go 1.22+ (backend/operator), TypeScript/React (frontend), Python 3.12+ (runner, Gerrit MCP server)
+**Primary Dependencies**: Gin (Go HTTP), React/Shadcn (frontend), gerrit-mcp-server (MCP tools), mcp SDK (Python)
+**Storage**: Kubernetes Secrets (credential storage, same pattern as existing integrations)
+**Testing**: `go test` (backend), Vitest (frontend), pytest (runner)
+**Target Platform**: Kubernetes (Linux containers)
+**Project Type**: Multi-component platform (backend + frontend + runner + operator)
+**Performance Goals**: Credential validation < 30 seconds, MCP tool invocations complete within Gerrit API response times
+**Constraints**: No new CRDs, no new K8s operators; fits within existing integration framework
+**Scale/Scope**: Support multiple Gerrit instances per user, standard platform user base
+
+## Constitution Check
+
+*GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.*
+
+No project-specific constitution defined. Default engineering principles apply:
+- No `panic()` in production Go code
+- No `any` types in frontend TypeScript
+- User token auth for all user-facing API operations
+- OwnerReferences on all child K8s resources
+- Conventional commits
+
+**Pre-design gate**: PASS (no violations)
+**Post-design gate**: PASS (design follows all existing patterns, no new abstractions introduced)
+
+## Project Structure
+
+### Documentation (this feature)
+
+```text
+specs/001-gerrit-integration/
+├── plan.md # This file
+├── research.md # Phase 0: Technology research and decisions
+├── data-model.md # Phase 1: Entity definitions and storage design
+├── quickstart.md # Phase 1: Development setup guide
+├── contracts/
+│ ├── gerrit-api.yaml # Phase 1: OpenAPI contract
+│ └── frontend-types.ts # Phase 1: TypeScript type definitions
+└── tasks.md # Phase 2: Task breakdown (created by /speckit.tasks)
+```
+
+### Source Code (repository root)
+
+```text
+components/backend/
+├── handlers/
+│ ├── gerrit_auth.go # NEW: Connect/disconnect/status/test handlers
+│ ├── integrations_status.go # MODIFY: Add Gerrit to unified status
+│ ├── integration_validation.go # MODIFY: Add ValidateGerritToken()
+│ └── runtime_credentials.go # MODIFY: Add session-scoped Gerrit fetch
+└── routes.go # MODIFY: Register Gerrit routes
+
+components/frontend/src/
+├── components/
+│ └── gerrit-connection-card.tsx # NEW: Connection card with auth method toggle
+├── services/
+│ ├── api/
+│ │ ├── gerrit-auth.ts # NEW: API client functions
+│ │ └── integrations.ts # MODIFY: Add Gerrit to status type
+│ └── queries/
+│ └── use-gerrit.ts # NEW: React Query hooks
+└── app/integrations/
+ └── IntegrationsClient.tsx # MODIFY: Add GerritConnectionCard to grid
+
+components/runners/ambient-runner/
+├── .mcp.json # MODIFY: Add Gerrit MCP server entry
+├── Dockerfile # MODIFY: Bundle Gerrit MCP server
+└── ambient_runner/
+ ├── platform/
+ │ └── auth.py # MODIFY: Add Gerrit credential fetching
+ └── bridges/claude/
+ └── mcp.py # MODIFY: Add Gerrit config generation + auth check
+```
+
+**Structure Decision**: This feature adds files across three existing components (backend, frontend, runner) following the established integration pattern. No new components or services are introduced. The Gerrit MCP server is bundled into the runner image at build time.
+
+## Design Decisions
+
+### D1: Dedicated Handler vs Generic MCP Credentials
+
+**Chosen**: Dedicated handler (`gerrit_auth.go`) following Jira/GitLab pattern.
+
+**Why**: Gerrit requires auth method selection (HTTP vs gitcookies), dedicated validation against the Gerrit REST API, and a custom connection card UI. The generic MCP pattern (`map[string]string` fields) lacks typed validation, auth method switching, and multi-instance listing.
+
+### D2: Runtime Config File Generation
+
+**Chosen**: Runner generates `gerrit_config.json` at session startup from fetched credentials.
+
+**Why**: The Gerrit MCP server reads configuration from a JSON file (configurable via `GERRIT_CONFIG_PATH`), not from environment variables. This is different from Jira/GitLab which use env vars directly. The runner must:
+1. Fetch all Gerrit credentials for the user from the backend
+2. Generate a `gerrit_config.json` with one `gerrit_hosts` entry per connected instance
+3. For gitcookies auth, write cookie content to a temp file and reference its path
+4. Set `GERRIT_CONFIG_PATH` before launching the MCP server
+
+### D3: Multi-Instance Secret Key Format
+
+**Chosen**: `{instanceName}:{userID}` keys within a single `gerrit-credentials` secret.
+
+**Why**: Consistent with the MCP credentials pattern (`serverName:userID`). Allows listing all instances for a user by scanning keys ending with `:userID`. Single secret simplifies cleanup and backup.
+
+### D4: Credential Validation Endpoint
+
+**Chosen**: `GET /a/accounts/self` on the Gerrit REST API.
+
+**Why**: Lightest authenticated endpoint. Returns the authenticated user's account info. Works with both HTTP basic and gitcookies auth. Returns 401 for invalid credentials. Must handle Gerrit's XSSI protection prefix (`)]}'`).
+
+### D5: Gerrit MCP Server Bundling
+
+**Chosen**: Clone at pinned commit and build in Dockerfile.
+
+**Why**: Not published on PyPI, cannot use `uvx`. Build script creates a venv with hash-verified dependencies. Pinning to a specific commit ensures reproducibility.
+
+## Component Interaction Flow
+
+```
+[User] → [Frontend: GerritConnectionCard]
+ → POST /api/auth/gerrit/connect
+ → [Backend: ConnectGerrit handler]
+ → ValidateGerritToken (GET /a/accounts/self on Gerrit instance)
+ → Store in K8s Secret (gerrit-credentials)
+ → Return success
+
+[User creates session] → [Operator creates Job]
+ → [Runner startup]
+ → fetch_gerrit_credentials() from backend API
+ → Generate gerrit_config.json + gitcookies temp file
+ → Set GERRIT_CONFIG_PATH env var
+ → Launch Gerrit MCP server in STDIO mode
+ → Agent has access to 21 Gerrit tools
+```
diff --git a/specs/001-gerrit-integration/quickstart.md b/specs/001-gerrit-integration/quickstart.md
new file mode 100644
index 000000000..d6460e02f
--- /dev/null
+++ b/specs/001-gerrit-integration/quickstart.md
@@ -0,0 +1,94 @@
+# Quickstart: Gerrit Integration Connector
+
+**Branch**: `001-gerrit-integration` | **Date**: 2026-03-24
+
+## Prerequisites
+
+- Local Kind cluster running (`make kind-up`)
+- Platform deployed (`make deploy`)
+- Access to a Gerrit instance for testing (e.g., `https://review.opendev.org`)
+- Gerrit HTTP credentials or gitcookies for your account
+
+## Development Setup
+
+### 1. Backend (Go)
+
+```bash
+cd components/backend
+# After adding new handler files:
+go vet ./...
+gofmt -l .
+go build ./...
+```
+
+### 2. Frontend (TypeScript)
+
+```bash
+cd components/frontend
+npm install
+npm run dev # http://localhost:3000
+```
+
+### 3. Runner (Python)
+
+```bash
+cd components/runners/ambient-runner
+uv venv && uv pip install -e .
+
+# Clone and build Gerrit MCP server locally for testing:
+git clone https://gerrit.googlesource.com/gerrit-mcp-server /tmp/gerrit-mcp-server
+cd /tmp/gerrit-mcp-server && ./build-gerrit.sh
+```
+
+## Key Files to Modify
+
+### Backend
+| File | Action |
+|------|--------|
+| `handlers/gerrit_auth.go` | **Create** - Connect/disconnect/status/test handlers |
+| `handlers/integrations_status.go` | **Modify** - Add Gerrit to unified status |
+| `handlers/integration_validation.go` | **Modify** - Add `ValidateGerritToken()` |
+| `handlers/runtime_credentials.go` | **Modify** - Add session-scoped Gerrit credential fetch |
+| `routes.go` | **Modify** - Register Gerrit routes |
+
+### Frontend
+| File | Action |
+|------|--------|
+| `components/gerrit-connection-card.tsx` | **Create** - Connection UI with auth method toggle |
+| `services/api/gerrit-auth.ts` | **Create** - API client functions |
+| `services/queries/use-gerrit.ts` | **Create** - React Query hooks |
+| `services/api/integrations.ts` | **Modify** - Add Gerrit to IntegrationsStatus type |
+| `app/integrations/IntegrationsClient.tsx` | **Modify** - Add GerritConnectionCard to grid |
+
+### Runner
+| File | Action |
+|------|--------|
+| `.mcp.json` | **Modify** - Add Gerrit MCP server entry |
+| `ambient_runner/platform/auth.py` | **Modify** - Add Gerrit credential fetching |
+| `ambient_runner/bridges/claude/mcp.py` | **Modify** - Add Gerrit config generation and auth check |
+| `Dockerfile` | **Modify** - Bundle Gerrit MCP server |
+
+## Testing the Integration
+
+### Manual Test Flow
+
+1. Start the platform locally
+2. Navigate to Integrations page
+3. Click "Connect Gerrit"
+4. Enter instance name, URL, and credentials
+5. Verify "Connected" status appears
+6. Create a new session
+7. Ask the agent: "List my open changes on Gerrit"
+8. Verify the agent returns results from the Gerrit instance
+
+### Credential Validation Test
+
+```bash
+# Test HTTP basic auth against Gerrit REST API:
+curl -u "username:http_password" "https://review.opendev.org/a/accounts/self" | tail -c +5 | jq .
+
+# Test with gitcookies:
+curl -b ~/.gitcookies "https://review.opendev.org/a/accounts/self" | tail -c +5 | jq .
+```
+
+Note: Gerrit REST API responses are prefixed with `)]}'` (XSSI protection). The `tail -c +5` strips this prefix.
diff --git a/specs/001-gerrit-integration/research.md b/specs/001-gerrit-integration/research.md
new file mode 100644
index 000000000..2dfbf4c0d
--- /dev/null
+++ b/specs/001-gerrit-integration/research.md
@@ -0,0 +1,136 @@
+# Research: Gerrit Integration Connector
+
+**Branch**: `001-gerrit-integration` | **Date**: 2026-03-24
+
+## R1: Gerrit MCP Server Capabilities & Configuration
+
+**Decision**: Use the open-source Gerrit MCP server from `gerrit.googlesource.com/gerrit-mcp-server` as-is, bundled into the runner image.
+
+**Rationale**: The project is maintained by the Gerrit community, exposes 21 MCP tools covering all code review operations needed (query, review, comment, manage), and supports the two authentication methods required (HTTP basic and gitcookies). Building from source is straightforward with a single shell script.
+
+**Alternatives considered**:
+- Community npm package (`@hicho-lge/gerrit-mcp`): Less mature, fewer tools, not maintained by Gerrit project.
+- Building a custom MCP server: Unnecessary duplication of effort; the official server already covers all required operations.
+
+### Key Technical Details
+
+**21 MCP Tools Available**: `query_changes`, `query_changes_by_date_and_filters`, `get_change_details`, `get_commit_message`, `list_change_files`, `get_file_diff`, `list_change_comments`, `get_bugs_from_cl`, `get_most_recent_cl`, `suggest_reviewers`, `add_reviewer`, `set_ready_for_review`, `set_work_in_progress`, `post_review_comment`, `abandon_change`, `set_topic`, `create_change`, `revert_change`, `revert_submission`, `changes_submitted_together`.
+
+**Configuration**: Uses `gerrit_config.json` file (path overrideable via `GERRIT_CONFIG_PATH` env var):
+```json
+{
+ "default_gerrit_base_url": "https://review.opendev.org/",
+ "gerrit_hosts": [
+ {
+ "name": "OpenStack",
+ "external_url": "https://review.opendev.org/",
+ "authentication": {
+ "type": "http_basic",
+ "username": "user",
+ "auth_token": "token"
+ }
+ }
+ ]
+}
+```
+
+**Authentication types**: `http_basic` (username + auth_token) and `git_cookies` (gitcookies_path). A third type `gob_curl` exists but is Google-internal only — out of scope.
+
+**STDIO launch**: `.venv/bin/python gerrit_mcp_server/main.py stdio` with `PYTHONPATH` set to project root.
+
+**Dependencies**: Python 3.12+, `mcp`, `uvicorn`, `websockets`, system `curl`.
+
+**Not on PyPI**: Must be built from source via `./build-gerrit.sh` (creates venv, installs deps with hash verification).
+
+---
+
+## R2: Platform Integration Architecture Patterns
+
+**Decision**: Use a dedicated backend handler pattern (like Jira/GitLab) rather than the generic MCP credentials pattern.
+
+**Rationale**: Gerrit requires a specialized connection card UI (auth method selector between HTTP credentials and gitcookies), dedicated credential validation against the Gerrit REST API, and custom runtime config file generation (`gerrit_config.json`). The generic MCP pattern (`map[string]string` fields) is too simple for this.
+
+**Alternatives considered**:
+- Generic MCP credentials (`POST /api/auth/mcp/:serverName/connect`): Simpler but no auth method selection UI, no dedicated validation, no typed fields.
+- Hybrid approach: Store via generic MCP but add custom validation. Adds complexity without benefit.
+
+### Platform Patterns to Follow
+
+**Backend (Go)**:
+- Credential struct: `GerritCredentials { UserID, URL, AuthMethod, Username, HTTPToken, GitcookiesContent, UpdatedAt }`
+- Secret name: `gerrit-credentials` with label `ambient-code.io/provider: gerrit`
+- Three core endpoints: `ConnectGerrit`, `GetGerritStatus`, `DisconnectGerrit`, plus `TestGerritConnection`
+- Session-scoped credential fetch: `GET /api/projects/:project/agentic-sessions/:session/credentials/gerrit`
+- Validation: Test request to Gerrit REST API `GET /a/accounts/self` with provided credentials
+- Conflict retry pattern: 3 retries on K8s optimistic locking conflicts
+
+**Frontend (React/TypeScript)**:
+- `GerritConnectionCard` component with auth method toggle (HTTP credentials vs gitcookies paste)
+- `use-gerrit.ts` React Query hooks (connect/disconnect mutations with query invalidation)
+- `gerrit-auth.ts` API client functions
+- Add to `IntegrationsClient.tsx` grid layout
+
+**Runner (Python)**:
+- Cannot use simple env var expansion like Jira — Gerrit MCP server requires a `gerrit_config.json` file
+- Runtime flow: fetch credentials from backend → generate `gerrit_config.json` → set `GERRIT_CONFIG_PATH` → launch MCP server
+- Add `fetch_gerrit_credentials()` to `auth.py`
+- Add Gerrit config generation to `mcp.py` or a new `gerrit.py` helper
+- Add `check_mcp_authentication()` case for Gerrit server
+- Register in `.mcp.json` with custom command pointing to bundled Gerrit MCP server
+
+**Integrations Status**:
+- Add `gerrit` field to unified status response in `integrations_status.go`
+- Add `gerrit` type to frontend `IntegrationsStatus` TypeScript type
+
+---
+
+## R3: Gerrit Credential Validation
+
+**Decision**: Validate by calling `GET /a/accounts/self` on the Gerrit REST API.
+
+**Rationale**: This is the lightest authenticated endpoint on Gerrit. It returns the authenticated user's account info. Works with both HTTP basic auth and gitcookies. Returns 401 on invalid credentials.
+
+**Alternatives considered**:
+- `GET /a/changes/?q=limit:1`: Heavier, may return results requiring parsing.
+- No validation: User experience suffers if invalid credentials are silently accepted.
+
+### Validation Details
+
+**HTTP Basic**: Standard HTTP Basic Auth header with `username:http_password`.
+
+**Gitcookies**: Parse the pasted content to extract the cookie for the target host, send as `Cookie` header. The gitcookies format is: `host\tFALSE\t/\tTRUE\t2147483647\to\tgit-user.cookies=value`.
+
+**Gerrit REST API note**: Gerrit prepends `)]}'` to JSON responses as an XSSI protection prefix. The validation must strip this before parsing.
+
+---
+
+## R4: Runner Bundling Strategy
+
+**Decision**: Clone and build the Gerrit MCP server into the runner Docker image at build time.
+
+**Rationale**: The Gerrit MCP server is not on PyPI, so it cannot be installed via `uvx` or `pip install`. Building at image build time ensures reproducible, hash-verified dependencies and avoids runtime network dependencies.
+
+**Alternatives considered**:
+- Runtime clone + build: Slow, unreliable, requires network access from runner pods.
+- Fork and publish to PyPI: Maintenance burden, diverges from upstream.
+- Vendoring source directly: Works but harder to update; Git clone with pinned tag is cleaner.
+
+### Bundling Approach
+
+In the runner Dockerfile:
+1. Clone `gerrit-mcp-server` at a pinned tag/commit
+2. Run `./build-gerrit.sh` to create venv with verified deps
+3. Copy built venv + source to final image
+4. Configure `.mcp.json` entry pointing to the bundled installation
+
+---
+
+## R5: Multi-Instance Support
+
+**Decision**: Store each Gerrit instance as a separate entry in the `gerrit-credentials` K8s secret, keyed by `instanceName:userID`.
+
+**Rationale**: Consistent with how the platform handles multi-value secrets. At runtime, the runner fetches all Gerrit credentials for the user and generates a single `gerrit_config.json` with multiple `gerrit_hosts` entries — leveraging the Gerrit MCP server's native multi-host support.
+
+**Alternatives considered**:
+- One secret per instance: More K8s objects to manage, harder to list all instances for a user.
+- Single credential only (no multi-instance): Limits users contributing to multiple Gerrit communities.
diff --git a/specs/001-gerrit-integration/spec.md b/specs/001-gerrit-integration/spec.md
new file mode 100644
index 000000000..7f8918abd
--- /dev/null
+++ b/specs/001-gerrit-integration/spec.md
@@ -0,0 +1,138 @@
+# Feature Specification: Gerrit Integration Connector
+
+**Feature Branch**: `001-gerrit-integration`
+**Created**: 2026-03-24
+**Status**: Draft
+**Input**: User description: "Add a Gerrit integration connector for AmbientCodePlatform, leveraging the existing open-source Gerrit MCP server from the Gerrit community (https://gerrit.googlesource.com/gerrit-mcp-server) to support communities like OpenStack that rely exclusively on Gerrit for code reviews."
+
+## Clarifications
+
+### Session 2026-03-24
+
+- Q: How should users provide their gitcookies credentials in the UI? → A: Paste gitcookies content into a text field (consistent with how other integrations accept tokens).
+- Q: Should Gerrit write operations be enabled by default or require opt-in? → A: All operations (read and write) enabled by default, consistent with GitHub/Jira/GitLab integrations.
+- Q: When multiple Gerrit instances are connected, should all be available in every session? → A: Yes, all connected instances are automatically available in every session.
+
+## User Scenarios & Testing *(mandatory)*
+
+### User Story 1 - Connect a Gerrit Instance (Priority: P1)
+
+A platform user navigates to the Integrations page and connects their Gerrit code review instance by providing their Gerrit server URL and authentication credentials. Once connected, the integration status shows as active and the user's agentic sessions can interact with Gerrit.
+
+**Why this priority**: Without a working connection, no Gerrit functionality is available. This is the foundation for all other Gerrit interactions.
+
+**Independent Test**: Can be fully tested by navigating to Integrations, filling in Gerrit credentials, and verifying the connection status indicator shows "connected." Delivers the value of establishing a verified link to a Gerrit instance.
+
+**Acceptance Scenarios**:
+
+1. **Given** a user is on the Integrations page, **When** they select an authentication method (HTTP credentials or gitcookies), enter a valid Gerrit server URL and the corresponding credentials, and click Connect, **Then** the system validates the credentials against the Gerrit instance and displays a "Connected" status.
+2. **Given** a user provides invalid Gerrit credentials, **When** they attempt to connect, **Then** the system displays a clear error message indicating the credentials are invalid.
+3. **Given** a user has a connected Gerrit integration, **When** they view the integrations status, **Then** Gerrit appears as an active integration with the server URL displayed.
+4. **Given** a user wants to disconnect Gerrit, **When** they click Disconnect, **Then** the integration is removed and future sessions no longer have Gerrit access.
+
+---
+
+### User Story 2 - Use Gerrit Tools in Agentic Sessions (Priority: P1)
+
+A platform user with a connected Gerrit integration creates an agentic session. The session automatically has access to Gerrit MCP tools, allowing the AI agent to query changes, review code, post comments, and manage code reviews on the user's behalf.
+
+**Why this priority**: This is the core value proposition - enabling AI-assisted code review workflows on Gerrit. Without this, the integration has no practical use.
+
+**Independent Test**: Can be tested by creating a session after connecting Gerrit, then asking the agent to query open changes or retrieve a specific change's details. Delivers the value of AI-powered Gerrit interaction.
+
+**Acceptance Scenarios**:
+
+1. **Given** a user has connected Gerrit credentials, **When** they start a new agentic session, **Then** the Gerrit MCP tools are available to the AI agent within that session.
+2. **Given** an active session with Gerrit tools, **When** the agent is asked to list open changes for a project, **Then** the agent uses the Gerrit query tool and returns results from the connected Gerrit instance.
+3. **Given** an active session with Gerrit tools, **When** the agent is asked to review a specific change, **Then** the agent can retrieve the change details, view file diffs, and read existing comments.
+4. **Given** a user has NOT connected Gerrit, **When** they start a session, **Then** Gerrit tools are not available and the agent does not attempt Gerrit operations.
+
+---
+
+### User Story 3 - View Gerrit Integration Status Per Session (Priority: P2)
+
+A platform user viewing an active session can see whether the Gerrit integration is connected and available for that session. This gives visibility into which tools the agent has access to.
+
+**Why this priority**: Provides transparency and troubleshooting capability, but sessions still function without this visibility.
+
+**Independent Test**: Can be tested by viewing the session settings/integrations panel and confirming Gerrit status is accurately reflected. Delivers the value of integration transparency.
+
+**Acceptance Scenarios**:
+
+1. **Given** a user has connected Gerrit and starts a session, **When** they view the session's integrations panel, **Then** Gerrit shows as "Connected" with the server URL.
+2. **Given** a user has NOT connected Gerrit, **When** they view the session's integrations panel, **Then** Gerrit shows as "Not connected" with a link to the Integrations page.
+
+---
+
+### User Story 4 - Connect Multiple Gerrit Instances (Priority: P3)
+
+A platform user who contributes to multiple Gerrit-hosted projects (e.g., OpenStack on review.opendev.org and Android on android-review.googlesource.com) can connect multiple Gerrit instances and specify which one to use in their sessions.
+
+**Why this priority**: Valuable for power users contributing to multiple communities, but the majority of users will work with a single Gerrit instance.
+
+**Independent Test**: Can be tested by connecting two different Gerrit servers, starting a session, and directing the agent to query changes from each instance. Delivers the value of multi-community collaboration.
+
+**Acceptance Scenarios**:
+
+1. **Given** a user has connected two Gerrit instances, **When** they start a session, **Then** all connected instances are automatically available and the agent can interact with both without per-session selection.
+2. **Given** a user has multiple Gerrit instances, **When** they view the Integrations page, **Then** each instance is listed separately with its own status and disconnect option.
+
+---
+
+### Edge Cases
+
+- What happens when a user's Gerrit credentials expire or are revoked mid-session? The session should gracefully report that the Gerrit tools are unavailable and suggest re-authenticating.
+- What happens when the Gerrit server is unreachable? The agent should report connectivity issues without crashing the session.
+- What happens when a user connects a Gerrit instance that requires a specific authentication method not supported? The system should display a clear error about unsupported authentication.
+- How does the system handle Gerrit instances behind corporate firewalls or VPNs? The system should attempt connection and report clear timeout/unreachable errors.
+
+## Requirements *(mandatory)*
+
+### Functional Requirements
+
+- **FR-001**: System MUST allow users to connect a Gerrit instance by providing a server URL and authentication credentials from the Integrations page.
+- **FR-002**: System MUST support two authentication methods: (a) HTTP credentials (username and HTTP password/access token generated from the Gerrit instance), and (b) gitcookies content pasted into a text field, which is the authentication method used by many Gerrit-based communities including OpenStack.
+- **FR-003**: System MUST validate Gerrit credentials upon connection by making a test request to the Gerrit instance.
+- **FR-004**: System MUST store Gerrit credentials securely, scoped to the individual user, following the same credential isolation patterns used by existing integrations.
+- **FR-005**: System MUST expose Gerrit MCP tools to agentic sessions when the user has a connected Gerrit integration.
+- **FR-006**: System MUST provide Gerrit integration status on both the global Integrations page and per-session integrations panel.
+- **FR-007**: System MUST allow users to disconnect a Gerrit integration, removing stored credentials.
+- **FR-008**: System MUST support the Gerrit MCP server's full capabilities with both read and write operations enabled by default: querying changes, viewing change details and diffs, posting review comments, managing reviewers, and change lifecycle operations (abandon, WIP, ready for review).
+- **FR-009**: System MUST inject Gerrit credentials into the runner environment at session startup so the Gerrit MCP server can authenticate with the Gerrit instance.
+- **FR-010**: System MUST support connecting multiple Gerrit instances per user, each identified by a unique name or server URL.
+
+### Key Entities
+
+- **Gerrit Integration**: A user's connection to a Gerrit code review instance, comprising a server URL, authentication credentials, and connection status.
+- **Gerrit MCP Server**: The open-source MCP server from the Gerrit community that translates MCP tool calls into Gerrit REST API requests. Bundled as part of the platform's runner environment.
+- **Gerrit Credentials**: User-scoped authentication data stored securely and fetched at session runtime. Comprises either (a) server URL, username, and HTTP password/access token, or (b) server URL and gitcookies file content.
+
+## Success Criteria *(mandatory)*
+
+### Measurable Outcomes
+
+- **SC-001**: Users can connect a Gerrit instance and see a confirmed "Connected" status within 30 seconds of providing valid credentials.
+- **SC-002**: Agentic sessions with a connected Gerrit integration can successfully query changes, retrieve change details, and post review comments.
+- **SC-003**: The Gerrit integration follows the same user experience patterns as existing integrations (GitHub, Jira, GitLab), requiring no additional learning curve for users already familiar with the platform.
+- **SC-004**: Users can disconnect and reconnect Gerrit integrations without affecting other integrations or active sessions.
+- **SC-005**: 95% of Gerrit tool invocations in sessions complete successfully when credentials are valid and the Gerrit server is reachable.
+
+## Assumptions
+
+- The open-source Gerrit MCP server from `gerrit.googlesource.com/gerrit-mcp-server` is suitable for bundling into the platform's runner environment.
+- Two authentication methods are supported: HTTP credentials (username + HTTP password/access token) and gitcookies file content. Both are widely used across Gerrit deployments.
+- The Gerrit MCP server will be run in STDIO mode within the runner pod, consistent with how other MCP servers (e.g., mcp-atlassian, google-workspace) are launched.
+- The platform's existing generic MCP server credential storage pattern (`mcp-server-credentials` secret with `serverName:userID` keying) can be extended to support Gerrit.
+- Users are responsible for obtaining their own credentials: either generating an HTTP password from their Gerrit instance's Settings page, or obtaining their gitcookies file content.
+
+## Dependencies
+
+- **Gerrit MCP Server**: The open-source project at `gerrit.googlesource.com/gerrit-mcp-server` must be compatible with the runner's Python environment.
+- **Existing Integration Framework**: This feature builds on the platform's existing MCP server integration patterns (backend credential storage, frontend connection cards, runner credential injection).
+
+## Out of Scope
+
+- OAuth or SSO-based authentication for Gerrit (only HTTP credentials and gitcookies are supported in v1).
+- Gerrit administrative operations (project creation, access control management, plugin management).
+- Hosting or proxying the Gerrit MCP server centrally - it runs within each session's runner pod.
+- Git push/fetch operations to Gerrit repositories (this integration covers the code review API, not Git transport).
diff --git a/specs/001-gerrit-integration/tasks.md b/specs/001-gerrit-integration/tasks.md
new file mode 100644
index 000000000..a7adad8ca
--- /dev/null
+++ b/specs/001-gerrit-integration/tasks.md
@@ -0,0 +1,218 @@
+# Tasks: Gerrit Integration Connector
+
+**Input**: Design documents from `/specs/001-gerrit-integration/`
+**Prerequisites**: plan.md (required), spec.md (required), research.md, data-model.md, contracts/
+
+**Tests**: Not explicitly requested in the feature specification. Test tasks are omitted.
+
+**Organization**: Tasks are grouped by user story to enable independent implementation and testing of each story.
+
+## Format: `[ID] [P?] [Story] Description`
+
+- **[P]**: Can run in parallel (different files, no dependencies)
+- **[Story]**: Which user story this task belongs to (e.g., US1, US2, US3)
+- Include exact file paths in descriptions
+
+## Path Conventions
+
+- **Backend**: `components/backend/`
+- **Frontend**: `components/frontend/src/`
+- **Runner**: `components/runners/ambient-runner/`
+
+---
+
+## Phase 1: Setup
+
+**Purpose**: Bundle the Gerrit MCP server into the runner image and establish the foundation for integration work.
+
+- [x] T001 Add Gerrit MCP server clone and build steps to runner Dockerfile at `components/runners/ambient-runner/Dockerfile` — clone `https://gerrit.googlesource.com/gerrit-mcp-server` at pinned commit `5666642afe1a5217e2529225d4bd9c9df6310bd6` (2026-03-23, master), run `./build-gerrit.sh`, copy built venv + source to final image under `/opt/gerrit-mcp-server/`
+
+---
+
+## Phase 2: Foundational (Blocking Prerequisites)
+
+**Purpose**: Backend credential storage and validation — MUST be complete before frontend or runner work.
+
+**CRITICAL**: No user story work can begin until this phase is complete.
+
+- [x] T002 Create `ValidateGerritToken()` function in `components/backend/handlers/integration_validation.go` — accepts URL, authMethod, username, httpToken, gitcookiesContent; makes `GET /a/accounts/self` request to the Gerrit instance using HTTP Basic Auth or gitcookies Cookie header; strips `)]}'` XSSI prefix from response; returns (bool, error) following the same pattern as `ValidateJiraToken()` and `ValidateGitLabToken()`
+- [x] T003 Create `components/backend/handlers/gerrit_auth.go` with GerritCredentials struct (UserID, InstanceName, URL, AuthMethod, Username, HTTPToken, GitcookiesContent, UpdatedAt), K8s Secret helpers (store/get/delete/list using `gerrit-credentials` secret with `{instanceName}:{userID}` key format and label `ambient-code.io/provider: gerrit`), and five HTTP handlers: `ConnectGerrit` (POST, validates via `ValidateGerritToken` then stores), `TestGerritConnection` (POST, validates without storing), `GetGerritStatus` (GET with `:instanceName` param), `DisconnectGerrit` (DELETE with `:instanceName` param), `ListGerritInstances` (GET, returns all instances for user) — follow `jira_auth.go` patterns for K8s client usage, conflict retry, and error handling
+- [x] T004 Add session-scoped Gerrit credential fetch handler `GetGerritCredentialsForSession` in `components/backend/handlers/runtime_credentials.go` — follows the same RBAC pattern as existing session credential endpoints; fetches ALL Gerrit instances for the effective user and returns them as a JSON array of instances with fields: instanceName, url, authMethod, username, httpToken, gitcookiesContent
+- [x] T005 Add Gerrit status to unified integrations status in `components/backend/handlers/integrations_status.go` — add `getGerritStatusForUser()` helper that lists all Gerrit instances for the user; include `gerrit` field in the response object with an `instances` array of `{connected, instanceName, url, authMethod, updatedAt}`
+- [x] T006 Register all Gerrit routes in `components/backend/routes.go` — cluster-level: `POST /api/auth/gerrit/connect`, `POST /api/auth/gerrit/test`, `GET /api/auth/gerrit/instances`, `GET /api/auth/gerrit/:instanceName/status`, `DELETE /api/auth/gerrit/:instanceName/disconnect`; session-scoped: `GET /api/projects/:projectName/agentic-sessions/:sessionName/credentials/gerrit`
+
+**Checkpoint**: Backend API is complete — Gerrit credentials can be stored, validated, listed, and fetched via session endpoints.
+
+---
+
+## Phase 3: User Story 1 — Connect a Gerrit Instance (Priority: P1) MVP
+
+**Goal**: Users can connect and disconnect Gerrit instances from the Integrations page with credential validation.
+
+**Independent Test**: Navigate to Integrations page, enter Gerrit credentials (HTTP basic or gitcookies), click Connect, verify "Connected" status appears. Disconnect and verify status clears.
+
+### Implementation for User Story 1
+
+- [x] T007 [P] [US1] Create API client functions in `components/frontend/src/services/api/gerrit-auth.ts` — export `connectGerrit(data: GerritConnectRequest)`, `testGerritConnection(data: GerritConnectRequest)`, `getGerritInstances()`, `getGerritInstanceStatus(instanceName: string)`, `disconnectGerrit(instanceName: string)` using the existing `apiClient` pattern from `jira-auth.ts`
+- [x] T008 [P] [US1] Add Gerrit types to `components/frontend/src/services/api/integrations.ts` — add `GerritAuthMethod`, `GerritInstanceStatus`, and `gerrit: { instances: GerritInstanceStatus[] }` field to the existing `IntegrationsStatus` type
+- [x] T009 [US1] Create React Query hooks in `components/frontend/src/services/queries/use-gerrit.ts` — export `useConnectGerrit()`, `useDisconnectGerrit()`, `useTestGerritConnection()` mutations with query invalidation on `['integrations', 'status']` and `['gerrit', 'instances']` keys; export `useGerritInstances()` query hook — follow `use-jira.ts` patterns
+- [x] T010 [US1] Create `components/frontend/src/components/gerrit-connection-card.tsx` — GerritConnectionCard component with: auth method toggle (radio/select: "HTTP Credentials" vs "Gitcookies"), conditional form fields (URL + username + HTTP token for http_basic; URL + gitcookies textarea for git_cookies), instance name input field, Connect/Test/Disconnect buttons, connection status indicator with server URL display, error/success toast messages — follow `jira-connection-card.tsx` layout and state patterns
+- [x] T011 [US1] Add GerritConnectionCard to integrations page grid in `components/frontend/src/app/integrations/IntegrationsClient.tsx` — import and render `` in the grid layout alongside existing cards, passing `status={integrations?.gerrit}` and `onRefresh={refetch}` props
+
+**Checkpoint**: User Story 1 complete — users can connect/disconnect Gerrit instances via the UI with real-time validation and status display.
+
+---
+
+## Phase 4: User Story 2 — Use Gerrit Tools in Agentic Sessions (Priority: P1)
+
+**Goal**: Sessions automatically have access to Gerrit MCP tools when the user has connected credentials.
+
+**Independent Test**: Connect Gerrit credentials, create a session, ask the agent to "list my open changes on Gerrit" and verify it returns results.
+
+### Implementation for User Story 2
+
+- [x] T012 [P] [US2] Add Gerrit MCP server entry to `components/runners/ambient-runner/.mcp.json` — add `"gerrit"` server with `command` pointing to bundled Python venv (`/opt/gerrit-mcp-server/.venv/bin/python`), `args` set to `["/opt/gerrit-mcp-server/gerrit_mcp_server/main.py", "stdio"]`, and `env` block with `PYTHONPATH: "/opt/gerrit-mcp-server/"` and `GERRIT_CONFIG_PATH: "${GERRIT_CONFIG_PATH}"`
+- [x] T013 [P] [US2] Add `fetch_gerrit_credentials()` function to `components/runners/ambient-runner/ambient_runner/platform/auth.py` — call `_fetch_credential(context, "gerrit")` to get all Gerrit instances from the backend session credential endpoint; return the parsed instances list
+- [x] T014 [US2] Add Gerrit config generation to `components/runners/ambient-runner/ambient_runner/bridges/claude/mcp.py` — implement `generate_gerrit_config(instances)` that: (1) creates `/tmp/gerrit-mcp/` directory, (2) for each instance with `git_cookies` auth, writes gitcookies content to `/tmp/gerrit-mcp/.gitcookies` temp file, (3) builds `gerrit_config.json` with `gerrit_hosts` array mapping each instance to the Gerrit MCP server's native config format (name, external_url, authentication with type/username/auth_token or gitcookies_path), (4) writes config to `/tmp/gerrit-mcp/gerrit_config.json`, (5) sets `GERRIT_CONFIG_PATH` env var; call this from `build_mcp_servers()` before loading MCP config
+- [x] T015 [US2] Add Gerrit auth check to `check_mcp_authentication()` in `components/runners/ambient-runner/ambient_runner/bridges/claude/mcp.py` — add case for server name `"gerrit"`: check if `GERRIT_CONFIG_PATH` is set and the config file exists; if yes return `(True, "Gerrit credentials configured")`; if not, attempt backend API fallback (same pattern as Jira); return `(False, "Gerrit not configured")` if no credentials found. Note: mid-session credential expiry is handled gracefully by the Gerrit MCP server itself (returns auth errors to the agent), so no additional error handling is needed in the runner
+- [x] T016 [US2] Add Gerrit to `populate_runtime_credentials()` in `components/runners/ambient-runner/ambient_runner/platform/auth.py` — add `fetch_gerrit_credentials(context)` to the `asyncio.gather()` call; if instances are returned, call `generate_gerrit_config(instances)` to write the config file before MCP server startup
+- [x] T017 [US2] Add Gerrit credential clearing to `clear_runtime_credentials()` in `components/runners/ambient-runner/ambient_runner/platform/auth.py` — remove `GERRIT_CONFIG_PATH` env var and delete `/tmp/gerrit-mcp/` directory (config file + gitcookies) after turn completes
+
+**Checkpoint**: User Story 2 complete — sessions with connected Gerrit credentials can query changes, view diffs, and post comments via the 21 Gerrit MCP tools.
+
+---
+
+## Phase 5: User Story 3 — View Gerrit Integration Status Per Session (Priority: P2)
+
+**Goal**: Users can see Gerrit connection status in the per-session integrations panel.
+
+**Independent Test**: Open a session's settings/integrations panel, verify Gerrit shows "Connected" with URL (or "Not connected" with link to Integrations page).
+
+### Implementation for User Story 3
+
+- [x] T018 [US3] Add Gerrit status display to the session integrations panel in `components/frontend/src/app/projects/[name]/sessions/[sessionName]/components/settings/integrations-panel.tsx` — add a Gerrit section that reads from the session's integration status; display "Connected" with instance URL(s) when Gerrit credentials exist, or "Not connected" with a link to `/integrations` when absent — follow the same display pattern used for Jira/GitLab in this panel
+
+**Checkpoint**: User Story 3 complete — session-level Gerrit status is visible.
+
+---
+
+## Phase 6: User Story 4 — Connect Multiple Gerrit Instances (Priority: P3)
+
+**Goal**: Users can connect multiple Gerrit instances and all are available in every session.
+
+**Independent Test**: Connect two different Gerrit servers (e.g., openstack + android), start a session, verify the agent can interact with both.
+
+### Implementation for User Story 4
+
+- [x] T019 [US4] Update `GerritConnectionCard` in `components/frontend/src/components/gerrit-connection-card.tsx` to display a list of connected instances with individual disconnect buttons, and an "Add Instance" button that shows the connection form — when multiple instances are connected, show each as a row with instance name, URL, auth method, and a disconnect action
+- [x] T020 [US4] Verify multi-instance `gerrit_config.json` generation in runner — ensure `generate_gerrit_config()` correctly maps multiple instances to multiple `gerrit_hosts` entries; the first instance's URL becomes `default_gerrit_base_url`; gitcookies from multiple instances are concatenated into a single `.gitcookies` file (one line per host)
+
+**Checkpoint**: User Story 4 complete — multiple Gerrit instances are manageable and all available in sessions.
+
+---
+
+## Phase 7: Polish & Cross-Cutting Concerns
+
+**Purpose**: Improvements that affect multiple user stories.
+
+- [x] T021 [P] Verify backend compiles cleanly: run `cd components/backend && gofmt -l . && go vet ./... && go build ./...`
+- [x] T022 [P] Verify frontend builds cleanly: run `cd components/frontend && npm run build` — ensure zero errors and zero warnings
+- [x] T023 [P] Verify runner installs cleanly: run `cd components/runners/ambient-runner && uv venv && uv pip install -e .`
+- [ ] T024 Run end-to-end manual validation per quickstart.md: connect Gerrit instance via UI, verify status, create session, query changes via agent, disconnect and verify cleanup
+
+---
+
+## Dependencies & Execution Order
+
+### Phase Dependencies
+
+- **Setup (Phase 1)**: No dependencies — can start immediately
+- **Foundational (Phase 2)**: Depends on Setup — BLOCKS all user stories
+- **User Stories (Phase 3+)**: All depend on Foundational phase completion
+ - US1 and US2 are both P1 but US2 depends on US1 (runner needs credentials connected via UI)
+ - US3 can proceed in parallel with US2 (different files)
+ - US4 extends US1 (multi-instance UI) and US2 (multi-instance config generation)
+- **Polish (Phase 7)**: Depends on all user stories being complete
+
+### User Story Dependencies
+
+- **User Story 1 (P1)**: Can start after Foundational (Phase 2) — no other story dependencies
+- **User Story 2 (P1)**: Can start after Foundational (Phase 2) — independent of US1 at code level but requires US1 for end-to-end testing
+- **User Story 3 (P2)**: Can start after Foundational (Phase 2) — independent of US1/US2
+- **User Story 4 (P3)**: Extends US1 (UI) and US2 (runner config) — start after both are complete
+
+### Within Each User Story
+
+- API client before React Query hooks before components (frontend)
+- Credential fetching before config generation (runner)
+- Core implementation before integration with existing pages
+
+### Parallel Opportunities
+
+- T002 (validation) can run in parallel with T001 (Dockerfile) — different components
+- T007 and T008 (frontend API + types) can run in parallel — different files
+- T012 (.mcp.json) can run in parallel with T013 (auth.py) — different files
+- T018 (US3 session panel) can run in parallel with T012-T017 (US2 runner work)
+- T021, T022, T023 (build verifications) can all run in parallel
+
+---
+
+## Parallel Example: User Story 1
+
+```bash
+# Launch API client and types in parallel:
+Task: "T007 [P] [US1] Create API client in gerrit-auth.ts"
+Task: "T008 [P] [US1] Add Gerrit types to integrations.ts"
+
+# Then sequentially:
+Task: "T009 [US1] Create React Query hooks in use-gerrit.ts"
+Task: "T010 [US1] Create GerritConnectionCard component"
+Task: "T011 [US1] Add card to IntegrationsClient.tsx grid"
+```
+
+## Parallel Example: User Story 2
+
+```bash
+# Launch MCP config and auth in parallel:
+Task: "T012 [P] [US2] Add Gerrit entry to .mcp.json"
+Task: "T013 [P] [US2] Add fetch_gerrit_credentials() to auth.py"
+
+# Then sequentially:
+Task: "T014 [US2] Add Gerrit config generation to mcp.py"
+Task: "T015 [US2] Add Gerrit auth check to mcp.py"
+Task: "T016 [US2] Add Gerrit to populate_runtime_credentials()"
+Task: "T017 [US2] Add Gerrit to clear_runtime_credentials()"
+```
+
+---
+
+## Implementation Strategy
+
+### MVP First (User Story 1 Only)
+
+1. Complete Phase 1: Setup (Dockerfile bundling)
+2. Complete Phase 2: Foundational (backend API)
+3. Complete Phase 3: User Story 1 (frontend connect/disconnect)
+4. **STOP and VALIDATE**: Test connecting a real Gerrit instance
+5. Deploy/demo if ready
+
+### Incremental Delivery
+
+1. Setup + Foundational -> Backend API ready
+2. Add User Story 1 -> Users can connect Gerrit (MVP!)
+3. Add User Story 2 -> Sessions can use Gerrit tools (core value!)
+4. Add User Story 3 -> Session-level status visibility
+5. Add User Story 4 -> Multi-instance support for power users
+6. Polish -> Build verification and manual E2E validation
+
+---
+
+## Notes
+
+- [P] tasks = different files, no dependencies
+- [Story] label maps task to specific user story for traceability
+- Each user story is independently completable and testable
+- Commit after each task or logical group
+- Stop at any checkpoint to validate story independently
+- The Gerrit MCP server is an external dependency — pin to a specific commit hash in the Dockerfile for reproducibility
+- Gerrit REST API responses have a `)]}'` XSSI prefix that must be stripped in validation code