Skip to main content

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

ModeBehaviorTimeoutUse Case
ManualReturns assembled prompt for human review. No execution, no issue closure.120sOne-off work, high-stakes changes, proof-of-concept
APICalls Claude API directly (Anthropic). Auto-closes GitHub issues on success.120sAutomated, low-risk tasks (refactoring, docs, simple bug fixes)
TOMMYDelegates to TOMMY workflow via HTTP POST. TOMMY SSH’s into target server, runs Claude CLI, commits changes. Auto-closes issues on status="SUCCESS".1800sProduction 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:
#FieldSourceTypeDefaultNotes
a1gh_org$json.body.gh_orgstring(required)GitHub organization (e.g., traceo-ai)
a2gh_repo$json.body.gh_repostring(required)Repository name (e.g., traceo-mcp-server)
a3epic_issue$json.body.epic_issue_numbernumber(required)Epic issue number to resolve
a4override_block$json.body.block_issue_numberstring""Optional block override; if empty, FORGE auto-selects next open block
a5exec_mode$json.body.execution_modeenum"manual""manual" | "api" | "tommy"
a6target_repo$json.body.target_repo_for_codestring""Reserved for future use (repo for code changes)
a7api_base(hardcoded)stringhttps://api.github.comGitHub API endpoint
a8claude_key$json.body.claude_api_keystring""API key for Claude (api mode only)
a9ssh_host$json.body.ssh_hoststringroot@vps.devarno.cloudSSH host for TOMMY deployment
a10project_path$json.body.project_pathstring/root/projects/so1-platformTarget project path on remote server
a11dry_run$json.body.dry_runbooleanfalsePhase 4 pattern: pass to TOMMY for safe-mode (plan-only, no git writes)
a12discord_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 a11>,
  "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:
  1. Prepare Closure — Map task data for loop iteration
  2. Close Task Issues Loop — SplitInBatches over task array
  3. Comment on Task — POST completion comment to each task issue
  4. Close Task Issue — Close each task GitHub issue
  5. Comment on Block — POST summary comment to block issue
  6. Close Block Issue — Close block issue
  7. 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?

AspectBFF Injectionn8n Env Vars
SecurityServer-side onlyExposed in n8n UI (risky)
CostFree$600/mo (n8n Pro feature)
RotationZero-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:
NodeTriggerPayload
Execution StartedImmediately after Set Configuration{ event: "started", epic, block, tasks_count }
Block SelectedAfter FORGE selects/overrides block{ event: "block_selected", block_number, block_title }
Execution ResultAfter execution completes (api/tommy){ event: "result", status: "SUCCESS" or "FAILURE", details }
Block ClosedAfter 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

Webhook Input Schema

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

NameIDTypeUsed By
GitHub (Dev4rno)FhglvXmXMeC2SQTlgithubApi~15 nodes (all GitHub API calls)
Claude API KeyPdEKB19B1R0PQ4thhttpHeaderAuthCall 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:
  1. Go to hab.so1.io → FORGE workflow
  2. Click each GitHub HTTP node → re-select “GitHub (Dev4rno)”
  3. Click “Call Claude API” node → re-select Claude credential
  4. 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.