Sling — Operator & API Reference
Start here
If you're an engineer adding a new repo to Sling, work through these in order:
- Add a block to
config/repositories.json - Make sure the right GitHub App is installed on the repo (
svenlabs-ci-appfor SvenLabs-owned repos,gdt-ci-appfor GDT) - Set the corresponding env vars on the Sling server (App ID + private-key path + webhook secret)
- Restart Sling:
pm2 restart sling --update-env - Open a labelled test issue and confirm the next Night Shift cron picks it up
Public routes
What's served and where. All routes below are mounted on https://sling.svenlabs.ai.
Onboarding + presentation (EPIC #441)
| Route | Auth | Purpose |
|---|---|---|
GET /onboarding | public | 10-minute walkthrough — pinnable in Slack |
GET /onboarding/:role | public | producer | engineer | exec deep dives |
GET /onboarding/demo | public | Synthetic-ticket replay (canned trace, no live AI calls) |
GET /onboarding/deck | public | Slide-deck mode; ?print=1 + ?notes=1 |
GET /onboarding/share | public | Copy-pasteable Slack/email/DM templates |
GET /og/onboarding.svg | public | SVG OG card for the universal onboarding page |
GET /og/onboarding/:variant.svg | public | SVG variants: producer / engineer / exec |
GET /og/onboarding.png | public | PNG OG card (Slack-friendly, rasterized via @resvg/resvg-js) |
GET /og/onboarding/:variant.png | public | PNG variants |
Operator surfaces
| Route | Auth | Purpose |
|---|---|---|
GET / | public | Public homepage with live stats |
GET /docs | public | This page |
GET /guide | public | Role-based reference guide |
GET /workflow | public | Workflow PDF implementation map (history) |
GET /status-page | auth | Worker pool depth, recent failures |
GET /dashboard | auth + org | Live state of every Sling task |
GET /dashboard/personal | auth + org | Per-user dashboard |
GET /dashboard/team | auth + org | Team dashboard |
GET /create | auth + org | Brief intake form (creates issues from natural-language briefs) |
GET /ticket/:owner/:repo/:issue | auth + org | Per-ticket timeline (lifecycle + cost + phases) |
GET /e2e | auth | E2E artifact dashboard |
GET /admin/onboarding-stats | auth + org | Per-section onboarding telemetry funnel |
Webhook + ingest endpoints
| Route | Auth | Notes |
|---|---|---|
POST /webhook/github | HMAC sig | GitHub App + repo webhooks. Per-tenant routing via installation_id. |
POST /webhook/slack/events | Slack sig | Slack Events API for AIA bot |
POST /webhook/slack/interactions | Slack sig | Slack interactivity (buttons in UAT messages) |
POST /webhook/cloudwatch | — | AWS CloudWatch alarm webhook |
POST /webhook/stripe | Stripe sig | Billing events |
POST /api/intentos/ingest | HMAC bearer | IntentOS spec ingest → creates GitHub issue |
POST /api/autodev/ingest | HMAC bearer | AutoDev specification bundle → creates issue with auto-implement |
POST /api/onboarding/event | public, DNT-respect | Onboarding telemetry beacon (B8) |
Comment commands (PR + issue)
Sling reads slash-commands from issue comments and PR comments. The webhook handler dispatches to the matching governance / pipeline action.
| Command | Status | Effect |
|---|---|---|
/approve | shipped | Clear governance gate for tasks awaiting human approval |
/reject | shipped | Reject a pending task — it will not run |
/retry | shipped (#401) | Re-enter the pipeline using the latest issue body + comment thread; reclaims the existing task_issue_map |
/revise | shipped (alias of /retry) | Same effect as /retry |
/update <text> | shipped | Inject a free-text producer change request and re-enter the pipeline. Targeted alternative to /retry's full thread replay. |
/qa | shipped | Find the open PR linked to this issue, run the E2E harness, post results back as a comment. Triggers the same pipeline as the post-PR auto-run. |
/cancel | shipped (#426) | Stop in-flight worker processes for this issue (SIGTERM → SIGKILL after 3s for direct mode + tmux kill-session for tmux mode), reject the governance task, and clear the task_issue_map row so the issue can be re-tried later |
/skip <phase> | partial | Apply the matching skip-* label and re-enter the pipeline. /skip tests is honoured today (skip-tests label). /skip quality and /skip review are accepted and labelled but the runner does not yet honour them. |
config/repositories.json schema
One entry per repo Sling manages. Minimal example with the most common fields:
{
"id": "workos",
"github_repo": "svenlabsllc/workos",
"github_app": "svenlabs-ci-app",
"branch": "develop",
"productionBranch": "main",
"sourceBranch": "main",
"execution_mode": "claude-code",
"language": "typescript",
"test_command": "pnpm test",
"labels": ["auto-implement", "sling"],
"max_concurrent_tasks": 1,
"claude_model": "auto",
"hipaa_mode": false,
"governance": { ... },
"staging": { ... },
"uat": { ... }
}
| Field | Type | Description |
|---|---|---|
id | string | Unique identifier for this repo entry |
github_repo | string | Full owner/repo on GitHub |
github_app | string | svenlabs-ci-app | gdt-ci-app — overrides the tenant default (sling#308) |
branch | string | PR merge target. Default develop for features. |
productionBranch | string | PR target for hotfixes (labels hotfix, critical, showstopper, or bug+priority:high) |
sourceBranch | string | (#403) Branch to cut from. Lets feature branches be cut from main while still merging to develop, so PR diffs are clean per-issue. |
execution_mode | string | stiv | claude-code | claude-code-tmux | cloud |
language | string | nodejs | typescript | php | python | go | rust |
test_command | string | Shell command to run the test suite |
lint_command | string | Optional lint command |
labels | string[] | Trigger labels Sling listens for on issues |
max_concurrent_tasks | number | Per-repo concurrency cap |
claude_model | string | Model override (auto picks per phase) |
anthropic_credential_id | string | Optional per-repo Claude Max credential routing (see credential-home.js) |
hipaa_mode | boolean | Stricter blocked-paths + data-handling rules |
governance | object | Per-repo overrides for risk tiers and blocked paths |
review | object | AI auto-review thresholds + custom rules |
staging | object | Staging deploy + E2E config |
uat | object | UAT Slack channel, GitHub Projects mappings, push-to-prod thresholds |
projectStatus | object | GitHub Projects v2 mapping (project ID, status field, status mappings) |
Branching model
Three orthogonal fields control the branch lifecycle. The split (#403) lets us merge to develop while cutting from main, so PRs don't carry unrelated divergence as their base.
| Field | Role | Default |
|---|---|---|
branch | PR merge target for features/fixes | develop |
productionBranch | PR merge target for hotfixes | main |
sourceBranch | Branch the new feature/fix branch is cut from | same as branch |
Branch naming: {type}/auto-{issue}/{slug} where type ∈ feat | fix | hotfix | chore | refactor | docs.
Environment variables
| Variable | Required | Purpose |
|---|---|---|
OPENROUTER_API_KEY | preferred | AI routing for STIV mode |
ANTHROPIC_API_KEY | fallback | Used only when OpenRouter is absent |
GITHUB_TOKEN | yes (legacy fallback) | Global PAT — used for repos without a configured App |
GITHUB_APP_ID | yes (gdt-ci-app) | App ID for the default GitHub App |
GITHUB_APP_PRIVATE_KEY_PATH | yes | Path to gdt-ci-app's .pem |
GITHUB_WEBHOOK_SECRET | yes | HMAC secret for the default App |
SVENLABS_GITHUB_APP_ID | for SvenLabs repos | App ID for svenlabs-ci-app (e.g. workos) |
SVENLABS_GITHUB_APP_PRIVATE_KEY_PATH | for SvenLabs repos | Path to svenlabs-ci-app's .pem |
SVENLABS_GITHUB_WEBHOOK_SECRET | for SvenLabs repos | HMAC secret for svenlabs-ci-app |
AUTODEVMINI_WORKER_COUNT | no | Worker pool size (default 3) |
WORKER_MODEL | no | Default model for STIV workers |
OPENROUTER_TRIAGE_MODEL | no | Cheap model for intent classification |
AUTODEV_INGEST_SECRET | for /api/autodev/ingest | HMAC bearer |
INTENTOS_WEBHOOK_SECRET | for /api/intentos/ingest | HMAC bearer |
CLAUDE_CODE_MODEL | no | Override for Claude Code (default: CLI default) |
CLAUDE_CODE_MAX_TURNS | no | Max turns per Claude Code run (default 80) |
CLAUDE_CODE_TIMEOUT_MS | no | Run timeout (default 3 600 000) |
SCTXT_URL / SCTXT_API_KEY | no | SharedContext (sctxt.ai) integration |
SLING_TICKET_BUDGET_USD | no | Per-ticket cost cap (default $25) |
SLING_AI_ISSUE_CREATION | no | true to allow AI-originated issue creation (default blocked) |
PR_REVIEW_DEDUP_HOURS | no | Skip re-review when same head_sha was reviewed within N hours (default 24) |
PR_REVIEW_QUORUM | no | Run AI reviewer N times in parallel; keep findings flagged by ≥ ceil(N/2) (default 1) |
SLACK_BOT_TOKEN | yes | AIA bot posts via this token |
ONBOARDING_VIDEO_* | no | Slot-based screencast IDs (B6); empty slots render placeholders |
Execution modes
Mode resolution priority: task label → repo config (execution_mode) → task property (executionMode) → default stiv.
| Mode | Trigger | How it works | Best for |
|---|---|---|---|
stiv | default | Spec → Test → Implement → Validate, iterative | Well-scoped, TDD-friendly tickets |
claude-code | Label or repo config | Direct spawn of Claude Code CLI; flat-rate Max plan; prompt piped via stdin | Multi-file changes; recommended over STIV for most repos today |
claude-code-tmux | Label claude-code-tmux | Same as above + tmux session for live tmux attach -t claude-{issue} | Debugging complex tasks live |
cloud | repo execution_mode: cloud | Runs on Anthropic infra via claude --remote; auto-creates PR | High-parallelism blitzes |
STIV pipeline + checkpointing
Phases written to PostgreSQL task_steps. On retry, completed phases are skipped (sling#291 resilience).
- workspace — clones repo, checks out
sourceBranch, creates feature branch - spec — turns the issue into structured ACs
- tests — generates tests for each AC (skipped on
skip-tests) - implement — writes code; up to 3 attempts per file
- quality — lint + types + existing test suite
- review — AI auto-review, score 0-10
- push + pr — pushes branch, opens PR with rolling comment
Workspace + branch are reused across retries. Heavy subprocesses run with nice -n 15 ionice -c2 -n7; default cap is 2 concurrent tasks.
Project lifecycle state machine
src/utils/lifecycle-store.js is the source of truth for where a ticket sits in the producer / BO / release loop (workflow PDF steps 11 + 13).
spec_review
→ building
→ ready_for_uat
→ uat_in_review
→ back_in_engineering ⇆ building
→ push_to_production | push_to_production_immediately
→ in_production
→ reverted
Two tables back it: project_lifecycle (current state per task) and project_lifecycle_history (audit trail with actor + reason). Use transitionAsync() for fire-and-forget calls; illegal transitions warn but never throw.
UAT + push to production
The UAT Slack message offers BOs three branches:
| Button | Approval gate | CI gate | Use when |
|---|---|---|---|
| Request Changes | n/a — resets approvals | n/a | Marks Back in Engineering |
| Push to Production | Configured requiredApprovals threshold | Standard CI + branch protection | Normal release |
| Push to Production Now | Single approver only | CI + branch protection still apply | Urgent fixes |
The urgent path records urgent = true + triggered_by = <slack user> on the releases row and prefixes the Slack message with :rotating_light: URGENT. It does not bypass branch protection — those are enforced by GitHub at merge time.
E2E testing
The post-PR pipeline runs Playwright E2E and downloads videos / traces / screenshots on every build, including every /retry re-entry. Artifacts are stored at /data/sling-artifacts/{owner}/{repo}/pr-{N}/{timestamp}/ with 30-day retention.
QA video + trace are forced ON via --trace=on --video=on appended to the default Playwright command. Opt out per-repo:
"staging": {
"e2e": { "qaVideo": false, "qaTrace": false }
}
When the repo provides its own testCmd, Sling does not modify it — pass the flags yourself.
Auto-review noise controls (EPIC #413 / #419)
| Knob | Default | Effect |
|---|---|---|
PR_REVIEW_DEDUP_HOURS | 24 | Skip re-review when the same head_sha was reviewed within this many hours |
PR_REVIEW_QUORUM | 1 | Run reviewer N times in parallel; keep findings flagged by ≥ ceil(N/2) |
PR label force-rereview | — | One-shot bypass of the SHA-dedup gate; auto-removed after a run |
The auto-review comment is rolled in place: first run creates a comment, every subsequent run edits it with a <sub>_Last updated …_</sub> footer.
Governance
Risk tiers determine whether a task auto-runs, requires human approval, or is rejected outright. Per-repo overrides live under governance in repositories.json.
| Tier | Examples | Disposition |
|---|---|---|
| 0 | Docs, comments, config tweaks | Auto-approved |
| 1 | Non-critical bug fixes, tests, refactors | Auto-approved (PR-only) |
| 2 | API changes, DB migrations | Requires human /approve |
| 3 | Auth, payments, HIPAA paths | Rejected — implement by hand |
EPIC protection (#399)
Issues labelled epic are excluded from auto-split. Sling will not break an EPIC into child issues automatically.
AI issue creation (#397 + #400)
AI-originated issue creation is blocked by default; SLING_AI_ISSUE_CREATION=true opts in. When enabled, dedup-candidate + product-repo-map signals are surfaced on every proposal. See I1 / I5 / I6 under infra#384.
Night Shift cron
Runs every 5 min via node-cron. Fetches eligible issues across all configured repos via getOctokitForRepo (sling#468), routes them through governance, queues them to workers. Stale task_issue_map rows reclaim themselves when their PR is closed-without-merge (sling#291).
API endpoints (operators)
POST /api/autodev/ingest
Receives AutoDev specification bundles from IntentOS. Auth: Authorization: Bearer <HMAC> with AUTODEV_INGEST_SECRET. Target repo resolved from X-Target-Repo header, or AUTODEV_DEFAULT_OWNER/_REPO.
curl -X POST https://sling.svenlabs.ai/api/autodev/ingest \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <hmac>" \
-H "X-Target-Repo: svenlabsllc/workos" \
-d '{ "title": "...", "requirements": [...], "constraints": [...], "acceptance": [...] }'
POST /api/intentos/ingest
IntentOS webhook for spec ingestion. Same HMAC pattern, separate secret (INTENTOS_WEBHOOK_SECRET).
POST /api/onboarding/event
Public beacon for B8 telemetry. Always 204. Validates a small whitelist (page, event_type, section, browser family); silently drops anything else. DNT-respecting on the client side.
Runbooks
Operator playbooks live under docs/runbooks/ in the Sling repo:
- db-restore.md — restoring a corrupted PostgreSQL state (#418 R1)
- sling-down.md — what to do when Sling is offline (#418 R3)
- env-vars.md — exhaustive env variable reference
- docs/adr/ — architectural decision records
Source: src/handlers/docs.js. Update this file whenever a public route, env var, or repositories.json field is added.