Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
257a295
docs: add architecture diagram
ekuris-redhat Jul 13, 2026
4530c3a
docs: fix architecture diagram inaccuracies
ekuris-redhat Jul 13, 2026
a38f15c
docs: fix diagram alignment — all box lines at width 75
ekuris-redhat Jul 13, 2026
9ce53bf
docs: address review feedback on architecture diagram
ekuris-redhat Jul 14, 2026
537c3d9
cosmetic changes
ekuris-redhat Jul 14, 2026
2150db9
Fix architecture diagram per PR review feedback
ekuris-redhat Jul 16, 2026
d47e2e4
Add verbal explanations to architecture doc for newcomers
ekuris-redhat Jul 19, 2026
4384012
Clean up formatting: remove em dashes, use colons for component labels
ekuris-redhat Jul 19, 2026
59e6b58
Remove inline code formatting from prose paragraphs
ekuris-redhat Jul 19, 2026
481a927
Fix layer count: five to six in System Overview
ekuris-redhat Jul 19, 2026
8c3a528
Address PR review feedback on architecture diagram
ekuris-redhat Jul 19, 2026
2b77843
Fix review findings: label mismatch, legend placement, docs nav
ekuris-redhat Jul 19, 2026
b277734
Fix two factual errors found in code review
ekuris-redhat Jul 19, 2026
2596a07
Add high-level pipeline diagram before detailed system overview
ekuris-redhat Jul 20, 2026
4aa367e
Rewrite high-level architecture section with accurate component descr…
ekuris-redhat Jul 20, 2026
f26b5a1
Address review feedback: legend placement, happy-path note, configura…
ekuris-redhat Jul 20, 2026
8f44f51
Cosmetic fixes
ekuris-redhat Jul 20, 2026
0c59664
Address architecture review: add missing views, fix correctness claims
ekuris-redhat Jul 21, 2026
1ad089a
Split architecture.md into structured parts, fix broken links and for…
ekuris-redhat Jul 22, 2026
d6e0897
Trim architecture docs for readability
ekuris-redhat Jul 22, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions docs/architecture/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Forge Architecture

Architecture reference for Forge, an AI-powered SDLC orchestrator. Covers system structure, runtime topology, state management, failure modes, security boundaries, and key design decisions.

For workflow details, see the [Feature](../guide/feature-workflow.md), [Bug](../guide/bug-workflow.md), and [Task](../guide/task-workflow.md) guides. For API reference, see the OpenAPI spec at `/docs` when the gateway is running.

| Part | Contents |
|------|----------|
| [System & Components](overview.md) | System context, external actors, component responsibilities |
| [Internals](internals.md) | Runtime topology, state and concurrency, failure recovery, security |
| [Reference](reference.md) | Architectural decisions, known limitations, workflow lifecycles |
56 changes: 56 additions & 0 deletions docs/architecture/internals.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Internals

## Runtime Topology

Forge runs as two process types plus Redis:

- **Gateway**: Single FastAPI/Uvicorn process. Stateless; can be load-balanced.
- **Worker(s)**: One or more `OrchestratorWorker` processes. Each joins the Redis consumer group. **Must run on a host with Podman installed.** Each worker handles up to 20 concurrent tasks (configurable via `QUEUE_MAX_CONCURRENT_TASKS`).
- **Redis**: Single-instance server. No built-in HA; must be provided externally if required.

Gateway and Worker communicate only through Redis and can be deployed on separate hosts. Horizontal Worker scaling has a limitation: per-ticket event serialization uses an in-process `asyncio.Lock`, not a distributed lock (see [Known Limitations](reference.md#known-limitations)).

## State and Event Processing

**Delivery guarantee:** At-least-once. Messages are acknowledged (`XACK`) only after successful processing. The system does not provide exactly-once semantics.

**Checkpointing:** LangGraph workflow state is persisted via `AsyncRedisSaver`, keyed by Jira ticket key (e.g., `AISOS-123`). Checkpoints are written after each graph node completes. When a new event arrives for an existing ticket, the workflow resumes from its last checkpoint.

**Idempotency:** A `DeduplicationService` exists but is not yet wired into the webhook routes. Branch creation and label operations are naturally idempotent; Jira comment posting is not.

**Consistency caveat:** Checkpoint writes and external side effects (Jira comments, GitHub PRs) are not transactional. A crash between a side effect and its checkpoint write can cause duplicate actions on retry.

## Failure and Recovery

| Component | Failure impact | Recovery |
|-----------|---------------|----------|
| Gateway | Incoming webhooks dropped | Jira/GitHub retry delivery per their own policies |
| Worker | In-flight messages stay in Redis PEL | Restart consumes new messages; PEL requires manual `XCLAIM` |
| Redis | Complete system outage; all state at risk | Configure Redis persistence (RDB/AOF) externally |
| LLM provider | Planning/code generation fails | Retried up to 3 times, then moved to dead-letter queue |
| Container | Non-zero exit captured by orchestrator | Retry mechanism determines re-attempt |

**Retry policy:** Up to 3 attempts with exponential backoff (30s initial, 2x multiplier, capped at 1 hour). Failed messages go to a dead-letter queue for manual investigation.

**Blocked workflows:** The `forge:blocked` label is applied to Jira tickets in error state. Adding `forge:retry` triggers re-entry at the failed step.

**Approval gates:** Workflows pause indefinitely at human review gates. There is no automatic timeout or escalation.

## Security Boundaries

**Webhook authentication:** HMAC-SHA256 validation via `hmac.compare_digest()`. Validation is conditional: it only runs when secrets are configured (`JIRA_WEBHOOK_SECRET`, `GITHUB_WEBHOOK_SECRET`). **Always configure secrets in production.**

**Credential distribution:**

| Credential | Worker | Container |
|------------|--------|-----------|
| Redis | Yes | No |
| Jira API token | Yes | No |
| GitHub App credentials | Yes | No |
| LLM provider (API key or Vertex AI) | Yes | Yes |
| Langfuse | Yes | Yes (when enabled) |
| Git identity | No | Yes |

Containers do not receive Jira, GitHub, or Redis credentials. All external platform operations are performed by the orchestrator after the container exits.

**Container isolation:** Rootless Podman with configurable network mode (`slirp4netns` default), memory limit (4GB), CPU limit (2 cores), and 30-minute timeout. Workspace mounted read-write at `/workspace`; task file read-only at `/task.json`.
85 changes: 85 additions & 0 deletions docs/architecture/overview.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
# System & Components

## System Context

Forge sits between project management (Jira), source control (GitHub), and LLM providers, orchestrating work from ticket creation through merged PR.

```mermaid
flowchart LR
A["Jira / GitHub\n(webhooks)"] --> B["Gateway\n(FastAPI)"]
B --> C["Redis\n(Streams + State)"]
C --> D["Workers\n(LangGraph)"]
D --> E["Podman\nContainers"]
D <--> F["LLM\n(Claude / Gemini)"]
E <--> F
D --> A
```

**External actors:**

- **Jira**: Source of ticket lifecycle events (issue and comment webhooks)
- **GitHub**: Source of PR, CI, and code review events (PR, check suite, and review webhooks)
- **LLM providers**: Anthropic (direct API) and Google Vertex AI (Claude and Gemini models)
- **Langfuse**: Optional observability for LLM call tracing and cost tracking
- **Human reviewers**: Approve or revise artifacts at defined workflow gates

## Component Responsibilities

```mermaid
flowchart TD
subgraph External["External Systems"]
Jira
GitHub
Langfuse["Langfuse (Observability)"]
end

subgraph Gateway["FastAPI Gateway (:8000)"]
JiraWH["POST /api/v1/webhooks/jira"]
GitHubWH["POST /api/v1/webhooks/github"]
end

subgraph Queue["Redis"]
Streams["Streams: forge:events:jira\nforge:events:github"]
State["AsyncRedisSaver\nLangGraph checkpointing"]
end

subgraph Workers["Worker Processes (consumer group: forge-workers)"]
Router{"WorkflowRouter\nroute by issue type"}
Feature["FeatureWorkflow\n(Feature/Story)"]
Bug["BugWorkflow\n(Bug)"]
Task["TaskTakeoverWorkflow\n(Task/Epic)"]
end

subgraph Container["Podman Container (ephemeral)"]
Agent["Deep Agents + MCP\n/workspace (repo mounted)"]
end

LLM["LLM Backends\nAnthropic API (Claude)\nVertex AI (Claude/Gemini)"]

Jira -- webhooks --> JiraWH
GitHub -- webhooks --> GitHubWH
JiraWH --> Streams
GitHubWH --> Streams
Streams --> Router
Router --> Feature
Router --> Bug
Router --> Task
Feature --> Container
Bug --> Container
Task --> Container
Workers <--> LLM
Container <--> LLM
Workers --> Jira
Workers --> GitHub
Workers --> Langfuse
```

**Gateway (FastAPI)**: Accepts webhooks over HTTPS, validates HMAC-SHA256 signatures, and publishes events to Redis Streams. Performs no workflow logic.

**Worker**: Consumes events from Redis Streams via the `forge-workers` consumer group. The `WorkflowRouter` resolves the target LangGraph workflow (Feature, Bug, or Task Takeover) based on Jira issue type and drives execution through planning, implementation, CI repair, and human review stages.

**Redis**: Event bus (Redis Streams), workflow state store (LangGraph `AsyncRedisSaver` checkpoints per ticket), retry queue, dead-letter queue, and supporting indexes (PR-to-ticket mapping, deduplication keys).

**Podman Container**: Ephemeral rootless containers that execute implementation tasks. Each container receives the repo at `/workspace` (read-write), a task file at `/task.json` (read-only), and LLM credentials. Runs Deep Agents with MCP tool access. The orchestrator handles pushing and PR creation after the container exits.

**LLM Backends**: Claude and Gemini models called by both orchestrator nodes (planning, review) and container agents (code generation). Supports Anthropic direct API and Google Vertex AI, selected automatically based on configured credentials.
95 changes: 95 additions & 0 deletions docs/architecture/reference.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
# Reference

## Key Architectural Decisions

### Redis Streams for Event Bus

Use Redis Streams with consumer groups instead of a dedicated message broker (RabbitMQ, Kafka). Redis already serves as the checkpoint store, so reusing it for event queuing eliminates an infrastructure dependency. The tradeoff: no built-in dead-letter queues or cross-datacenter replication.

### LangGraph for Workflow Orchestration

Use LangGraph `StateGraph` with `AsyncRedisSaver` checkpointing instead of Temporal or Airflow. LangGraph provides native LLM-driven decision nodes, conditional routing, and checkpointed pause/resume. The tradeoff: a less mature ecosystem with fewer operational tools.

### Host-Level Podman for Code Execution

Run implementation tasks in rootless Podman containers on the Worker host instead of Kubernetes jobs or remote VMs. This simplifies the container lifecycle but requires Podman on every Worker host.

### Workflow Separation by Issue Type

Three separate LangGraph workflow definitions (Feature, Bug, Task Takeover) rather than one parameterized workflow. Each has fundamentally different planning stages. Shared implementation/CI/review nodes are reused across all three.

### Human Approval Gates

Workflows pause at defined gates and wait indefinitely for human approval. The `forge:yolo` label provides an opt-in escape hatch for autonomous operation. The tradeoff: increased latency for every ticket.

## Known Limitations

- **No PEL reclaim**: Unacknowledged messages from crashed workers remain in Redis PEL indefinitely. Recovery requires manual `XCLAIM`.
- **No distributed per-ticket lock**: Multiple workers can process events for the same ticket concurrently, causing potential checkpoint conflicts.
- **Webhook deduplication not wired**: `DeduplicationService` exists but is not connected to webhook routes.
- **Webhook signature validation is optional**: Endpoints accept unsigned payloads when secrets are not configured.
- **No approval gate timeout**: Paused workflows wait indefinitely with no escalation.
- **Single Redis dependency**: No Sentinel, Cluster, or HA. Redis is a single point of failure.
- **Container security hardening gaps**: No `--cap-drop ALL`, `--no-new-privileges`, or `--read-only` root filesystem.
- **No cross-stream ordering**: Jira and GitHub streams are consumed independently with no ordering guarantee.

## Workflow Lifecycles

`>>` marks human approval gates. Gates are auto-approved when the `forge:yolo` label is set. For detailed node-level flows, see the [Feature](../guide/feature-workflow.md), [Bug](../guide/bug-workflow.md), and [Task](../guide/task-workflow.md) workflow guides.

### Feature Lifecycle

```mermaid
flowchart TD
A["Ticket created\n(Feature/Story)"] --> B["Generate PRD"]
B --> C[">> PRD approval"]
C --> D["Generate technical spec"]
D --> E[">> Spec approval"]
E --> F["Decompose into epics"]
F --> G[">> Plan approval"]
G --> H["Generate tasks"]
H --> I[">> Task approval"]
I --> J["Route tasks by repo"]

J --> K["Implement in container"]
K --> L["Review and open PR"]
L --> M["CI repair loop"]
M --> N[">> Human code review"]
N -->|merged| O["Aggregate status\nComplete"]
```

### Bug Lifecycle

```mermaid
flowchart TD
A["Ticket created\n(Bug)"] --> B["Triage check"]
B -->|"missing info"| C[">> Ask reporter"]
C --> B
B -->|"sufficient"| D["Root cause analysis"]
D --> E[">> Present fix options\n(user selects >option N)"]
E --> F["Generate fix plan"]
F --> G[">> Plan approval"]

G --> H["Implement in container"]
H --> I["Review and open PR"]
I --> J["CI repair loop"]
J --> K[">> Human code review"]
K -->|merged| L["Post-merge summary\nComplete"]
```

### Task Lifecycle

```mermaid
flowchart TD
A["Ticket created\n(Task/Epic)"] --> B["Triage check"]
B -->|"missing context"| C[">> Ask for details"]
C --> B
B -->|"sufficient"| D["Generate plan"]
D --> E[">> Plan approval"]

E --> F["Implement in container"]
F --> G["Review and open PR"]
G --> H["CI repair loop"]
H --> I[">> Human code review"]
I -->|merged| J["Complete"]
```
6 changes: 6 additions & 0 deletions zensical.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@ nav = [
]},
{"Developer Guide" = [
{"Overview" = "developer-guide.md"},
{"Architecture" = [
{"Overview" = "architecture/index.md"},
{"System & Components" = "architecture/overview.md"},
{"Internals" = "architecture/internals.md"},
{"Reference" = "architecture/reference.md"},
]},
{"Local Setup" = "dev/setup.md"},
{"Testing" = "dev/testing.md"},
{"Contributing" = "dev/contributing.md"},
Expand Down
Loading