Author: Claudia
Date: 2026-03-29
Status: Research
Project: RSH-005
Related: PRJ-039 (CI/CD Pipeline)
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.
| 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.
CI (both repos) — triggers on push to main:
- Checkout → Node 22 setup →
npm install(+ workspace packages) npm run build- Tests (currently disabled/commented out in both repos)
- Changeset enforcement (fail if no
.changeset/*.mdpresent) - Upload
dist/artifact (retention: 30 days)
Release (rapido-fab) — nightly cron 5am CET OR manual dispatch:
- Check for pending changesets (skip if none)
changeset version(or--snapshot rcfor RC channel)- Rebuild with new version
npm publish(with@rcor@latesttag)- Git commit version bump + tag + push
gh release createwith tarball
Release (openclaw-channel-github) — triggered by CI workflow_run success:
- Same flow as rapido-fab but without nightly/manual modes
- Downloads CI artifacts, rebuilds with new version
- Publishes, commits, tags, creates GH Release
- 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/clifor version management - npm publish requires
NPM_TOKENsecret - GH Release creation requires
GH_PATsecret
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 |
Lobster ships with github.pr.monitor and github.pr.monitor.notify — already CI-adjacent.
| 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 | |
workflow_run chaining |
Pipeline steps / cron triggers | ✅ Equivalent |
| Concurrency groups | Lock files | |
$GITHUB_STEP_SUMMARY |
Notification via clawd.invoke → message tool |
✅ Better (Telegram/Slack) |
| Pull request status checks | No native GH status API integration |
┌─────────────────────────────────────────────────┐
│ 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
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"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.approvedTrigger 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"}'| # | 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" |
| # | 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 |
| 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%) | |
| PR status checks | ✅ Native | ❌ Needs gh api call |
| Debugging | ❌ Push-and-pray | ✅ Run locally |
| Audit trail | ✅ Actions tab | |
| Notifications | ✅ clawd.invoke native | |
| Secrets isolation | ✅ Encrypted vault |
-
Where do
.lobsterfiles 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/ - (a) In each repo root (
-
PR status checks — should we add
gh apicalls 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" -
Should we keep GHA as a dormant fallback? If yes: rename workflows to
ci.disabled.yml/release.disabled.ymlso they're available but don't trigger. Can be re-enabled by renaming back.
-
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.
-
Approval timeout — if no one approves within N hours, should the pipeline auto-cancel or auto-approve?
-
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 publishis idempotent (E409 on duplicate version)
-
openclaw-channel-github integration — the plugin currently only handles issues/PRs/comments. Adding
pushevent handling requires:- Subscribe to
pushevents 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?
- Subscribe to
-
devops-skill integration — the post-merge hook needs to:
- Know which repo was just merged
- Find the
.lobsterpath - Call
lobster run
This means the devops-skill needs a repo→path mapping. Same config as the webhook handler?
-
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)
-
Failure notification — CI failure should alert immediately. Release failure is more critical. Notification targets: Telegram group? Slack #inbox-actions? Both?
-
NPM_TOKEN availability — is the token already in Claudia's environment, or does it need to be set up?
-
Nightly release cron —
deploy-nightlyalready 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)?
- Write
ci.lobsterfor rapido-fab - Test locally:
lobster run ci.lobster --args-json '...' - Add push webhook handler to openclaw-channel-github
- Add post-merge hook to devops-skill
- Run both GHA and Lobster in parallel for 1 week
- If Lobster is reliable → disable GHA CI (rename to
.disabled.yml)
- Write
release.lobsterfor rapido-fab - Test with RC channel first (
--channel rc) - Wire up nightly cron to Lobster instead of GHA
- Run one production release via Lobster (manually monitored)
- If successful → disable GHA Release
- Repeat for openclaw-channel-github
- PR status checks via
gh api - Structured logging
- Failure alerting
- Documentation update
Estimated effort: 2-3 days for Phase 1+2, 1 day for Phase 3.
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:
- Fix PRJ-027 (make the GitHub plugin work) — already planned
- Add this as PRJ-039 Step S0X: "Lobster CI/Release pipelines"
- Create implementation tasks for the two pipeline files + trigger integrations