mvp-factory-openhands/phase3-implementation-plan.md

20 KiB

Phase 3 Implementation Plan: Autonomous Build Test MVP

Date: 2025-12-02 Estimated Duration: 4-5 hours Current Workflow: ID j1MmXaRhDjvkRSLa (7 nodes) → Target: 11 nodes


📋 EXECUTIVE SUMMARY

Transform the current 7-node basic workflow into a production-ready autonomous CI/CD system with:

  • Retry logic (max 3 attempts)
  • Error feedback to OpenHands
  • Gitea commit status updates
  • Real project build testing

Current State:

  • Workflow active: j1MmXaRhDjvkRSLa
  • SSH credentials configured: /home/bam/.ssh/n8n_key
  • OpenHands SDK wrapper: /home/bam/openhands-sdk-wrapper-sh.sh
  • API keys available: MiniMax & DeepSeek

🎯 IMPLEMENTATION ROADMAP

Phase 3 Workflow Design (11 Nodes)

[1] Gitea Webhook (existing)
    ↓
[2] Extract Repo Info (modify existing)
    ↓
[3] Initialize Retry Counter (NEW)
    ↓
[4] Start OpenHands Build (modify existing)
    ↓
[5] Wait for Completion (modify existing)
    ↓
[6] Check Build Results (modify existing)
    ↓
[7] Decision: Build OK? (NEW)
    ├─ YES → [8] Update Gitea Success → [11] Success Response
    └─ NO → [9] Format Error Feedback (NEW)
                 ↓
             [10] Check Retry Count (NEW)
                 ├─ < 3 → Loop back to [4]
                 └─ ≥ 3 → Update Gitea Failure → [11] Final Failure

📝 STEP-BY-STEP IMPLEMENTATION

STEP 1: Setup Test Repository (20 min)

Action: Create a test repository with intentional build errors

# Via Gitea API
curl -X POST https://git.oky.sh/api/v1/user/repos \
  -H "X-Gitea-Token: {YOUR_GITEA_TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "autonomous-build-test",
    "description": "Phase 3 test repository with intentional errors",
    "private": false
  }'

# Alternative: Use Gitea UI
# 1. Go to https://git.oky.sh
# 2. Click "+" → New Repository
# 3. Name: autonomous-build-test
# 4. Create with sample Node.js project

Sample Test Files:

package.json (with intentional error):

{
  "name": "autonomous-build-test",
  "version": "1.0.0",
  "scripts": {
    "build": "node build.js"
  },
  "dependencies": {
    "express": "^4.18.2"
  }
}

build.js (intentional syntax error):

// This will fail - missing closing brace
const express = require('express');
const app = express();

app.get('/', (req, res) => {
  res.send('Hello World!'
  // Missing closing brace
});

app.listen(3000);
console.log('Server running');

STEP 2: Configure Gitea Webhook (15 min)

Action: Set up webhook to trigger n8n workflow

  1. Go to Repository Settings:

  2. Add Webhook:

    • URL: https://n8n.oky.sh/webhook/openhands-autonomous-build
    • Trigger: Push events
    • HTTP Method: POST
    • Active: ✓
    • Save
  3. Test Webhook:

    • Click "Test Delivery"
    • Should see 200 OK response

STEP 3: Modify n8n Workflow Structure (60 min)

Current Workflow (7 nodes):

1. Gitea Webhook
2. Extract Repo Info
3. Start OpenHands Build
4. Wait 10s
5. Check Build Status
6. Format Response
7. Send Response

Target Workflow (11 nodes):

Node 1: Gitea Webhook (Existing)

Configuration:

  • Path: /webhook/openhands-autonomous-build
  • HTTP Method: POST
  • No changes needed

Node 2: Extract Repo Info (Modify Existing)

Current Code:

// Keep existing code, add repo info extraction
const data = $json;

// Extract repository information
const repoData = {
  repo_name: data.repository?.name || 'unknown',
  repo_full_name: data.repository?.full_name || 'unknown',
  owner: data.repository?.owner?.name || data.repository?.owner?.username || 'unknown',
  branch: data.ref?.replace('refs/heads/', '') || 'main',
  commit_sha: data.after || 'unknown',
  pusher: data.pusher?.name || 'unknown'
};

return repoData;

Changes: Add repo_name, owner, branch, commit_sha fields

Node 3: Initialize Retry Counter (NEW)

Type: Code Node Name: "Initialize Retry Counter"

Code:

// Initialize retry counter in workflow staticData
$workflow.staticData = $workflow.staticData || {};

// Initialize or increment retry count
$workflow.staticData.retry_count = ($workflow.staticData.retry_count || 0);

// Preserve repo data from previous node
const repoData = $node["Extract Repo Info"].json;

return {
  ...repoData,
  retry_count: 0,
  status: 'INITIALIZED',
  attempt: 1
};

Configuration:

  • Execute Once: False (important for retry loops)

Node 4: Start OpenHands Build (Modify Existing)

Type: SSH Node Name: "Execute OpenHands Build"

Configuration:

Authentication: Private Key
Host: localhost
User: bam
Private Key: /home/bam/.ssh/n8n_key
Timeout: 300000 (5 minutes)

Command (JavaScript - Enhanced with feedback):

// Enhanced task with retry feedback
const repoData = $node["Extract Repo Info"].json;
const retryCount = $node["Initialize Retry Counter"].json.retry_count;
const buildDir = `/workspace/${repoData.repo_name}`;

// Base task
let task = `Build and test the project at ${buildDir}.

Execute the following steps:
1. cd ${buildDir}
2. If package.json exists: npm install
3. If build script exists: npm run build
4. Report build status (success/failure)
5. Capture and report any errors

Repository: ${repoData.repo_name}
Branch: ${repoData.branch}
Commit: ${repoData.commit_sha.substring(0, 8)}
`;

// Add feedback if this is a retry
if (retryCount > 0) {
  const previousOutput = $node["Check Build Results"].json;
  const errorDetails = previousOutput?.error_message || 'Unknown error';

  task += `

PREVIOUS BUILD FAILED (Attempt ${retryCount}):
Error Details:
${errorDetails}

Please analyze the previous errors and fix them. Be thorough and ensure all issues are resolved before attempting the build again.

This is retry attempt #${retryCount + 1}. Please be extremely careful and fix ALL problems.`;
}

// Execute via SDK wrapper
return `sh /home/bam/openhands-sdk-wrapper-sh.sh "${task.replace(/"/g, '\\"')}"`;

Data Preservation (CRITICAL):

const sshOutput = $json;
const repoData = $node["Extract Repo Info"].json;
const retryCount = $node["Initialize Retry Counter"].json.retry_count;

return {
  ...repoData,              // Preserve repository data
  code: sshOutput.code,
  stdout: sshOutput.stdout,
  stderr: sshOutput.stderr,
  status: sshOutput.code === 0 ? 'SUCCESS' : 'FAILED',
  retry_count: retryCount,
  attempt: retryCount + 1
};

Node 5: Wait for Completion (Modify Existing)

Type: Wait Node Name: "Wait for Build Completion"

Configuration:

  • Amount: 10
  • Unit: Seconds
  • No changes to existing configuration

Node 6: Check Build Results (Modify Existing)

Type: Code Node Name: "Evaluate Build Results"

Code:

// Get OpenHands output
const openhandsOutput = $json;

// Determine success/failure
const buildSuccess = openhandsOutput.code === 0;

// Collect errors if build failed
let errorDetails = '';
if (!buildSuccess) {
  // Prefer stderr, fallback to stdout
  errorDetails = openhandsOutput.stderr || openhandsOutput.stdout || 'Build failed with no error output';
}

// Return structured results
return {
  ...openhandsOutput,  // Preserve all data
  build_success: buildSuccess,
  error_details: errorDetails,
  timestamp: new Date().toISOString()
};

Node 7: Decision: Build OK? (NEW)

Type: IF Node Name: "Decision: Build Success?"

Configuration:

Condition: JSON
Value 1: {{ $json.build_success }}
Operation: Equal
Value 2: true

True Path (YES): → Node 8 (Update Gitea Success) False Path (NO): → Node 9 (Format Error Feedback)

Node 8: Update Gitea Success (NEW)

Type: HTTP Request Node Name: "Update Gitea - Success"

Configuration:

Method: POST
URL: https://git.oky.sh/api/v1/repos/{{ $node["Extract Repo Info"].json.owner }}/{{ $node["Extract Repo Info"].json.repo_name }}/statuses/{{ $node["Extract Repo Info"].json.commit_sha }}
Headers:
  - X-Gitea-Token: {YOUR_GITEA_API_TOKEN}
  - Content-Type: application/json

Body:
{
  "state": "success",
  "description": "✅ Build passed after {{ $node["Initialize Retry Counter"].json.retry_count }} attempt(s)",
  "context": "openhands/autonomous-build",
  "target_url": "https://n8n.oky.sh"
}

Response Handling:

// Preserve data and add Gitea response
const giteaResponse = $json;
const previousData = $node["Extract Repo Info"].json;

return {
  ...previousData,
  gitea_status: 'success',
  gitea_response: giteaResponse,
  final_status: 'SUCCESS',
  build_success: true
};

Node 9: Format Error Feedback (NEW)

Type: Code Node Name: "Format Error for Retry"

Code:

// Get build results and repo data
const buildResults = $node["Check Build Results"].json;
const repoData = $node["Extract Repo Info"].json;
const retryCount = $node["Initialize Retry Counter"].json.retry_count;

// Format comprehensive error message
const errorMsg = `Build failed with the following errors:

REPOSITORY: ${repoData.repo_name}
BRANCH: ${repoData.branch}
COMMIT: ${repoData.commit_sha.substring(0, 8)}
ATTEMPT: ${retryCount + 1}/3

ERROR DETAILS:
${buildResults.error_details}

BUILD OUTPUT (stderr):
${buildResults.stderr || 'No stderr output'}

BUILD OUTPUT (stdout):
${buildResults.stdout || 'No stdout output'}

NEXT STEPS:
Please analyze these errors and fix all issues to ensure a successful build.
Focus on:
1. Dependency issues (npm install errors)
2. Build script failures
3. Code syntax errors
4. Configuration problems

After fixing, the project should build successfully with: npm install && npm run build

This is attempt ${retryCount + 1} of 3. You have ${2 - retryCount} retry(s) remaining.`;

Return:

return {
  ...repoData,
  ...buildResults,
  status: 'FAILED',
  error_message: errorMsg,
  retry_count: retryCount,
  can_retry: retryCount < 2,  // < 3 total attempts
  formatted_error: errorMsg
};

Node 10: Check Retry Count (NEW)

Type: IF Node Name: "Can We Retry?"

Configuration:

Condition: JSON
Value 1: {{ $json.can_retry }}
Operation: Equal
Value 2: true

True Path (YES - Can Retry): → Loop back to Node 4 False Path (NO - Max Retries): → Node 11 (Final Failure)

Node 11: Final Response (Modify Existing)

Type: Code Node Name: "Final Response"

Success Path Code:

// Format success response
const repoData = $node["Extract Repo Info"].json;
const retryCount = $node["Initialize Retry Counter"].json.retry_count;

const successResponse = {
  status: 'SUCCESS',
  repo: repoData.repo_name,
  branch: repoData.branch,
  commit: repoData.commit_sha.substring(0, 8),
  attempts: retryCount + 1,
  message: 'Build completed successfully ✅',
  timestamp: new Date().toISOString(),
  gitea_status: 'success'
};

return successResponse;

Failure Path Code:

// Format failure response
const repoData = $node["Extract Repo Info"].json;
const errorData = $node["Format Error for Retry"].json;

const failureResponse = {
  status: 'FAILED',
  repo: repoData.repo_name,
  branch: repoData.branch,
  commit: repoData.commit_sha.substring(0, 8),
  attempts: errorData.retry_count + 1,
  max_attempts: 3,
  message: 'Build failed after 3 attempts ❌',
  errors: errorData.error_details,
  timestamp: new Date().toISOString(),
  gitea_status: 'failure',
  next_steps: 'Please review the error messages and fix the issues manually before pushing again.'
};

return failureResponse;

HTTP Response Node:

const response = $json;
return [
  {
    statusCode: response.status === 'SUCCESS' ? 200 : 500,
    body: response
  }
];

🔧 CONFIGURATION CHECKLIST

Credentials Required

  • n8n API Key: /home/bam/.n8n_api_key
  • SSH Key: /home/bam/.ssh/n8n_key
  • OpenHands API Keys: /home/bam/openhands/.env
  • Gitea API Token: ⚠️ NEED TO GENERATE

Generate Gitea API Token

# Via Gitea API (or use UI)
curl -X POST https://git.oky.sh/api/v1/users/gitadmin/tokens \
  -H "X-Gitea-Token: {ADMIN_TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "n8n-autonomous-build",
    "scopes": ["repo", "admin:repo_hook"]
  }'

# OR via UI:
# 1. Go to https://git.oky.sh/user/settings/applications
# 2. Click "Generate Token"
# 3. Name: n8n-autonomous-build
# 4. Scopes: repo, admin:repo_hook
# 5. Copy token (will look like: gho_xxxxxxxxxxxxxxxxx)

Store token securely:

echo "GITEA_API_TOKEN=gho_xxxxxxxxxxxxxxxxx" > /home/bam/.gitea_token
chmod 600 /home/bam/.gitea_token

🧪 TESTING STRATEGY

Test 1: Success Path (30 min)

Setup:

# Create clean test repo
git clone https://git.oky.sh/gitadmin/test-success.git
cd test-success

# Create valid package.json
cat > package.json << 'EOF'
{
  "name": "test-success",
  "version": "1.0.0",
  "scripts": {
    "build": "echo 'Build successful'"
  }
}
EOF

# Create simple build script
cat > build.js << 'EOF'
console.log('Build completed successfully!');
process.exit(0);
EOF

# Commit and push
git add .
git commit -m "Test successful build"
git push origin main

Expected Results:

  1. Webhook triggered
  2. Workflow executes
  3. OpenHands builds successfully
  4. Gitea status: "success"
  5. Response: 200 OK

Success Criteria:

{
  "status": "SUCCESS",
  "attempts": 1,
  "gitea_status": "success"
}

Test 2: Retry Logic with Fixable Errors (45 min)

Setup:

# Clone test repo
git clone https://git.oky.sh/gitadmin/autonomous-build-test.git
cd autonomous-build-test

# Fix the syntax error in build.js
cat > build.js << 'EOF'
// Fixed version - proper closing braces
const express = require('express');
const app = express();

app.get('/', (req, res) => {
  res.send('Hello World!');  // Now properly closed
});

app.listen(3000, () => {
  console.log('Server running');
});
EOF

# Commit and push
git add .
git commit -m "Fix: Corrected syntax errors in build.js"
git push origin main

Expected Flow:

  1. 1st Attempt: Fails (syntax error)
  2. 2nd Attempt: OpenHands receives feedback, fixes error
  3. 3rd Attempt: Succeeds

Success Criteria:

{
  "status": "SUCCESS",
  "attempts": 3,
  "message": "Build completed successfully ✅",
  "gitea_status": "success"
}

Test 3: Max Retries with Persistent Errors (45 min)

Setup:

# Force persistent error
cat > build.js << 'EOF'
// This will always fail
console.error('Intentional error - will not fix');
process.exit(1);
EOF

git add .
git commit -m "Test: Persistent error (should fail after 3 attempts)"
git push origin main

Expected Flow:

  1. 1st Attempt: Fails
  2. 2nd Attempt: Fails
  3. 3rd Attempt: Fails
  4. Stops: Max retries exceeded

Success Criteria:

{
  "status": "FAILED",
  "attempts": 3,
  "max_attempts": 3,
  "message": "Build failed after 3 attempts ❌",
  "gitea_status": "failure"
}

Test 4: Real Project Build (45 min)

Setup: Use actual MVP project:

# Clone real project
git clone https://git.oky.sh/gitadmin/mvp-project.git
cd mvp-project

# Make a small change
echo "// Test change" >> README.md

# Commit and push
git add .
git commit -m "Test: Trigger autonomous build"
git push origin main

Success Criteria:

  • OpenHands executes full build
  • Dependencies installed correctly
  • Build completes or fails with clear errors
  • Retry logic works if needed

🚨 TROUBLESHOOTING GUIDE

Issue 1: Retry Count Always 0

Symptom: Workflow doesn't retry, always shows attempt 1

Solution: Check Node 3 configuration

// MUST initialize staticData
$workflow.staticData = $workflow.staticData || {};
$workflow.staticData.retry_count = ($workflow.staticData.retry_count || 0) + 1;

Issue 2: Gitea Status Not Updating

Symptom: Commit status stays "pending" or doesn't appear

Diagnosis:

# Check token permissions
curl -H "X-Gitea-Token: {YOUR_TOKEN}" \
  https://git.oky.sh/api/v1/user

# Should return user info

Solution:

  1. Ensure token has "repo" scope
  2. Check URL format: /api/v1/repos/{owner}/{repo}/statuses/{sha}
  3. Verify commit_sha is correct (40 character SHA)

Issue 3: Workflow Hangs After OpenHands

Symptom: Workflow stops at Node 4 or Node 5

Solution:

  1. Check Wait Node: Set to 10 seconds minimum
  2. Check SSH Timeout: Set to 300000ms (5 minutes)
  3. Check SSH Authentication: Test manually
ssh -i /home/bam/.ssh/n8n_key bam@localhost "echo 'SSH works'"

Issue 4: OpenHands Not Using Feedback

Symptom: Retry attempts show same errors

Solution: Check Node 4 command generation

// Must include previous error in task
if (retryCount > 0) {
  task += `

PREVIOUS BUILD FAILED:
${errorDetails}`;
}

Issue 5: Data Lost in Retry Loop

Symptom: Repository info missing on retry

Solution: Check data preservation in Node 4

return {
  ...repoData,              // ← CRITICAL: Preserve repo data
  code: sshOutput.code,
  stdout: sshOutput.stdout,
  stderr: sshOutput.stderr,
  status: 'SUCCESS'
};

📊 MONITORING & DEBUGGING

Enable Workflow Debugging

In n8n UI:

  1. Open workflow → Toggle "Save Manual Executions"
  2. View execution history
  3. Check each node's input/output

Key Metrics to Track

  • Success Rate: % of builds that succeed
  • Retry Efficiency: Avg attempts per build
  • Build Time: Duration from push to completion
  • Error Categories: Type of failures

Debug Commands

# Check OpenHands execution
tail -f /tmp/openhands_execution_*.log

# Test SSH manually
ssh -i /home/bam/.ssh/n8n_key bam@localhost \
  "sh /home/bam/openhands-sdk-wrapper-sh.sh 'Test task'"

# Check workflow status
curl -H "X-N8N-API-KEY: {TOKEN}" \
  https://n8n.oky.sh/api/v1/workflows/j1MmXaRhDjvkRSLa

⏱️ TIME-BOXED IMPLEMENTATION

Session 1 (2 hours): Core Workflow

  • Step 1: Setup test repository (20 min)
  • Step 2: Configure Gitea webhook (15 min)
  • Step 3: Add nodes 3, 7, 8, 9, 10 (45 min)
  • Test success path (30 min)
  • Buffer for issues (10 min)

Session 2 (2 hours): Retry Logic

  • Test failure path with fixable errors (45 min)
  • Test max retries (45 min)
  • Debug and fix issues (30 min)

Session 3 (1 hour): Real Project & Documentation

  • Test with real project (45 min)
  • Document workflow (15 min)

SUCCESS CRITERIA

Must Have:

  • End-to-end workflow completes (push → build → response)
  • OpenHands executes autonomously
  • Retry counter prevents infinite loops (max 3)
  • Error feedback improves retry attempts
  • Gitea commit status updates (success/failure)
  • Works with real projects

Verification:

# Test workflow manually
curl -X POST https://n8n.oky.sh/webhook/openhands-autonomous-build \
  -H "Content-Type: application/json" \
  -d '{
    "repository": {"name": "test-repo", "full_name": "gitadmin/test-repo"},
    "ref": "refs/heads/main",
    "after": "abc123def456"
  }'

Expected Response:

{
  "status": "SUCCESS",
  "attempts": 1,
  "repo": "test-repo",
  "branch": "main",
  "gitea_status": "success"
}

📚 REFERENCE FILES

  • Phase 3 Plan: /home/bam/claude/mvp-factory/phase3.md
  • SDK Wrapper: /home/bam/openhands-sdk-wrapper-sh.sh
  • n8n API Docs: /home/bam/claude/mvp-factory/n8n-api.md
  • Phase 2 Learnings: /home/bam/claude/mvp-factory/phase2.md
  • Data Preservation: /home/bam/claude/mvp-factory/N8N_DATA_PRESERVATION_SOLUTION.md

🎉 FINAL NOTES

Key Reminders:

  1. Data Preservation: Always use $node["Node Name"].json pattern
  2. Retry Counter: Initialize with $workflow.staticData = $workflow.staticData || {}
  3. SSH Timeout: Set to 5 minutes minimum
  4. Wait Node: 10 seconds between nodes
  5. Gitea Token: Must have "repo" scope for status updates

Workflow ID: j1MmXaRhDjvkRSLa Target Webhook: https://n8n.oky.sh/webhook/openhands-autonomous-build


Implementation Ready Estimated Total Time: 4-5 hours Can proceed immediately