FORGE Automation Stack
Overview
FORGE (Forge Orchestrates Resolved GitHub Execution) is the strategic planner workflow that reads GitHub epics, extracts blocks and tasks, assembles structured prompts, and delegates execution via three modes : manual review, Claude API, or SSH-based TOMMY.
The workflow consists of 47 nodes with three execution paths, two proven automation patterns (dry-run passthrough + BFF secret injection), and a Discord telemetry system for burn-in monitoring.
Architecture Diagram
┌─────────────────────────────────────────────────────────────────┐
│ FORGE Webhook Trigger (POST /webhook/forge-resolve-next-ticket) │
└───────────────────────────┬─────────────────────────────────────┘
│
▼
┌─────────────────────┐
│ Set Configuration │ (a1–a12: parse & inject fields)
│ (12 fields) │
└────────┬────────────┘
│
┌──────────────────┼──────────────────┐
│ │ │
▼ ▼ ▼
┌───────────────┐ ┌────────────────┐ ┌──────────────────┐
│ Fetch Epic │ │ Parse Epic │ │ Block Override? │
│ Issue │ │ for Blocks │ │ (a4 populated?) │
│ (GitHub) │ └────────────────┘ └──────────────────┘
└───────────────┘ │
┌────────────────┘
│
┌──────────────────┼──────────────────┐
│ │ │
(YES - Override) (NO - Iterate) (RESOLVED)
│ │ │
▼ ▼ ▼
┌──────────────┐ ┌────────────────┐ ┌──────────────┐
│ Fetch Block │ │ Split Blocks │ │ Package │
│ Issue (a4) │ │ to Items Loop │ │ Override │
│ │ │ │ │ Block │
│ │ │ Fetch Block │ └──────────────┘
│ │ │ Classify Status│
└──────────────┘ │ Find next open │
│ └────────────────┘
│ │
└──────────┬───────┘
│
▼
┌───────────────────────────┐
│ Parse Block for Tasks │
│ Iterate Task Issues │
│ Extract Task Metadata │
└───────────────┬───────────┘
│
▼
┌───────────────────────────┐
│ Assemble FORGE Prompt │
│ (epic + block + tasks) │
└───────────────┬───────────┘
│
▼
┌───────────────────────────┐
│ Execution Mode Switch │
│ (a5: exec_mode value) │
└─┬──────────────┬──────────┘
│ │
┌─┴──┐ ┌────┴───┐ ┌──────────┐
│ │ │ │ │ │
"manual" "api" "tommy" DEFAULT (error) │
│ │ │ │
▼ ▼ ▼ ▼
┌────────────────────────────────────────────────┐
│ │
├─────────────────────────────────────────────┐ │
│ Output 0: Manual │ │
│ → Return Prompt (no execution) │ │
├─────────────────────────────────────────────┤ │
│ Output 1: API │ │
│ → Call Claude API → Execute │ │
│ → Closure: Close issues (7-step chain) │ │
├─────────────────────────────────────────────┤ │
│ Output 2: TOMMY │ │
│ → Build TOMMY Payload → HTTP POST │ │
│ → 1800s timeout → TOMMY executes │ │
│ → If status="SUCCESS" → Closure chain │ │
└─────────────────────────────────────────────┘ │
│ │
└────────────────────────────────────────────────┘
│
▼
┌───────────────────────────┐
│ Return Result to Webhook │
│ Caller (Rover / n8n) │
└───────────────────────────┘
Three Execution Modes
Mode Behavior Timeout Use Case Manual Returns assembled prompt for human review. No execution, no issue closure. 120s One-off work, high-stakes changes, proof-of-concept API Calls Claude API directly (Anthropic). Auto-closes GitHub issues on success. 120s Automated, low-risk tasks (refactoring, docs, simple bug fixes) TOMMY Delegates to TOMMY workflow via HTTP POST. TOMMY SSH’s into target server, runs Claude CLI, commits changes. Auto-closes issues on status="SUCCESS". 1800s Production deployments, complex multi-file changes, critical infrastructure
Set Configuration (12 Fields)
All workflow parameters are collected in a single Set Configuration node at the start, keyed as a1 through a12:
# Field Source Type Default Notes a1 gh_org$json.body.gh_orgstring (required) GitHub organization (e.g., traceo-ai) a2 gh_repo$json.body.gh_repostring (required) Repository name (e.g., traceo-mcp-server) a3 epic_issue$json.body.epic_issue_numbernumber (required) Epic issue number to resolve a4 override_block$json.body.block_issue_numberstring ""Optional block override; if empty, FORGE auto-selects next open block a5 exec_mode$json.body.execution_modeenum "manual""manual" | "api" | "tommy"a6 target_repo$json.body.target_repo_for_codestring ""Reserved for future use (repo for code changes) a7 api_base(hardcoded) string https://api.github.comGitHub API endpoint a8 claude_key$json.body.claude_api_keystring ""API key for Claude (api mode only) a9 ssh_host$json.body.ssh_hoststring root@vps.devarno.cloudSSH host for TOMMY deployment a10 project_path$json.body.project_pathstring /root/projects/so1-platformTarget project path on remote server a11 dry_run$json.body.dry_runboolean falsePhase 4 pattern : pass to TOMMY for safe-mode (plan-only, no git writes)a12 discord_webhook_forge$json.body.discord_webhook_forgestring ""ZOID Phase A pattern : BFF-injected from Vercel env (never user-supplied)
Key insight: Fields a1–a11 are user-supplied (from webhook or Rover form). Field a12 is always injected by the BFF at trigger time, sourced from process.env.DISCORD_WEBHOOK_FORGE. This pattern keeps the Discord webhook URL server-side only.
TOMMY Integration
When exec_mode === "tommy", FORGE constructs a structured payload and POSTs to the TOMMY workflow:
{
"repo" : "<gh_org>/<gh_repo>" ,
"branch" : "main" ,
"project_path" : "<from a10>" ,
"ssh_host" : "<from a9>" ,
"dry_run" : <from a 11 > ,
"max_concurrency" : 1 ,
"delay_between_ms" : 5000 ,
"forge_tasks" : [
{
"title" : "Task title from GitHub issue" ,
"description" : "Task body extracted from issue" ,
"issue_number" : 42 ,
"html_url" : "https://github.com/..." ,
"file_targets" : [ "src/foo.ts" , "tests/foo.test.ts" ],
"acceptance_criteria" : "..."
}
],
"forge_context" : {
"epic_title" : "..." ,
"epic_number" : 22 ,
"block_title" : "..." ,
"block_number" : 24
}
}
TOMMY HTTP Endpoint
URL: https://hab.so1.io/webhook/atomiser
Method: POST
Timeout: 1800 seconds (30 minutes) — increased from 600s to absorb FORGE + TOMMY verification loops
Credentials: None (webhook is public)
Response: { status: "SUCCESS" | "FAILURE", details: {...} }
On status === "SUCCESS", FORGE triggers the issue closure chain (see below).
Issue Closure Chain
Both api and tommy modes run a closure chain on success:
Prepare Closure — Map task data for loop iteration
Close Task Issues Loop — SplitInBatches over task array
Comment on Task — POST completion comment to each task issue
Close Task Issue — Close each task GitHub issue
Comment on Block — POST summary comment to block issue
Close Block Issue — Close block issue
Return Result — Respond to webhook caller with success details
The closure chain is conditional : it only runs if execution succeeded (Claude API returned result, or TOMMY returned status="SUCCESS").
BFF Secret Injection (ZOID Phase A Pattern)
The Discord webhook URL is a sensitive credential that should never be exposed to the browser. Here’s how FORGE handles it:
Why BFF Instead of n8n Env Vars?
Aspect BFF Injection n8n Env Vars Security Server-side only Exposed in n8n UI (risky) Cost Free $600/mo (n8n Pro feature) Rotation Zero-downtime (Vercel env) Requires n8n restart Browser Exposure ❌ Never ❌ Often (through console) Single Source of Truth ✅ Vercel (BFF reads) ❌ Distributed (n8n + browser)
Implementation Flow
┌─────────────────────────────────────────────┐
│ Rover Console (Browser) │
│ Trigger FORGE Webhook Form │
│ (sends: gh_org, repo, epic#, exec_mode...) │
└────────────┬────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────┐
│ BFF (so1-control-plane-api) │
│ Webhook Handler: POST /api/bff/workflows │
│ │
│ 1. Validate BetterAuth session │
│ 2. Read process.env.DISCORD_WEBHOOK_FORGE │
│ 3. Inject into payload: │
│ { ...userFields, │
│ discord_webhook_forge: <URL> │
│ } │
│ 4. POST to n8n FORGE webhook │
└────────────┬────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────┐
│ n8n FORGE Workflow │
│ - Set Configuration stores a12 = <URL> │
│ - Discord HTTP nodes reference a12 │
│ - Fire-and-forget telemetry (continueOnFail)│
└─────────────────────────────────────────────┘
BFF Code Location
File: platform-tools/so1-control-plane-api/src/routes/workflows.ts (lines 124–160)
// Example: BFF injects Discord webhook from Vercel env
const payload = {
gh_org: body . gh_org ,
gh_repo: body . gh_repo ,
epic_issue_number: body . epic_issue_number ,
execution_mode: body . execution_mode ,
// ... other user-supplied fields ...
discord_webhook_forge: process . env . DISCORD_WEBHOOK_FORGE || "" ,
};
// POST to n8n FORGE webhook
const response = await fetch (
"https://hab.so1.io/webhook/forge-resolve-next-ticket" ,
{
method: "POST" ,
headers: { "Content-Type" : "application/json" },
body: JSON . stringify ( payload ),
},
);
Telemetry & Notifications (Discord)
Four HTTP Request nodes post to the Discord webhook at key decision points:
Node Trigger Payload Execution Started Immediately after Set Configuration { event: "started", epic, block, tasks_count }Block Selected After FORGE selects/overrides block { event: "block_selected", block_number, block_title }Execution Result After execution completes (api/tommy) { event: "result", status: "SUCCESS" or "FAILURE", details }Block Closed After closure chain completes { event: "block_closed", block_number, tasks_closed }
Fire-and-Forget Semantics
All four Discord nodes are configured with:
continueOnFail: true — If Discord is unreachable, FORGE continues (doesn’t fail)
Disabled by default — Nodes remain disabled until Phase A burn-in is complete (5+ successful Phase 5 runs)
Parallel execution — All four nodes can run concurrently if multiple events are queued
User-Supplied Fields (from Rover)
{
"gh_org" : "traceo-ai" ,
"gh_repo" : "traceo-mcp-server" ,
"epic_issue_number" : 22 ,
"block_issue_number" : null ,
"execution_mode" : "manual|api|tommy" ,
"dry_run" : false ,
"ssh_host" : "root@vps.devarno.cloud" ,
"project_path" : "/root/projects/so1-platform" ,
"claude_api_key" : "sk-..."
}
BFF-Injected Field
{
"discord_webhook_forge" : "https://discord.com/api/webhooks/..."
}
The BFF adds discord_webhook_forge before POSTing to n8n . The browser never sees this value.
Credentials
Name ID Type Used By GitHub (Dev4rno) FhglvXmXMeC2SQTlgithubApi~15 nodes (all GitHub API calls) Claude API Key PdEKB19B1R0PQ4thhttpHeaderAuthCall Claude API node (api mode only)
⚠️ Credential Rebinding Issue
After updating the workflow via the n8n API , all credential bindings detach. You must manually re-select each credential in the n8n UI and save:
Go to hab.so1.io → FORGE workflow
Click each GitHub HTTP node → re-select “GitHub (Dev4rno)”
Click “Call Claude API” node → re-select Claude credential
Save the workflow
This is a known n8n limitation (credentials not persisted through API updates).
Rover UI Integration
The Rover console provides a dedicated form to trigger FORGE:
Component: ForgeForm in platform-tools/so1-rover/src/components/workflows/webhook-trigger-form.tsx (lines 154–310)
Auto-Detection: Form is shown when webhook path or workflow name contains “forge”
Fields:
GitHub Org (text, default: traceo-ai)
Repository (text, default: traceo-mcp-server)
Epic Issue # (number, default: 22)
Block Override (optional text)
Execution Mode (select: Manual / API / Tommy)
SSH Host (conditional, shown when tommy selected)
Project Path (conditional, shown when tommy selected)
Timeouts: 120s (manual/api), 1800s (tommy)
Dry-Run Passthrough (Phase 4)
Set dry_run: true to pass safe-mode flag to TOMMY:
{
"gh_org" : "traceo-ai" ,
"gh_repo" : "traceo-mcp-server" ,
"epic_issue_number" : 22 ,
"execution_mode" : "tommy" ,
"dry_run" : true
}
TOMMY receives this flag and:
✅ Plans the changes
✅ Simulates git commits (without writing)
✅ Reports what would change
❌ Does not push to git
Use this for:
Testing FORGE → TOMMY integration
Verifying task detection and prompt assembly
Rehearsing deployments before going live
Known Issues & Limitations
GitHub Issue Structure Required: FORGE expects epics to have markdown
checklists referencing block issues, and blocks to have checklists referencing
task issues. If your epic doesn’t follow this pattern, FORGE won’t find any
blocks or tasks. Example epic body: markdown ## Blocks - [x] #123 Block 1: Setup infrastructure - [ ] #124 Block 2: Deploy services ## Context This epic delivers the Q2 platform upgrade.
Vercel Proxy Timeout
The Rover UI proxies through Vercel, which has a 300-second serverless function limit. Tommy-mode executions exceeding 5 minutes timeout at the Vercel layer (but the n8n execution continues regardless).
Workaround: Trigger FORGE directly via webhook curl if you need longer timeouts:
curl -X POST https://hab.so1.io/webhook/forge-resolve-next-ticket \
-H "Content-Type: application/json" \
-d '{
"gh_org": "traceo-ai",
"gh_repo": "traceo-mcp-server",
"epic_issue_number": 22,
"execution_mode": "tommy"
}'
Phases Verification Step-by-step verification procedures for Phases 4–7 + ZOID Phase A with curl
examples and expected behavior.
Patterns & Scalability Dry-run + BFF injection patterns, replication roadmap, implementation
templates for other workflows.
FORGE Skill Quick-reference skill for triggering FORGE from the Rover console.