Architecture

System Overview

Phixr is a FastAPI application that bridges GitLab’s issue workflow with OpenCode’s AI coding sessions. It translates GitLab webhooks into OpenCode API calls and posts results back as issue comments.

Request Flow

GitLab Issue Comment (@phixr ...)
  --> POST /webhooks/gitlab
  --> WebhookValidator (token check)
  --> CommentHandler.handle_issue_comment()
  --> CommandParser.parse()
    |-- /session [--vibe]  --> _handle_session_start()
    |-- /end               --> _handle_session_end()
    +-- <message>          --> _handle_message() --> send_followup()

Session Creation

_handle_session_start()
  --> ContextExtractor.extract_issue_context()
      (issue details, repo URL, branch, comments)
  --> OpenCodeIntegrationService.create_session()
      --> POST /session (OpenCode API -- creates AI session)
      --> POST /session/{id}/prompt_async (inject issue context)
      --> VibeRoomManager.create_room() (if --vibe)
  --> monitor_session() (background asyncio task)
      --> GET /event (SSE stream -- real-time events)
      --> Auto-approve permissions and questions
      --> Detect idle --> post results to GitLab

Message Forwarding

_handle_message()
  --> get_active_session_for_issue()
  --> OpenCodeIntegrationService.send_followup()
      --> POST /session/{id}/prompt_async

Core Components

GitLab Integration Layer

Module File Role
Webhook Handler webhooks/gitlab_webhook.py Receives GitLab note events
Command Parser commands/parser.py Parses /session, /end, and message passthrough
Comment Handler handlers/comment_handler.py Routes commands to session lifecycle methods
GitLab Client utils/gitlab_client.py API wrapper for issues, comments, branches, MRs

Orchestration Layer

Module File Role
Integration Service integration/opencode_integration_service.py Core coordinator – sessions, messages, monitoring, results
Session Store integration/session_store.py Redis-backed session persistence with in-memory fallback
Context Extractor context/extractor.py Pulls issue details, repo state, branch info
Branch Manager git/branch_manager.py Creates ai-work/issue-{id} branches

OpenCode Integration

Module File Role
OpenCode Client bridge/opencode_client.py HTTP + SSE client for OpenCode’s REST API
Vibe Room Manager collaboration/vibe_room_manager.py Tracks collaborative viewing sessions

Key Mappings

GitLab Concept OpenCode Concept Phixr Bridge
Project Repository Shared repo URL (cloned via system instructions)
Issue Session issue_sessions mapping (one session per issue)
Issue comment Session message send_followup() via prompt API
Branch (ai-work/issue-{id}) Working directory System instructions with git clone + git checkout

Session Lifecycle

CREATED --> RUNNING --> COMPLETED (AI went idle)
                    --> TIMEOUT (exceeded time limit)
                    --> ERROR (OpenCode error)
                    --> STOPPED (user ran /end)

OpenCode API

Phixr communicates with OpenCode via its HTTP API (default port 4096):

Endpoint Method Purpose
/session POST Create new session
/session GET List sessions
/session/{id} DELETE Delete session
/session/{id}/prompt_async POST Send prompt (fire-and-forget, returns 204)
/session/{id}/message GET Retrieve conversation history
/session/status GET Session status (idle = absent from dict)
/event GET SSE event stream (messages, tools, permissions)
/permission/{id}/reply POST Auto-approve tool execution
/question/{id}/reply POST Auto-answer with first option
/global/health GET Health check

SSE Events

The monitor task subscribes to /event and handles:

OpenCode Web UI URLs

The OpenCode web UI uses URL pattern: /{base64url(directory)}/session/{sessionID}

For sessions running in / (container root), the encoded directory is Lw (base64url of “/”).

Example: https://opencode.example.com/Lw/session/ses_abc123

Data Persistence

Redis (Primary Store)

Keys and their purposes:

Key Pattern Value TTL
phixr:session:{id} JSON session data 24h
phixr:oc_id:{id} OpenCode session ID 24h
phixr:oc_slug:{id} OpenCode session slug 24h
phixr:issue:{project}:{issue} Phixr session ID 24h

In-Memory Fallback

If Redis is unavailable, all data is stored in Python dicts. Functional but lost on restart.

Technology Stack