Skip to content

feat: implement worker core, tests, bot, CI/CD, and Dockerfile#15

Merged
abrichr merged 10 commits intomainfrom
feat/worker-core-and-tests
Mar 2, 2026
Merged

feat: implement worker core, tests, bot, CI/CD, and Dockerfile#15
abrichr merged 10 commits intomainfrom
feat/worker-core-and-tests

Conversation

@abrichr
Copy link
Member

@abrichr abrichr commented Mar 1, 2026

Summary

Full implementation of the wright dev automation worker, ported from fastable2's Ralph Loop pattern and generalized for any repo/language.

Worker Core (6 files, ~1400 lines)

  • queue-poller.ts: Supabase job queue polling with atomic claiming, stale recovery, graceful SIGTERM requeue
  • dev-loop.ts: Ralph Loop orchestrator — clone → detect → install → Claude loop → test → PR
  • claude-session.ts: Claude Agent SDK wrapper (spawns Claude Code via PTY)
  • test-runner.ts: Auto-detects test runner (pytest/jest/vitest/playwright/go/cargo) and package manager (uv/pip/npm/pnpm/yarn/cargo/go) from repo files
  • github-ops.ts: Clone, branch, commit, push, create PR
  • index.ts: Express HTTP server with health/drain/cancel + scale-to-zero idle shutdown

Tests (53 passing)

 ✓ test-runner.test.ts          (30 tests) — detection + execution
 ✓ test-runner-parsers.test.ts  ( 4 tests) — pytest/jest/go/cargo parsing
 ✓ github-ops.test.ts           ( 4 tests) — branch + commit with real git
 ✓ dev-loop.test.ts             ( 5 tests) — full pipeline with mocked externals
 ✓ queue-poller.test.ts         ( 6 tests) — state management
 ✓ index.test.ts                ( 5 tests) — constants + server config

 Test Files  6 passed (6)
      Tests  53 passed (53)

Telegram Bot

  • grammY framework with /start, /task, /status, /cancel commands
  • Supabase realtime subscription streaming job events to Telegram
  • Inline keyboard for PR approve/reject

Infrastructure

  • Production multi-stage Dockerfile (Node 22, Python/uv, Go, Rust)
  • GitHub Actions CI (lint + test + build) and Fly.io deploy
  • Updated SQL migration with retry + token columns

Test plan

  • All 53 unit/integration tests passing locally
  • CI pipeline validates on push
  • Manual test: submit a job via Telegram bot
  • Manual test: verify scale-to-zero on Fly.io

🤖 Generated with Claude Code

abrichr and others added 4 commits March 1, 2026 18:58
Worker (apps/worker):
- Queue poller with atomic claiming, stale recovery, graceful SIGTERM requeue
- Dev loop (Ralph Loop): clone → detect → install → Claude loop → test → PR
- Claude Agent SDK wrapper for spawning Claude Code sessions
- Test runner auto-detection for pytest, jest, vitest, playwright, go, cargo
- Git operations: clone, branch, commit, push, create PR
- HTTP server with health, drain, cancel endpoints + scale-to-zero
- 53 tests across 6 test files, all passing

Telegram Bot (apps/bot):
- grammY-based bot with /start, /task, /status, /cancel commands
- Supabase realtime subscription for streaming job events to chat
- Inline keyboard for PR approve/reject actions

Infrastructure:
- Production multi-stage Dockerfile (Node 22, Python/uv, Go, Rust)
- GitHub Actions CI (lint + test + build) and deploy (Fly.io)
- Updated shared types (retry fields, DevLoopConfig, DevLoopResult)
- Updated SQL migration (attempt, max_attempts, github_token columns)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix command injection in createPullRequest (use execFileSync instead of execSync)
- Add try-catch to installDependencies with proper error propagation
- Use WORKSPACE_DIR env var instead of hardcoded /tmp path in dev-loop
- Add GitHub CLI (gh) to Dockerfile for PR creation
- Copy .dockerignore to repo root for proper Docker build context
- Add composite: true to shared tsconfig for project references
- Fix migration to use gen_random_uuid() instead of uuid_generate_v4()
- Add Supabase config files

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The @anthropic-ai/claude-agent-sdk spawns a Claude Code subprocess
via the query() function. The CLI must be globally installed.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@abrichr abrichr mentioned this pull request Mar 2, 2026
2 tasks
abrichr and others added 5 commits March 2, 2026 01:33
Update stale OpenAdaptAI/wright URLs to OpenAdaptAI/openadapt-wright
in both README.md and dev-loop.ts.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Test runners emit colored output that broke our regex parsers,
causing 0/0 results even when tests were actually passing.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Vitest uses "Tests  2 passed (2)" format while Jest uses "Tests: 2 failed, 5 passed, 7 total".
The parser now handles both formats, fixing the 0/0 results seen in production.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- cloneRepo now accepts an optional branch parameter to checkout the
  correct base branch instead of always cloning the default branch
- dev-loop passes job.branch to cloneRepo so auto-detection works
  against the right codebase
- parseJest now handles vitest's output format ("Tests  2 passed (2)")
  in addition to jest's format ("Tests: 2 failed, 5 passed, 7 total")

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Fly.io's auto_stop_machines was killing the worker during long Claude
sessions because execSync blocks the event loop, preventing health
checks from responding. Now the worker manages its own lifecycle
via the 5-minute idle timer (process.exit(0)).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: security and correctness fixes for worker and bot

- Allowlist env vars passed to Claude subprocess (prevent leaking
  SUPABASE_SERVICE_ROLE_KEY, BOT_TOKEN, GITHUB_TOKEN, etc.)
- Wire AbortController from queue-poller through dev-loop to
  claude-session for graceful SIGTERM cancellation
- Add github_token to bot insertJob (fixes NOT NULL constraint failure)
- Add Telegram chat ID allowlist middleware (ALLOWED_TELEGRAM_USERS)
- Add @types/node to shared and bot packages (fixes pre-existing
  build failures)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: add TMPDIR to env allowlist and use once for abort listener

- Add TMPDIR/TMP/TEMP to allowed env vars (git/npm need temp dirs)
- Use { once: true } on abort signal listener to prevent accumulation
  across loop iterations

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: simplify abortController pass-through and filter NaN from allowlist

- Pass abortController directly instead of conditional spread
- Filter non-finite values from ALLOWED_TELEGRAM_USERS parsing

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant