Skip to content

Instantly share code, notes, and snippets.

@RupertBarrow
Created March 29, 2026 09:15
Show Gist options
  • Select an option

  • Save RupertBarrow/917933fbd7c1e68b8c558f5a740b3aca to your computer and use it in GitHub Desktop.

Select an option

Save RupertBarrow/917933fbd7c1e68b8c558f5a740b3aca to your computer and use it in GitHub Desktop.
RSH-005: Replace GHA CI/Release with Lobster Pipelines — Research

RSH-005 — Replace GHA CI/Release with Lobster Pipelines

Author: Claudia
Date: 2026-03-29
Status: Research
Project: RSH-005
Related: PRJ-039 (CI/CD Pipeline)


1. Executive Summary

This research evaluates replacing GitHub Actions (GHA) CI and Release workflows with OpenClaw Lobster pipelines for our two private repos (rapido-fab, openclaw-channel-github). The goal: zero GHA minutes, zero LLM tokens, event-driven local execution with built-in approval gates.

Verdict: Fully feasible. Every GHA step maps to a Lobster command. The migration is straightforward with clear benefits and manageable risks.


2. Current State

2.1 Repositories

Repo Visibility GHA Runs (March 2026) Workflows
rapido-fab Private ~250 ci.yml, release.yml
openclaw-channel-github Private ~34 ci.yml, release.yml

Both repos consume GHA minutes from the 2,000 min/month free tier for private repos.

2.2 Current GHA Workflows

CI (both repos) — triggers on push to main:

  1. Checkout → Node 22 setup → npm install (+ workspace packages)
  2. npm run build
  3. Tests (currently disabled/commented out in both repos)
  4. Changeset enforcement (fail if no .changeset/*.md present)
  5. Upload dist/ artifact (retention: 30 days)

Release (rapido-fab) — nightly cron 5am CET OR manual dispatch:

  1. Check for pending changesets (skip if none)
  2. changeset version (or --snapshot rc for RC channel)
  3. Rebuild with new version
  4. npm publish (with @rc or @latest tag)
  5. Git commit version bump + tag + push
  6. gh release create with tarball

Release (openclaw-channel-github) — triggered by CI workflow_run success:

  1. Same flow as rapido-fab but without nightly/manual modes
  2. Downloads CI artifacts, rebuilds with new version
  3. Publishes, commits, tags, creates GH Release

2.3 Key Characteristics

  • Total GHA time per CI run: ~1-2 min
  • Total GHA time per Release run: ~2-3 min
  • ~284 runs/month across both repos → ~400-600 GHA minutes/month
  • Tests are disabled in CI (TODO comments in both workflows)
  • Changeset enforcement is the primary CI gate
  • Release uses @changesets/cli for version management
  • npm publish requires NPM_TOKEN secret
  • GH Release creation requires GH_PAT secret

3. Lobster Capabilities Assessment

3.1 What Lobster Can Do

Lobster is a typed workflow shell for OpenClaw. Key features:

Feature Details
exec Run any shell command (npm, git, gh, etc.)
Pipeline chaining cmd1 | cmd2 | cmd3 with JSON piping
Approval gates approve --prompt "Publish?" — pauses pipeline, returns resumeToken
Workflow files .lobster YAML files with steps, condition, approval
State management state.get / state.set for persisting run data
clawd.invoke Call OpenClaw tools (e.g., message for notifications)
Resume tokens Halted workflows resume without re-running earlier steps
Tool mode --mode tool returns structured JSON envelope
Timeouts timeoutMs per pipeline
Dedup Built-in deduplication command

3.2 Built-in Workflows

Lobster ships with github.pr.monitor and github.pr.monitor.notify — already CI-adjacent.

3.3 What Lobster Cannot Do (vs GHA)

GHA Feature Lobster Equivalent Gap?
Remote execution (ubuntu VM) Local execution on Claudia's Mac Different, not worse
Artifact upload/download Not needed — build and publish in same pipeline ✅ None
Matrix builds (multi-OS) Not applicable (we only target one env) ✅ None
Secrets management Environment variables on local host ⚠️ Less isolated
workflow_run chaining Pipeline steps / cron triggers ✅ Equivalent
Concurrency groups Lock files ⚠️ Manual implementation
$GITHUB_STEP_SUMMARY Notification via clawd.invoke → message tool ✅ Better (Telegram/Slack)
Pull request status checks No native GH status API integration ⚠️ Gap (see §5.2)

4. Proposed Architecture

4.1 Two Pipelines, Two Triggers Each

┌─────────────────────────────────────────────────┐
│                  TRIGGERS                        │
├──────────────────────┬──────────────────────────┤
│  GitHub App webhook  │  devops-skill post-merge │
│  (push to main)      │  (agent merges PR)       │
└──────────┬───────────┴────────────┬─────────────┘
           │                        │
           ▼                        ▼
    ┌──────────────┐         ┌──────────────┐
    │  ci.lobster  │         │  ci.lobster  │
    │  (webhook)   │         │  (immediate) │
    └──────────────┘         └──────────────┘
           │ dedup by commit SHA
           ▼
    Build → Test → Changeset check → Notify
    
    ┌───────────────────┐    ┌───────────────────┐
    │  OpenClaw cron    │    │  Manual trigger    │
    │  (nightly 5am)    │    │  (agent/human)     │
    └─────────┬─────────┘    └─────────┬─────────┘
              │                        │
              ▼                        ▼
       ┌─────────────────┐     ┌─────────────────┐
       │ release.lobster │     │ release.lobster  │
       │ (production)    │     │ (rc channel)     │
       └─────────────────┘     └─────────────────┘
              │
              ▼
       Version → Build → [APPROVE] → Publish → Tag → GH Release → Notify

4.2 CI Pipeline (ci.lobster)

name: ci
args:
  repo: { required: true }
  repoPath: { required: true }
  commitSha: { default: "HEAD" }
steps:
  - id: lock
    command: |
      LOCK="/tmp/.lobster-ci-${repo}.lock"
      if [ -f "$LOCK" ] && [ "$(cat $LOCK)" = "${commitSha}" ]; then
        echo '{"skip":true,"reason":"already running for this SHA"}'
        exit 0
      fi
      echo "${commitSha}" > "$LOCK"
      echo '{"skip":false}'
    
  - id: pull
    command: cd ${repoPath} && git pull origin main --ff-only
    condition: $lock.stdout.skip == false

  - id: install
    command: cd ${repoPath} && npm install
    condition: $lock.stdout.skip == false

  - id: build
    command: cd ${repoPath} && npm run build
    condition: $lock.stdout.skip == false

  - id: test
    command: cd ${repoPath} && npm test 2>&1 || true
    condition: $lock.stdout.skip == false

  - id: changeset-check
    command: |
      cd ${repoPath}
      COUNT=$(find .changeset -name '*.md' ! -name 'README.md' 2>/dev/null | wc -l | tr -d ' ')
      echo "{\"count\":${COUNT}}"
    condition: $lock.stdout.skip == false

  - id: notify
    command: |
      clawd.invoke --tool message --action send --args-json '{
        "channel": "telegram",
        "to": "-1003328295368",
        "message": "✅ CI passed for ${repo} (${commitSha:0:7}). Changesets: ${changeset_count}"
      }'
    condition: $lock.stdout.skip == false

  - id: cleanup
    command: rm -f "/tmp/.lobster-ci-${repo}.lock"

4.3 Release Pipeline (release.lobster)

name: release
args:
  repo: { required: true }
  repoPath: { required: true }
  channel: { default: "production" }
steps:
  - id: check-changesets
    command: |
      cd ${repoPath}
      COUNT=$(find .changeset -name '*.md' ! -name 'README.md' 2>/dev/null | wc -l | tr -d ' ')
      if [ "$COUNT" -eq 0 ]; then
        echo '{"proceed":false,"reason":"no pending changesets"}'
      else
        echo '{"proceed":true,"count":'$COUNT'}'
      fi

  - id: version
    command: |
      cd ${repoPath}
      VER_BEFORE=$(node -e "console.log(require('./package.json').version)")
      if [ "${channel}" = "production" ]; then
        npm run version-packages
      else
        npx changeset version --snapshot "${channel}"
      fi
      VER_AFTER=$(node -e "console.log(require('./package.json').version)")
      echo "{\"before\":\"${VER_BEFORE}\",\"after\":\"${VER_AFTER}\"}"
    condition: $check-changesets.stdout.proceed == true

  - id: build
    command: cd ${repoPath} && npm run build
    condition: $check-changesets.stdout.proceed == true

  - id: approve-publish
    command: echo "Ready to publish ${repo} v${version_after} (channel: ${channel})"
    approval: required
    condition: $check-changesets.stdout.proceed == true

  - id: publish
    command: |
      cd ${repoPath}
      if [ "${channel}" = "production" ]; then
        npm publish
      else
        npm publish --tag "${channel}"
      fi
    condition: $approve-publish.approved

  - id: commit-tag
    command: |
      cd ${repoPath}
      VER=$(node -e "console.log(require('./package.json').version)")
      git add package.json openclaw.plugin.json CHANGELOG.md .changeset/
      git commit -m "chore: release v${VER} [skip ci]" || true
      git tag "v${VER}" 2>/dev/null || true
      git push origin main --tags
    condition: $approve-publish.approved

  - id: gh-release
    command: |
      cd ${repoPath}
      VER=$(node -e "console.log(require('./package.json').version)")
      PRERELEASE=""
      [ "${channel}" != "production" ] && PRERELEASE="--prerelease"
      gh release create "v${VER}" --title "v${VER}" --generate-notes $PRERELEASE
    condition: $approve-publish.approved

  - id: notify
    command: |
      clawd.invoke --tool message --action send --args-json '{
        "channel": "telegram",
        "to": "-1003328295368",
        "message": "📦 Released ${repo} v${version} (${channel})"
      }'
    condition: $approve-publish.approved

4.4 Trigger Integration

Trigger A — GitHub App Webhook: In openclaw-channel-github plugin, add a handler for push events on main:

// In webhook handler
if (event === 'push' && payload.ref === 'refs/heads/main') {
  const repo = payload.repository.name;
  const repoPath = REPO_PATHS[repo]; // config map
  exec(`lobster run ci.lobster --args-json '{"repo":"${repo}","repoPath":"${repoPath}","commitSha":"${payload.after}"}'`);
}

Trigger B — devops-skill post-merge hook: After gh pr merge, the devops-skill adds:

lobster run ci.lobster --args-json '{"repo":"rapido-fab","repoPath":"/Users/claudia/Documents/WORK/REPOS/rapido-fab","commitSha":"'"$(git rev-parse HEAD)"'"}'

Trigger C — Nightly release cron: OpenClaw cron release-nightly-rapido-fab at 5am CET:

{
  "name": "release-nightly-rapido-fab",
  "schedule": "cron 0 4 * * * @ Europe/Paris",
  "type": "lobster",
  "pipeline": "release.lobster",
  "args": {"repo": "rapido-fab", "repoPath": "/Users/claudia/Documents/WORK/REPOS/rapido-fab", "channel": "production"}
}

Trigger D — Manual release (agent or human):

lobster run release.lobster --args-json '{"repo":"rapido-fab","repoPath":"...","channel":"rc"}'

5. Pros and Cons

5.1 Pros

# Pro Impact
1 Zero GHA minutes Saves ~400-600 min/month from 2,000 free tier. Eliminates future billing risk as repos/runs grow
2 Zero LLM tokens Lobster is pure bash — no AI model involved
3 Faster execution No VM cold-start (~30-60s on GHA). Local build: ~10-30s
4 Approval gates Lobster approve is first-class. GHA has no native approval for npm publish. Currently we publish blindly
5 Event-driven GitHub App webhook fires immediately on push. No polling
6 Dual trigger redundancy Webhook + devops-skill hook = nothing slips through
7 Better notifications clawd.invoke sends results to Telegram/Slack directly. GHA requires separate notification action
8 Unified toolchain Everything in OpenClaw ecosystem. No context-switching to GHA YAML
9 Simpler debugging Run lobster run ci.lobster locally to debug. GHA debugging = push-and-pray
10 Resume on failure Lobster resumeToken means you can fix an issue and continue from where it failed
11 CI + Release decouple naturally Two separate .lobster files, each independently triggered
12 Consistent with Rapido strategy Rupert directive: "formalise as many automated procedures as possible as lobster pipelines"

5.2 Cons / Risks

# Con Severity Mitigation
1 Single point of failure High If Claudia's Mac is offline, no CI/release runs. GHA runs on GitHub's infra
2 No PR status checks Medium GHA sets commit status (✅/❌) on PRs. Lobster doesn't natively update GitHub commit status. PRs won't show "CI passed" badge
3 Secrets on local machine Low NPM_TOKEN and GH_PAT live as env vars on Claudia's Mac instead of GHA encrypted secrets. Already the case for the deploy cron
4 No parallel/matrix builds Low Not needed — single Node version, single OS target
5 Lock file dedup is fragile Low File-based locks can get stale on crash. Need cleanup logic
6 Webhook must be reliable Medium Tailscale Funnel must stay up. GitHub retries failed webhooks 3x, but extended downtime = missed events
7 No GHA marketplace actions Low We only use checkout, setup-node, upload-artifact, download-artifact — all replaceable
8 Lobster workflow file format Low The .lobster YAML spec is young. Step conditions, variable interpolation, and approval flow may have edge cases
9 No audit trail on GitHub Medium GHA runs are visible in the repo's Actions tab. Lobster runs are local. Need explicit logging
10 npm publish from local Low Publishing from a developer machine is common in small teams. Npm 2FA (if enabled) could block automation

5.3 Comparison Matrix

Dimension GHA Lobster
Cost (minutes) ~400-600/month 0
Cost (tokens) 0 0
Execution speed ~1-3 min (VM spin-up) ~10-30s (local)
Trigger latency ~5-15s ~1-3s (webhook), 0s (post-merge)
Approval gates ❌ None ✅ Built-in
Reliability ✅ GitHub infra (99.9%) ⚠️ Single Mac
PR status checks ✅ Native ❌ Needs gh api call
Debugging ❌ Push-and-pray ✅ Run locally
Audit trail ✅ Actions tab ⚠️ Must log explicitly
Notifications ⚠️ Extra action needed ✅ clawd.invoke native
Secrets isolation ✅ Encrypted vault ⚠️ Env vars

6. Open Questions

6.1 Architecture

  1. Where do .lobster files live? Options:

    • (a) In each repo root (.lobster/ci.lobster, .lobster/release.lobster) — versioned with the code
    • (b) Centralized in rapido-library/pipelines/ — single source of truth
    • (c) In OpenClaw config dir (~/.openclaw/pipelines/)

    Recommendation: (a) — keeps pipeline definition next to the code it builds, like .github/workflows/

  2. PR status checks — should we add gh api calls to set commit status from Lobster? This would restore the "CI passed" badge on PRs. Example:

    gh api repos/{owner}/{repo}/statuses/{sha} \
      -f state=success -f context="lobster/ci" -f description="Build and tests passed"
  3. Should we keep GHA as a dormant fallback? If yes: rename workflows to ci.disabled.yml / release.disabled.yml so they're available but don't trigger. Can be re-enabled by renaming back.

6.2 Approval Gate Design

  1. Who approves npm publish?

    • (a) Rupert via Telegram inline button (resumeToken → approve/deny)
    • (b) Auto-approve for nightly production, require approval for RC only
    • (c) Always auto-approve (trust the pipeline)
    • (d) Claudia approves (agent judgment: did CI pass? are changesets valid?)

    Recommendation: (b) — nightly production is already an unattended cron today. RC is manual/intentional, so approval adds value there.

  2. Approval timeout — if no one approves within N hours, should the pipeline auto-cancel or auto-approve?

6.3 Trigger Mechanics

  1. Webhook dedup — when both triggers fire for the same push, how exactly to dedup?

    • Lock file with commit SHA (proposed above)
    • Lobster state.set / state.get (built-in, more robust)
    • Let both run — npm publish is idempotent (E409 on duplicate version)
  2. openclaw-channel-github integration — the plugin currently only handles issues/PRs/comments. Adding push event handling requires:

    • Subscribe to push events in GitHub App settings
    • Add handler in src/webhook/ router
    • Map repo name → local path (config)

    Is this in scope for PRJ-027 (channel plugin fixes) or a separate task?

  3. devops-skill integration — the post-merge hook needs to:

    • Know which repo was just merged
    • Find the .lobster path
    • Call lobster run

    This means the devops-skill needs a repo→path mapping. Same config as the webhook handler?

6.4 Operational

  1. Logging — where do Lobster run logs go? Options:

    • (a) OpenClaw gateway logs (if invoked via tool)
    • (b) Dedicated log dir (~/.openclaw/lobster-logs/)
    • (c) Lobster state (queryable later)
  2. Failure notification — CI failure should alert immediately. Release failure is more critical. Notification targets: Telegram group? Slack #inbox-actions? Both?

  3. NPM_TOKEN availability — is the token already in Claudia's environment, or does it need to be set up?

  4. Nightly release crondeploy-nightly already exists for rapido-fab (installs + restarts gateway). Should the release cron be separate from deploy, or merged?

    • Current: GHA releases → deploy-nightly installs
    • Proposed: Lobster releases locally → deploy-nightly just restarts gateway
    • Or: merge into one pipeline (release + deploy)?

7. Migration Plan (if approved)

Phase 1: CI Pipeline (low risk)

  1. Write ci.lobster for rapido-fab
  2. Test locally: lobster run ci.lobster --args-json '...'
  3. Add push webhook handler to openclaw-channel-github
  4. Add post-merge hook to devops-skill
  5. Run both GHA and Lobster in parallel for 1 week
  6. If Lobster is reliable → disable GHA CI (rename to .disabled.yml)

Phase 2: Release Pipeline (medium risk)

  1. Write release.lobster for rapido-fab
  2. Test with RC channel first (--channel rc)
  3. Wire up nightly cron to Lobster instead of GHA
  4. Run one production release via Lobster (manually monitored)
  5. If successful → disable GHA Release
  6. Repeat for openclaw-channel-github

Phase 3: Polish

  1. PR status checks via gh api
  2. Structured logging
  3. Failure alerting
  4. Documentation update

Estimated effort: 2-3 days for Phase 1+2, 1 day for Phase 3.


8. Recommendation

Proceed. The benefits (zero cost, faster, approval gates, unified toolchain) outweigh the risks (single machine, no PR status badges). The migration can be done incrementally with a parallel-run phase to validate reliability.

Key prerequisite: The openclaw-channel-github plugin must be working (PRJ-027 Phase 1) to receive webhook push events. This research should feed into PRJ-039 as a step, with PRJ-027 as a dependency.

Suggested next steps:

  1. Fix PRJ-027 (make the GitHub plugin work) — already planned
  2. Add this as PRJ-039 Step S0X: "Lobster CI/Release pipelines"
  3. Create implementation tasks for the two pipeline files + trigger integrations
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment