wendy-cloud-iterate/SKILL.md
Autonomous continuous integration loop for the Wendy Cloud repository. Use when asked to: (1) iterate on the cloud stack, (2) find and fix bugs in Wendy Cloud, (3) run the cloud test loop, (4) continuously test the Swift broker, (5) scan for regressions after new features. This skill manages the full local dev stack autonomously including starting Docker, the Swift broker, pki-core, running tests, diagnosing failures, and applying fixes.
npx skillsauth add wendylabsinc/claude-skills wendy-cloud-iterateInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
This skill drives an autonomous continuous loop that starts the Wendy Cloud dev stack, runs the integration test suite, diagnoses any failures, attempts fixes, and self-paces based on results.
Run this block at the very start of every session before touching any code or starting any service. Fix every gap before proceeding.
#!/usr/bin/env bash
set -euo pipefail
MISSING=()
# --- Homebrew (needed to install everything else on macOS) ---
if ! command -v brew &>/dev/null; then
echo "Installing Homebrew..."
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
fi
# --- Docker Desktop ---
if ! command -v docker &>/dev/null; then
echo "Docker not found. Install Docker Desktop from https://www.docker.com/products/docker-desktop/ then re-run."
MISSING+=("Docker Desktop")
else
# Docker installed but daemon not running — start it
if ! docker info &>/dev/null 2>&1; then
echo "Starting Docker Desktop..."
open -a Docker
echo "Waiting for Docker to be ready..."
until docker info &>/dev/null 2>&1; do sleep 3; done
echo "Docker ready."
fi
fi
# --- GitHub CLI ---
if ! command -v gh &>/dev/null; then
echo "Installing gh..."
brew install gh
fi
# Check auth
if ! gh auth status &>/dev/null 2>&1; then
echo "gh is not authenticated. Run: gh auth login"
MISSING+=("gh auth (run: gh auth login)")
fi
# --- Swift toolchain ---
if ! command -v swift &>/dev/null; then
echo "Swift not found. Install Xcode or the Swift toolchain from https://swift.org/download/"
MISSING+=("Swift toolchain")
fi
# --- protoc (required for Swift broker build) ---
if ! command -v protoc &>/dev/null; then
echo "Installing protoc..."
brew install protobuf
fi
# --- wendy CLI ---
if ! command -v wendy &>/dev/null; then
echo "Installing wendy CLI..."
brew install wendy
fi
# --- pki-core repo ---
if [ ! -d "/Users/wendy/Documents/Projects/pki-core" ]; then
echo "Cloning pki-core..."
gh repo clone wendylabsinc/pki-core /Users/wendy/Documents/Projects/pki-core -- --depth=1
fi
# --- Cloud repo ---
if [ ! -d "/Users/wendy/Documents/Projects/cloud" ]; then
echo "Cloning cloud repo..."
gh repo clone wendylabsinc/cloud /Users/wendy/Documents/Projects/cloud
fi
# --- Chrome MCP ---
# The Chrome MCP extension enables autonomous browser control (needed for
# wendy auth login). Check by listing connected browsers.
# If the tool is unavailable, auth must be completed manually.
CHROME_MCP_AVAILABLE=true
if ! command -v google-chrome &>/dev/null && ! ls /Applications/Google\ Chrome.app &>/dev/null 2>&1; then
echo "WARNING: Google Chrome not found. Install Chrome and the Claude Chrome extension"
echo " to enable autonomous wendy auth login."
CHROME_MCP_AVAILABLE=false
fi
# --- Report ---
if [ ${#MISSING[@]} -gt 0 ]; then
echo ""
echo "BLOCKED: the following must be resolved manually before the loop can run:"
for item in "${MISSING[@]}"; do
echo " - $item"
done
exit 1
fi
echo "All prerequisites satisfied."
echo "Chrome MCP available: $CHROME_MCP_AVAILABLE"
If Chrome MCP is not available, wendy auth login must be completed manually by the user: open the URL printed by the CLI in a browser, select the org, and wait for "Certificates saved." before the loop proceeds.
You are fully autonomous in this skill. You may:
wendylabsinc org without askingmake target in any of the reposdocker compose commands (up, down, restart, logs)wendy CLI commandsgh pr createDo NOT ask for confirmation before any of the above. If something is missing (a repo, a binary, a config file), fix it autonomously.
The Wendy Cloud monorepo lives at /Users/wendy/Documents/Projects/cloud. Sibling repos:
| Repo | Location | Purpose |
|------|----------|---------|
| cloud | /Users/wendy/Documents/Projects/cloud | Main monorepo (dashboard, services, swift) |
| pki-core | /Users/wendy/Documents/Projects/pki-core | PKI certificate authority engine |
If pki-core is missing from disk, clone it:
gh repo clone wendylabsinc/pki-core /Users/wendy/Documents/Projects/pki-core -- --depth=1
| Port | Service | Notes | |------|---------|-------| | 50051 (host) | Swift broker | Plaintext gRPC | | 50052 (host) | Swift tunnel-broker | One-way TLS | | 50061 (host) | Go services (remapped in override) | Avoids host port conflict with Swift broker | | 9200 (host) | Dashboard | Next.js | | 9400 (host) | Envoy | gRPC-web proxy (Docker Compose) | | 9443 (host) | pki-core | Certificate issuance | | 9300 (host) | Postgres | Primary DB |
The Go services container does NOT bind host ports 50051/50052 — those belong to the Swift broker process. The docker-compose.override.yml remaps the services container to 50061/50062 for debugging and sets required dev environment variables. It is gitignored. Re-create it if missing (see below).
cd /Users/wendy/Documents/Projects/cloud
# 1. Start Docker if not running
open -a Docker
until docker info &>/dev/null; do sleep 2; done
# 2. Start Docker Compose services (Postgres, pki-core, dashboard, Envoy, Go services)
make dev &
sleep 30
# 3. Start the Swift broker on host (needs ports 50051 and 50052)
cd swift && ./scripts/start-local.sh > /tmp/swift-broker.log 2>&1 &
sleep 10
Verify all services are up:
lsof -iTCP:50051 -iTCP:50052 -iTCP:9200 -sTCP:LISTEN -nP 2>/dev/null | grep -E "50051|50052|9200"
If docker-compose.override.yml does not exist at the repo root, create it:
# Local dev override. Not committed — gitignored.
#
# Uses the real pki-core from the sibling repo at ../pki-core.
# pki-core is required for the Swift broker to issue device and user certificates.
#
# Enables dev auth: the dashboard shows a "Dev Login" button that sets a fixed
# fake JWT as the firebase-token cookie. The services backend accepts that token
# without verifying its signature when FIREBASE_AUTH_DISABLED=true.
# Run `make seed-dev` once after `make dev` to populate the dev user and org.
services:
pki-core:
build:
context: ../pki-core
dockerfile: Dockerfile
args: {}
healthcheck:
disable: true
dashboard:
environment:
- GRPC_SERVER_ENDPOINT=http://envoy:8080
- NEXT_PUBLIC_DEV_AUTH_ENABLED=true
- DEV_AUTH_ENABLED=true
- NEXT_PUBLIC_APP_URL=http://localhost:9200
services:
ports:
# Remap host ports so the Go services container can coexist with the
# Swift broker (which binds host :50051 and :50052 directly).
# Internal Docker routing (Envoy -> services:50051) is unaffected.
- "50061:50051"
- "50062:50052"
environment:
PKICORE_ENABLED: "false"
FIREBASE_AUTH_DISABLED: "true"
# Must match JWT_SECRET in swift/scripts/start-local.sh so the Swift
# broker can verify enrollment tokens issued by the Go services.
PROVISIONING_JWT_SECRET: "local-dev-jwt-secret-change-in-prod"
Key notes on the override:
PROVISIONING_JWT_SECRET in the services container must match JWT_SECRET in swift/scripts/start-local.sh (both default to "local-dev-jwt-secret-change-in-prod"). A mismatch causes wendy auth login to fail with "invalid or expired enrollment token".PKICORE_ENABLED: "false" in the services container is intentional — the Go services do not call pki-core directly; only the Swift broker does.docker-compose.yml does NOT bind host ports 50051/50052 for the services container. This is a committed change. Docker Compose merges port lists, so adding ports only in the override avoids a conflict with the Swift broker.The wendy CLI needs a local auth session to call the Swift broker. Run this once per session (the session persists in ~/.wendy/config.json):
cd /Users/wendy/Documents/Projects/cloud
wendy auth login --cloud http://localhost:9200 --cloud-grpc localhost:50051 --json > /tmp/wendy-auth-out.txt 2>&1 &
sleep 3
cat /tmp/wendy-auth-out.txt # shows the cli-auth URL with callback port
Then use the Chrome MCP tools to complete the flow:
http://localhost:9200/cli-auth?redirect_uri=http%3A%2F%2F127.0.0.1%3A<PORT>%2Fcli-callbackmcp__Claude_in_Chrome__navigate with url: "http://localhost:9200/cli-auth?redirect_uri=http://127.0.0.1:<PORT>/cli-callback"/login: click "Dev Login (local only)" at approximately coordinate (756, 508), wait 3s, re-navigate to the cli-auth URLcat /tmp/wendy-auth-out.txt should end with Certificates saved.Expected successful output:
Received enrollment token.
Authentication successful. Certificates saved.
cd /Users/wendy/Documents/Projects/cloud
make test-swift 2>&1 | tail -40
Filter to a specific test:
make test-swift FILTER=TestCreateAsset
Tests use BrokerFixture (in-process gRPC, MockPKIServer, real Postgres). No real Swift broker or pki-core needed for tests. Approximately 60 tests across 6 suites.
make test-services 2>&1 | tail -40
make test-swift — any failures are the primary signal.cat /tmp/swift-broker.log | grep -iE "error|fatal|panic" | tail -30docker compose logs --since 10m services 2>/dev/null | grep -iE "error|fatal|panic" | tail -20wendy discover --json 2>&1 | head -10 — verify Gerrit (wendyos-gerrit.local) is reachable if hardware testing is the goal.Every code change follows this sequence. Do NOT commit directly to the current branch.
Step 1: Identify the current branch
git -C /Users/wendy/Documents/Projects/cloud branch --show-current
# Note this as BASE_BRANCH
Step 2: Create a worktree for the fix
BASE=/Users/wendy/Documents/Projects/cloud
BRANCH=fix/<short-description> # e.g. fix/nil-bool-cast
git -C "$BASE" worktree add "$BASE/.worktrees/$BRANCH" -b "$BRANCH"
Step 3: Dispatch a subagent to implement and test the fix in the worktree
Give the subagent:
make test-swift FILTER=<TestName> inside the worktree to verifyThe subagent works entirely inside $BASE/.worktrees/$BRANCH and never touches the main checkout.
Step 4: UI smoke test and plan expansion
The UI smoke test plan lives in the cloud repo and is meant to grow. Your job is not only to execute it but to expand it when you find gaps.
Read the plan:
cat /Users/wendy/Documents/Projects/cloud/docs/testing/ui-smoke-test.md
Execute: Run all sections relevant to the change using Chrome MCP tools at http://localhost:9200. At minimum always run sections 1 (Authentication) and 6 (Console errors). Take a screenshot after each section.
Expand: After executing, identify gaps using the route grep in the plan's "Finding gaps" block:
grep -r "path:\|href=\|router.push\|<Link" \
/Users/wendy/Documents/Projects/cloud/dashboard/src \
--include="*.tsx" --include="*.ts" -h \
| grep -oE '"[/][^"]*"' | sort -u
Cross-reference with the coverage index table at the top of the plan. For any route or flow that appears in the source but not in the index, write a new section following the format in the "Adding new sections" block and add it to the plan file inside the current worktree. The additions travel with the pull request.
Attach screenshots to the pull request body as evidence of each executed section.
Step 5: Open a pull request and wait for CI
cd "$BASE/.worktrees/$BRANCH"
gh pr create --base BASE_BRANCH --title "fix: <description>" --body "..."
gh pr checks --watch # wait for all checks to pass
Do NOT proceed until gh pr checks reports all green. If CI fails, diagnose and push additional commits to the same branch.
Step 6: Merge and clean up
gh pr merge --squash --delete-branch
git -C "$BASE" worktree remove "$BASE/.worktrees/$BRANCH"
Only after the pull request is merged does the fix land on the current branch.
When to open a Linear issue instead of fixing inline:
| Symptom | Likely Cause | Fix |
|---------|-------------|-----|
| Bool? nil creates OID 0 params | PostgresNIO can't infer type for nil Bool | Use \(value)::bool explicit cast |
| connection pool exhausted | Postgres default max_connections (100) hit | Set pool size to 1 in TestDB.swift |
| Handler not registered in BrokerFixture | Handler created but never added to test server | Register handler in BrokerFixture.swift |
| Address already in use on :50051 or :50052 | Previous broker process still running | pkill -f start-local.sh; pkill -f /.build/.*broker; sleep 2 |
| invalid or expired enrollment token | PROVISIONING_JWT_SECRET in services container does not match JWT_SECRET in swift/scripts/start-local.sh | Set PROVISIONING_JWT_SECRET: "local-dev-jwt-secret-change-in-prod" in docker-compose.override.yml services environment |
| unknown profile "operator-tier-a" | pki-core profile name mismatch; operator certs must use profile "operator" | Fixed in CertificateServiceHandler.swift (both IssueCertificate and RefreshCertificate) |
| certificate is not valid for client authentication | Production CLI cert missing clientAuth Extended Key Usage | Known cloud PKI bug; use local stack for testing |
| pki-core unavailable at startup | pki-core repo not cloned | Clone to /Users/wendy/Documents/Projects/pki-core and ensure override builds from ../pki-core |
After each iteration, schedule the next wake-up based on what was found:
| Outcome | Next check | |---|---| | Failures found and fixed | 5 minutes (confirm the fix held) | | Failures found, not fixed | 10 minutes (allow time for manual review) | | Everything clean | 30 minutes |
The loop terminates itself after 3 consecutive clean iterations — no test failures, no broker errors, no new smoke test gaps, device reachable or skipped.
To end the loop, simply do not schedule the next wake-up. Log a termination message
and stop. The loop resumes on the next explicit /loop invocation.
[wendy-cloud-iterate] 3 consecutive clean scans. Nothing left to fix.
Terminating loop. Run again with /loop to restart.
Track the consecutive clean count in the iteration summary. Reset it to 0 whenever a failure is found or a fix is applied, even if the fix succeeds.
The threshold of 3 is intentional: one clean scan after a fix is not enough to confirm stability (the next test run could expose a regression the fix introduced). Three clean scans at 30-minute intervals means the stack has been stable for 90 minutes, which is a reasonable confidence threshold before handing back control.
If you want to run indefinitely (e.g. leaving the loop running overnight), pass
no-auto-terminate as part of the invocation prompt:
/loop Use the wendy-cloud-iterate skill to continuously scan the Wendy Cloud
repository. Do not auto-terminate.
Log this at the end of every iteration:
[wendy-cloud-iterate] Iteration N complete.
Tests: X passed, Y failed
Fixes applied: <list or "none">
Smoke test: <sections run> / <gaps added: N>
Device: <reachable / unreachable / skipped>
Consecutive clean: Z/3
Next check: Xm (or "loop terminated")
Device tests verify that a real WendyOS device can be reached and responds correctly.
They are optional — skip if no device is connected or if the production cert lacks
clientAuth Extended Key Usage (a known cloud PKI bug).
If a device was named in the conversation (e.g. "use gerrit", "test against my Jetson"), use that hostname directly.
Otherwise, discover what is on the network:
wendy discover --json 2>&1
In autonomous loop mode, use the first device returned by discover. If the list is empty, log "no device found — skipping device tests" and continue.
In interactive mode (user is present), present the discovered list and ask which device to use before proceeding.
Store the chosen hostname as DEVICE_HOST (just the hostname, no port):
DEVICE_HOST=<hostname> # e.g. wendyos-gerrit.local or 192.168.1.42
# Verify reachability on the plaintext port
wendy discover --json 2>&1 | grep "$DEVICE_HOST"
# Check device info (requires valid clientAuth cert)
wendy cloud device info --device "$DEVICE_HOST" --json 2>&1 | head -20
If device info fails with "certificate is not valid for client authentication",
skip the mTLS checks — this is the known production PKI bug, not a regression.
development
Expert guidance on Swift best practices, patterns, and implementation. Use when developers mention: (1) Swift configuration or environment variables, (2) swift-log or logging patterns, (3) OpenTelemetry or swift-otel, (4) Swift Testing framework or @Test macro, (5) Foundation avoidance or cross-platform Swift, (6) platform-specific code organization, (7) Span or memory safety patterns, (8) non-copyable types (~Copyable), (9) API design patterns or access modifiers.
tools
Expert guidance on building and deploying apps to WendyOS edge devices. Use when developers mention: (1) Wendy or WendyOS, (2) wendy CLI commands, (3) wendy.json or entitlements, (4) deploying apps to edge devices, (5) remote debugging Swift on ARM64, (6) NVIDIA Jetson or Raspberry Pi apps, (7) cross-compiling Swift for ARM64.
development
Curated Swift package ecosystem for WendyOS and Linux. Use when developers mention: (1) Swift packages for Linux or ARM64/AMD64, (2) choosing a Swift library, (3) Swift Package Index, (4) swiftpackageindex.com, (5) what Swift library to use, (6) Swift on WendyOS dependencies, (7) edge computing Swift libraries.
development
Expert guidance on building WASM apps for Wendy Lite MCU firmware on ESP32-C6. Use when developers mention: (1) Wendy Lite or wendy-lite, (2) WASM apps on ESP32 or microcontrollers, (3) WendyLite Swift package or import WendyLite, (4) building C/Rust/Swift/Zig apps for ESP32, (5) WAMR runtime on embedded devices, (6) GPIO/I2C/SPI/UART/NeoPixel from WASM, (7) Embedded Swift on WASM or wasm32-none-none-wasm, (8) BLE provisioning on ESP32-C6, (9) uploading WASM binaries to MCU, (10) TLS/networking on ESP32 from Swift.