diff --git a/.github/workflows/gitleaks.yml b/.github/workflows/gitleaks.yml new file mode 100644 index 0000000..b57eb97 --- /dev/null +++ b/.github/workflows/gitleaks.yml @@ -0,0 +1,19 @@ +name: gitleaks + +on: + push: + branches: [main] + pull_request: + +jobs: + gitleaks: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - uses: gitleaks/gitleaks-action@v2 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITLEAKS_LICENSE: ${{ secrets.GITLEAKS_LICENSE }} diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 0000000..d34f15a --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,21 @@ +name: Lint + +on: + pull_request: + branches: [main] + +concurrency: + group: lint-${{ github.ref }} + cancel-in-progress: true + +jobs: + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + node-version: "24" + + - run: make lint diff --git a/.node-version b/.node-version new file mode 100644 index 0000000..a45fd52 --- /dev/null +++ b/.node-version @@ -0,0 +1 @@ +24 diff --git a/AGENTS.md b/AGENTS.md index cebd973..a1094f3 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,33 +1,54 @@ -> **First-time setup**: Customize this file for your project. Prompt the user to customize this file for their project. -> For Mintlify product knowledge (components, configuration, writing standards), -> install the Mintlify skill: `npx skills add https://mintlify.com/docs` - # Documentation project instructions ## About this project -- This is a documentation site built on [Mintlify](https://mintlify.com) +- This is the UseZombie documentation site built on [Mintlify](https://mintlify.com) - Pages are MDX files with YAML frontmatter - Configuration lives in `docs.json` -- Run `mint dev` to preview locally +- Run `mint dev` to preview locally on port 3000 - Run `mint broken-links` to check links +- Deploys automatically on push to the default branch (Mintlify GitHub integration) ## Terminology -{/* Add product-specific terms and preferred usage */} -{/* Example: Use "workspace" not "project", "member" not "user" */} +- Use "spec" not "specification" or "ticket" +- Use "run" not "job" or "task" +- Use "workspace" not "project" +- Use "gate loop" not "CI pipeline" or "validation loop" +- Use "scorecard" not "report" or "metrics" +- Use "agent" not "bot" or "AI" +- Use "PR" not "pull request" (except on first mention per page) +- Use "zombiectl" in code formatting when referring to CLI commands +- Use "zombied" in code formatting when referring to server processes +- Use "Mission Control" for the web dashboard (app.usezombie.com) ## Style preferences -{/* Add any project-specific style rules below */} - - Use active voice and second person ("you") - Keep sentences concise — one idea per sentence - Use sentence case for headings - Bold for UI elements: Click **Settings** - Code formatting for file names, commands, paths, and code references +- Mermaid for all sequence and architecture diagrams +- Do not use time estimates or effort ratings in user-facing docs +- Mark future features with `` callout: "This feature is coming soon." ## Content boundaries -{/* Define what should and shouldn't be documented */} -{/* Example: Don't document internal admin features */} +- Do not document internal deployment playbooks (those live in the main repo) +- Do not expose credential values, vault paths, or 1Password references +- Do not document internal agent pipeline internals (NullClaw config details, executor RPC protocol) — keep operator docs at the operational level +- Do not reference specific cloud provider pricing or account details + +## Design system colors + +{/* SYNC SOURCE: ~/Projects/usezombie/ui/packages/design-system/src/tokens.css + When touching colors in this repo (docs.json, logos, custom CSS), always + verify values against the canonical design-system tokens first. + Run: grep -E "^ --z-(orange|bg|text|green|cyan|red|amber)" ~/Projects/usezombie/ui/packages/design-system/src/tokens.css */} + +Primary brand color: `#d96b2b` (`--z-orange`). Use this for emphasis and CTAs. +Primary bright/hover: `#e78a3c` (`--z-orange-bright`). +Background dark: `#05080d` (`--z-bg-0`). Surface: `#0f1520` (`--z-surface-0`). +Text primary: `#e8f2ff` (`--z-text-primary`). Text muted: `#8b97a8` (`--z-text-muted`). +Status colors: green `#39ff85` (`--z-green`, done), cyan `#5ed4ec` (`--z-cyan`, running), red `#ff4d6a` (`--z-red`, failed), amber `#c99232` (`--z-amber`, queued). diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..9bd7d31 --- /dev/null +++ b/Makefile @@ -0,0 +1,8 @@ +.PHONY: dev lint + +dev: + npx mintlify dev + +lint: + npx mintlify validate + npx mintlify broken-links diff --git a/ai-tools/claude-code.mdx b/ai-tools/claude-code.mdx deleted file mode 100644 index a59ca25..0000000 --- a/ai-tools/claude-code.mdx +++ /dev/null @@ -1,43 +0,0 @@ ---- -title: "Claude Code setup" -description: "Configure Claude Code for your documentation workflow" -icon: "asterisk" ---- - -Set up Claude Code to help you write and maintain your Mintlify documentation. - -## Prerequisites - -- Active Claude subscription (Pro, Max, or API access) - -## Setup - - - - ```bash - npm install -g @anthropic-ai/claude-code - ``` - - - - In your docs directory, run: - - ```bash - npx skills add https://mintlify.com/docs - ``` - - This gives Claude Code Mintlify's component reference, writing standards, - and workflow guidance. - - - - Edit `AGENTS.md` in your project root to add project-specific terminology, - style preferences, and content boundaries. - - - - ```bash - claude - ``` - - diff --git a/ai-tools/cursor.mdx b/ai-tools/cursor.mdx deleted file mode 100644 index 80bab56..0000000 --- a/ai-tools/cursor.mdx +++ /dev/null @@ -1,39 +0,0 @@ ---- -title: "Cursor setup" -description: "Configure Cursor for your documentation workflow" -icon: "arrow-pointer" ---- - -Set up Cursor to help you write and maintain your Mintlify documentation. - -## Prerequisites - -- Cursor editor installed - -## Setup - - - - Open the root of your documentation repository where `docs.json` is located. - - - - In the integrated terminal, run: - - ```bash - npx skills add https://mintlify.com/docs - ``` - - This gives Cursor Mintlify's component reference, writing standards, - and workflow guidance. - - - - Edit `AGENTS.md` in your project root to add project-specific terminology, - style preferences, and content boundaries. - - - - Open a file and use Cursor's AI features to draft and edit documentation. - - diff --git a/ai-tools/windsurf.mdx b/ai-tools/windsurf.mdx deleted file mode 100644 index 0776cca..0000000 --- a/ai-tools/windsurf.mdx +++ /dev/null @@ -1,39 +0,0 @@ ---- -title: "Windsurf setup" -description: "Configure Windsurf for your documentation workflow" -icon: "water" ---- - -Set up Windsurf's Cascade AI assistant to help you write and maintain your Mintlify documentation. - -## Prerequisites - -- Windsurf editor installed - -## Setup - - - - Open the root of your documentation repository where `docs.json` is located. - - - - In the integrated terminal, run: - - ```bash - npx skills add https://mintlify.com/docs - ``` - - This gives Windsurf Mintlify's component reference, writing standards, - and workflow guidance. - - - - Edit `AGENTS.md` in your project root to add project-specific terminology, - style preferences, and content boundaries. - - - - Open a file and use Cascade to draft and edit documentation. - - diff --git a/api-reference/endpoint/agent-stream.mdx b/api-reference/endpoint/agent-stream.mdx new file mode 100644 index 0000000..386d130 --- /dev/null +++ b/api-reference/endpoint/agent-stream.mdx @@ -0,0 +1,128 @@ +--- +title: Agent stream +description: Stateless agent relay for spec init and impact preview. +--- + +## `POST /v1/agent/stream` + +Stateless relay between the CLI and the workspace's LLM provider. `zombied` adds the system prompt and API key, forwards to the provider, and streams the response back as SSE. + +The CLI manages conversation history and resends the full message array with each request. `zombied` holds no state between requests. + +### Request body + +```json +{ + "mode": "spec_init", + "messages": [ + { + "role": "user", + "content": "Generate a spec template for: Add rate limiting per API key with Redis backend" + } + ], + "tools": [ + { + "name": "read_file", + "description": "Read a file from the user's repo", + "input_schema": { + "type": "object", + "properties": { "path": { "type": "string" } }, + "required": ["path"] + } + }, + { + "name": "list_dir", + "description": "List directory contents", + "input_schema": { + "type": "object", + "properties": { "path": { "type": "string" } }, + "required": ["path"] + } + }, + { + "name": "glob", + "description": "Find files matching a glob pattern", + "input_schema": { + "type": "object", + "properties": { "pattern": { "type": "string" } }, + "required": ["pattern"] + } + } + ] +} +``` + +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| `mode` | string | yes | System prompt selector. One of: `spec_init`, `preview` | +| `messages` | array | yes | Conversation history in Messages API format. CLI accumulates and resends with each request. | +| `tools` | array | yes | Tool definitions. CLI sends read-only tools (read_file, list_dir, glob). | + +### Response + +`200 text/event-stream` — SSE stream with the following event types: + +**`tool_use`** — The model wants to call a tool. CLI should execute locally and send the result back. + +``` +event: tool_use +data: {"id":"tu_01","name":"read_file","input":{"path":"go.mod"}} +``` + +**`text_delta`** — Streaming text from the model's response. + +``` +event: text_delta +data: {"text":"# M5_001: Rate Limiting\n\n**Prototype:** v1.0.0\n"} +``` + +**`done`** — Stream complete. Includes usage and cost. + +``` +event: done +data: {"usage":{"input_tokens":12450,"output_tokens":3200,"cost_usd":0.085,"provider":"anthropic","model":"claude-sonnet-4-6","round_trips":4}} +``` + +**`error`** — Provider error or timeout. + +``` +event: error +data: {"message":"provider timeout after 30s"} +``` + +### Tool call round trip + +When the CLI receives a `tool_use` event: + +1. Execute the tool locally (read file from laptop, list directory, etc.) +2. Append the assistant's `tool_use` message and the user's `tool_result` to the message history +3. POST the updated messages to the same endpoint + +The loop continues until the model returns text (no more tool calls) and a `done` event. + +### Modes + +| Mode | System prompt behavior | Typical tool calls | +|------|----------------------|-------------------| +| `spec_init` | Explore the repo, detect language/ecosystem, generate a milestone spec | 3-5 (list root, read manifest, read Makefile, list src/) | +| `preview` | Read the spec, explore the repo, predict which files will be touched | 4-8 (read spec, list directories, read key files, grep patterns) | + +### Provider resolution + +`zombied` resolves the LLM provider from workspace configuration. The CLI never specifies or sees the provider. Supported providers include Anthropic, OpenAI, Google, and user-supplied keys. + +### Security + +- Tool calls are executed by the CLI on the user's machine, not by `zombied` +- The CLI validates all paths against the repo root before reading (prevents path traversal) +- `zombied` has no filesystem awareness and never sees file contents directly +- Files only leave the laptop one at a time when the model explicitly requests them + +### Errors + +| Status | Code | Meaning | +|--------|------|---------| +| 400 | `INVALID_MODE` | Unknown mode value | +| 401 | `UNAUTHORIZED` | Missing or invalid auth token | +| 403 | `FORBIDDEN` | Insufficient role for workspace | +| 500 | — | SSE `event: error` emitted before stream closes | diff --git a/api-reference/endpoint/create-run.mdx b/api-reference/endpoint/create-run.mdx new file mode 100644 index 0000000..9b6cf61 --- /dev/null +++ b/api-reference/endpoint/create-run.mdx @@ -0,0 +1,43 @@ +--- +title: "Create run" +description: "Submit a spec and start a new run." +api: "POST /v1/runs" +--- + +## Request body + + + The spec content to execute. Must pass schema validation. + + + + The workspace to run the spec against. + + +## Response + + + Unique identifier for the created run. + + + + Initial run status. One of `QUEUED`, `RUNNING`, `COMPLETED`, `FAILED`. + + + + ISO 8601 timestamp of run creation. + + + +```json +{ + "run_id": "run_01J8XVKA3M", + "status": "QUEUED", + "created_at": "2026-03-29T14:22:00Z" +} +``` + + +## Deduplication + +If a run with identical spec content already exists in the workspace and is still in progress, the API returns a `DUPLICATE_RUN` error instead of creating a new run. Completed or failed runs do not block new submissions. diff --git a/api-reference/endpoint/create.mdx b/api-reference/endpoint/create.mdx deleted file mode 100644 index 5689f1b..0000000 --- a/api-reference/endpoint/create.mdx +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: 'Create Plant' -openapi: 'POST /plants' ---- diff --git a/api-reference/endpoint/delete.mdx b/api-reference/endpoint/delete.mdx deleted file mode 100644 index 657dfc8..0000000 --- a/api-reference/endpoint/delete.mdx +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: 'Delete Plant' -openapi: 'DELETE /plants/{id}' ---- diff --git a/api-reference/endpoint/get-run.mdx b/api-reference/endpoint/get-run.mdx new file mode 100644 index 0000000..e2755d7 --- /dev/null +++ b/api-reference/endpoint/get-run.mdx @@ -0,0 +1,59 @@ +--- +title: "Get run" +description: "Fetch run status and scorecard." +api: "GET /v1/runs/{run_id}" +--- + +## Path parameters + + + The unique identifier of the run. + + +## Response + + + Unique identifier for the run. + + + + Current run status. One of `QUEUED`, `RUNNING`, `COMPLETED`, `FAILED`. + + + + Scorecard with dimension-level pass/fail results. Present when the run has completed. + + + + Array of gate evaluation results. Each entry includes the gate name, pass/fail status, and output. + + + + Array of artifact URLs produced by the run (logs, diffs, screenshots). + + + +```json +{ + "run_id": "run_01J8XVKA3M", + "status": "COMPLETED", + "scorecard": { + "total": 12, + "passed": 11, + "failed": 1, + "dimensions": [ + { "id": "1.1", "name": "CLI parser handles required flags", "status": "PASSED" }, + { "id": "1.2", "name": "Run command returns deterministic state", "status": "FAILED" } + ] + }, + "gate_results": [ + { "gate": "lint", "status": "PASSED", "output": "" }, + { "gate": "test", "status": "PASSED", "output": "" } + ], + "artifacts": [ + "https://api.usezombie.com/v1/artifacts/run_01J8XVKA3M/log.txt", + "https://api.usezombie.com/v1/artifacts/run_01J8XVKA3M/diff.patch" + ] +} +``` + diff --git a/api-reference/endpoint/get.mdx b/api-reference/endpoint/get.mdx deleted file mode 100644 index 56aa09e..0000000 --- a/api-reference/endpoint/get.mdx +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: 'Get Plants' -openapi: 'GET /plants' ---- diff --git a/api-reference/endpoint/list-specs.mdx b/api-reference/endpoint/list-specs.mdx new file mode 100644 index 0000000..ebc7c7d --- /dev/null +++ b/api-reference/endpoint/list-specs.mdx @@ -0,0 +1,48 @@ +--- +title: "List specs" +description: "List specs in a workspace." +api: "GET /v1/specs" +--- + +## Query parameters + + + The workspace to list specs for. + + +## Response + +Returns an array of spec objects belonging to the workspace. + + + Array of spec objects. + + + +```json +{ + "specs": [ + { + "spec_id": "spec_01J8XW2A1K", + "name": "M4_001_CLI_RUNTIME", + "version": "v1.0.0", + "milestone": "M4", + "workstream": "001", + "status": "ACTIVE", + "created_at": "2026-03-20T09:00:00Z", + "updated_at": "2026-03-28T11:30:00Z" + }, + { + "spec_id": "spec_01J8XW2B3L", + "name": "M4_002_NPM_PUBLISH", + "version": "v1.0.0", + "milestone": "M4", + "workstream": "002", + "status": "ACTIVE", + "created_at": "2026-03-21T10:15:00Z", + "updated_at": "2026-03-27T14:00:00Z" + } + ] +} +``` + diff --git a/api-reference/endpoint/pause-workspace.mdx b/api-reference/endpoint/pause-workspace.mdx new file mode 100644 index 0000000..febb391 --- /dev/null +++ b/api-reference/endpoint/pause-workspace.mdx @@ -0,0 +1,34 @@ +--- +title: "Pause workspace" +description: "Pause a workspace to stop new runs." +api: "POST /v1/workspaces/{workspace_id}:pause" +--- + +## Path parameters + + + The unique identifier of the workspace to pause. + + +## Response + + + The workspace identifier. + + + + New workspace status. Always `PAUSED` on success. + + + +```json +{ + "workspace_id": "ws_01J8XV9R2M", + "status": "PAUSED" +} +``` + + + + Pausing a workspace prevents new runs from being submitted. In-flight runs are **not** interrupted and will continue to completion. + diff --git a/api-reference/endpoint/retry-run.mdx b/api-reference/endpoint/retry-run.mdx new file mode 100644 index 0000000..9782f6d --- /dev/null +++ b/api-reference/endpoint/retry-run.mdx @@ -0,0 +1,39 @@ +--- +title: "Retry run" +description: "Retry a failed run." +api: "POST /v1/runs/{run_id}:retry" +--- + +## Path parameters + + + The unique identifier of the run to retry. + + +## Response + + + Unique identifier for the new retry run. + + + + Initial status of the retried run. Always `QUEUED`. + + + + ISO 8601 timestamp of the retry creation. + + + +```json +{ + "run_id": "run_01J8XVKB7N", + "status": "QUEUED", + "created_at": "2026-03-29T15:10:00Z" +} +``` + + + + Retry is only available for runs in `FAILED` status. Attempting to retry a run in any other state returns an `INVALID_STATE_TRANSITION` error. + diff --git a/api-reference/endpoint/sync-workspace.mdx b/api-reference/endpoint/sync-workspace.mdx new file mode 100644 index 0000000..9a3ce6e --- /dev/null +++ b/api-reference/endpoint/sync-workspace.mdx @@ -0,0 +1,39 @@ +--- +title: "Sync workspace" +description: "Sync specs from the connected repository." +api: "POST /v1/workspaces/{workspace_id}:sync" +--- + +## Path parameters + + + The unique identifier of the workspace to sync. + + +## Response + + + The workspace identifier. + + + + Number of specs synced from the repository. + + + + ISO 8601 timestamp of the sync completion. + + + +```json +{ + "workspace_id": "ws_01J8XV9R2M", + "synced_specs": 7, + "synced_at": "2026-03-29T14:45:00Z" +} +``` + + + + Sync pulls the latest spec files from the connected GitHub repository. Only files matching the spec format (`docs/spec/v1/M*_*.md`) are imported. + diff --git a/api-reference/endpoint/webhook.mdx b/api-reference/endpoint/webhook.mdx deleted file mode 100644 index 3291340..0000000 --- a/api-reference/endpoint/webhook.mdx +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: 'New Plant' -openapi: 'WEBHOOK /plant/webhook' ---- diff --git a/api-reference/introduction.mdx b/api-reference/introduction.mdx index c835b78..6b8c767 100644 --- a/api-reference/introduction.mdx +++ b/api-reference/introduction.mdx @@ -1,33 +1,70 @@ --- -title: 'Introduction' -description: 'Example section for showcasing API endpoints' +title: API overview +description: REST API for programmatic spec submission and run management. --- - - If you're not looking to build API reference documentation, you can delete - this section by removing the api-reference folder. - + + This reference covers the public spec submission and run management API. Internal endpoints (authentication sessions, billing, harness management, agent profiles) are not documented here. + -## Welcome +## Base URL -There are two ways to build API documentation: [OpenAPI](https://mintlify.com/docs/api-playground/openapi/setup) and [MDX components](https://mintlify.com/docs/api-playground/mdx/configuration). For the starter kit, we are using the following OpenAPI specification. - - - View the OpenAPI specification file - +| Environment | URL | +|-------------|-----| +| Production | `https://api.usezombie.com/v1` | +| Development | `https://api-dev.usezombie.com/v1` | ## Authentication -All API endpoints are authenticated using Bearer tokens and picked up from the specification file. +All API requests require a Bearer token in the `Authorization` header: + +```bash +curl -H "Authorization: Bearer " \ + https://api.usezombie.com/v1/runs +``` + +Get a token via `zombiectl login` or the Clerk API. + +## Content type + +All requests and responses use `application/json`. + +## Error envelope + +Errors return a structured JSON envelope: ```json -"security": [ - { - "bearerAuth": [] +{ + "error": { + "code": "SPEC_VALIDATION_FAILED", + "message": "File reference 'src/missing.ts' does not exist in repository." } -] +} ``` + +### Error codes + +| Code | Meaning | +|------|---------| +| `WORKSPACE_PAUSED` | Workspace is paused, no new runs accepted | +| `INVALID_STATE_TRANSITION` | Run is not in a valid state for this operation | +| `SPEC_VALIDATION_FAILED` | Spec failed structural or file reference validation | +| `DUPLICATE_RUN` | Active run with same spec hash + repo + base commit exists | +| `CREDIT_EXHAUSTED` | Free plan credit depleted | +| `BUDGET_EXCEEDED` | Workspace monthly budget exceeded | +| `UNAUTHORIZED` | Missing or invalid auth token | +| `FORBIDDEN` | Insufficient role for this operation | + +## Agent route isolation + +The `/agents` path is isolated from the human pipeline with separate authentication and rate limits. External agents (Claude Code, Codex, OpenCode) submit specs via this route for agent-to-agent workflows. + +## Agent relay endpoint + +`POST /v1/agent/stream` is a stateless relay for lightweight agent interactions. The CLI sends tool definitions and messages, `zombied` adds the workspace's LLM API key and system prompt, forwards to the configured provider, and streams the response back as SSE. + +This endpoint powers `spec init` and `run --preview`. See the [agent relay model](/how-it-works#agent-relay-model) for the full architecture and the [endpoint reference](/api-reference/endpoint/agent-stream) for request/response details. + +## Rate limits + +Rate limits are applied per workspace. Specific limits vary by plan tier. Rate-limited responses return HTTP 429 with a `Retry-After` header. diff --git a/api-reference/openapi.json b/api-reference/openapi.json deleted file mode 100644 index da5326e..0000000 --- a/api-reference/openapi.json +++ /dev/null @@ -1,217 +0,0 @@ -{ - "openapi": "3.1.0", - "info": { - "title": "OpenAPI Plant Store", - "description": "A sample API that uses a plant store as an example to demonstrate features in the OpenAPI specification", - "license": { - "name": "MIT" - }, - "version": "1.0.0" - }, - "servers": [ - { - "url": "http://sandbox.mintlify.com" - } - ], - "security": [ - { - "bearerAuth": [] - } - ], - "paths": { - "/plants": { - "get": { - "description": "Returns all plants from the system that the user has access to", - "parameters": [ - { - "name": "limit", - "in": "query", - "description": "The maximum number of results to return", - "schema": { - "type": "integer", - "format": "int32" - } - } - ], - "responses": { - "200": { - "description": "Plant response", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Plant" - } - } - } - } - }, - "400": { - "description": "Unexpected error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Error" - } - } - } - } - } - }, - "post": { - "description": "Creates a new plant in the store", - "requestBody": { - "description": "Plant to add to the store", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/NewPlant" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "plant response", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Plant" - } - } - } - }, - "400": { - "description": "unexpected error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Error" - } - } - } - } - } - } - }, - "/plants/{id}": { - "delete": { - "description": "Deletes a single plant based on the ID supplied", - "parameters": [ - { - "name": "id", - "in": "path", - "description": "ID of plant to delete", - "required": true, - "schema": { - "type": "integer", - "format": "int64" - } - } - ], - "responses": { - "204": { - "description": "Plant deleted", - "content": {} - }, - "400": { - "description": "unexpected error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Error" - } - } - } - } - } - } - } - }, - "webhooks": { - "/plant/webhook": { - "post": { - "description": "Information about a new plant added to the store", - "requestBody": { - "description": "Plant added to the store", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/NewPlant" - } - } - } - }, - "responses": { - "200": { - "description": "Return a 200 status to indicate that the data was received successfully" - } - } - } - } - }, - "components": { - "schemas": { - "Plant": { - "required": [ - "name" - ], - "type": "object", - "properties": { - "name": { - "description": "The name of the plant", - "type": "string" - }, - "tag": { - "description": "Tag to specify the type", - "type": "string" - } - } - }, - "NewPlant": { - "allOf": [ - { - "$ref": "#/components/schemas/Plant" - }, - { - "required": [ - "id" - ], - "type": "object", - "properties": { - "id": { - "description": "Identification number of the plant", - "type": "integer", - "format": "int64" - } - } - } - ] - }, - "Error": { - "required": [ - "error", - "message" - ], - "type": "object", - "properties": { - "error": { - "type": "integer", - "format": "int32" - }, - "message": { - "type": "string" - } - } - } - }, - "securitySchemes": { - "bearerAuth": { - "type": "http", - "scheme": "bearer" - } - } - } -} \ No newline at end of file diff --git a/billing/budgets.mdx b/billing/budgets.mdx new file mode 100644 index 0000000..292da45 --- /dev/null +++ b/billing/budgets.mdx @@ -0,0 +1,44 @@ +--- +title: "Budgets and cost control" +description: "Per-run and workspace-level cost controls." +--- + +UseZombie enforces budgets at multiple levels to prevent runaway costs. + +## Per-run limits + +Every run has built-in limits that cap resource consumption: + +| Control | Default | Configurable | +|---------|---------|-------------| +| Token budget | 100K tokens | Yes | +| Wall time limit | 300 seconds | Yes | +| Repair loop cap | 3 iterations | Yes | + +These limits are checked at submission time. If a run would exceed the workspace budget, it is **rejected before execution starts** -- no credit is consumed. + +## Workspace monthly budget + +Set a monthly spending cap on any workspace from the [dashboard](https://app.usezombie.com/billing). + +The `billing budget set` CLI command is planned for a future release. + +When the workspace reaches its monthly budget, new runs are rejected until the next billing cycle or the budget is increased. + +## Run cancellation + +Cancel a running job at any time via CLI or API: + +```bash +zombiectl run cancel +``` + +The `run cancel` command is planned for a future release. Currently, runs can be cancelled from the [dashboard](https://app.usezombie.com). + +Cancelled runs are billed only for resources consumed up to the point of cancellation. + +## Credit deduction timing + +On the Hobby (Free) plan, credit is deducted only **after a run completes successfully**. Failed runs and cancelled runs do not reduce your credit balance. + +On the Scale plan, usage-based billing follows the same principle: you pay for completed work, not failed attempts. diff --git a/billing/plans.mdx b/billing/plans.mdx new file mode 100644 index 0000000..1466544 --- /dev/null +++ b/billing/plans.mdx @@ -0,0 +1,46 @@ +--- +title: "Plans" +description: "Free and Scale plan details." +--- + +UseZombie offers two plans. Both include the full spec-driven agent pipeline -- the difference is scale and support. + +## Hobby (Free) + +Get started with no commitment. + +| Feature | Details | +|---------|---------| +| Included credit | $10 | +| Specs | Unlimited | +| Self-repair gate loop | Included | +| Scored PRs | Included | +| Workspaces | 1 | +| Support | Community | + +Credit is deducted only for **completed runs**. Failed runs are free -- you are never charged for work that did not produce a result. + + + Coming soon: runs scoring below Bronze (score < 40) will be automatically marked non-billable. You will never pay for low-quality output. + + +## Scale (Pay as you go) + +Everything in Hobby, plus: + +| Feature | Details | +|---------|---------| +| Workspaces | Unlimited | +| Priority execution queue | Included | +| Cost control dashboards | Included | +| Support | Email | + +Scale billing is usage-based. You are charged for compute and token consumption on completed runs. The same rule applies: failed runs are free. + +## Choosing a plan + +Start with Hobby. When you need more than one workspace, priority queue access, or email support, upgrade to Scale from the dashboard or CLI: + +```bash +zombiectl workspace upgrade-scale +``` diff --git a/cli/configuration.mdx b/cli/configuration.mdx new file mode 100644 index 0000000..df3ed86 --- /dev/null +++ b/cli/configuration.mdx @@ -0,0 +1,37 @@ +--- +title: "Configuration" +description: "Configuration precedence and environment variables." +--- + +## Auth precedence + +Authentication is resolved in this order, highest priority first: + +1. **CLI flag** -- `--token ` on any command +2. **Environment variable** -- `ZOMBIE_TOKEN` +3. **Stored credentials** -- written by `zombiectl login` to `~/.config/zombiectl/credentials` + +A value at a higher level always wins. Use `ZOMBIE_TOKEN` in CI so no interactive login is needed. Use `--token` for one-off overrides without touching stored credentials. + +## API URL precedence + +1. **CLI flag** -- `--api https://...` +2. **Environment variable** -- `ZOMBIE_API_URL` +3. **`.env.local`** -- dev fallback file in the current directory +4. **Built-in default** -- `https://api.usezombie.com` + +## Environment variables + +| Variable | Purpose | Default | +|----------|---------|---------| +| `ZOMBIE_API_URL` | API base URL | `https://api.usezombie.com` | +| `ZOMBIE_TOKEN` | Auth token (for CI and scripts) | *(from `zombiectl login`)* | + +## CI usage + +In CI environments, set `ZOMBIE_TOKEN` from your secrets manager and use `--no-input` for non-interactive operation: + +```bash +export ZOMBIE_TOKEN="$ZOMBIE_SECRET" +zombiectl runs list --no-input --json +``` diff --git a/cli/flags.mdx b/cli/flags.mdx new file mode 100644 index 0000000..b4b1412 --- /dev/null +++ b/cli/flags.mdx @@ -0,0 +1,35 @@ +--- +title: "Global flags" +description: "Flags available on all zombiectl commands." +--- + +## Global flags + +These flags can be passed to any `zombiectl` command. + +| Flag | Description | Default | +|------|-------------|---------| +| `--api ` | API base URL | `https://api.usezombie.com` | +| `--json` | Machine-readable JSON output | `false` | +| `--no-open` | Suppress browser auto-open (login) | `false` | +| `--no-input` | Non-interactive mode | `false` | +| `--help` | Show help text | | +| `--version` | Show CLI version | | + +## Exit codes + +| Code | Meaning | +|------|---------| +| `0` | Success | +| `1` | General error (validation failure, API error) | + +When `--json` is enabled, error responses include structured error codes for programmatic consumption: + +```json +{ + "error": { + "code": "SPEC_VALIDATION_FAILED", + "message": "Spec file is missing required frontmatter fields." + } +} +``` diff --git a/cli/install.mdx b/cli/install.mdx new file mode 100644 index 0000000..766099d --- /dev/null +++ b/cli/install.mdx @@ -0,0 +1,58 @@ +--- +title: "Install" +description: "Install the zombiectl CLI." +--- + +## Install + + + + If you don't have Node.js installed yet, [download it here](https://nodejs.org). + + + + Run the following command in your terminal: + + ```bash + npm install -g @usezombie/zombiectl + ``` + + Use `npx` to run without a global install: + + ```bash + npx @usezombie/zombiectl --help + ``` + + + +## Agent Skills + +If you are running an AI coding agent (Claude Code, Cursor, GitHub Copilot, etc.), register `zombiectl` as a skill so the agent can invoke it directly during a session: + +```bash +npx skills add @usezombie/zombiectl +``` + +Once registered, the agent can call `zombiectl run`, `zombiectl workspace`, and other commands natively without manual invocation. + +## Verify installation + +```bash +zombiectl --version +``` + +## Check connectivity + +Run the built-in diagnostics command to verify API connectivity, auth status, and local configuration: + +```bash +zombiectl doctor +``` + +`doctor` checks: +- CLI version +- API reachability (`https://api.usezombie.com`) +- Auth token validity +- Detected AI agent integrations (Claude Code, Cursor, Copilot, etc.) + +It does not require authentication to run. diff --git a/cli/zombiectl.mdx b/cli/zombiectl.mdx new file mode 100644 index 0000000..92e1542 --- /dev/null +++ b/cli/zombiectl.mdx @@ -0,0 +1,260 @@ +--- +title: "Command reference" +description: "Complete zombiectl command reference." +--- + +## Overview + +| Group | Commands | +|-------|----------| +| Authentication | `login`, `logout` | +| Workspaces | `workspace add`, `workspace list`, `workspace remove`, `workspace upgrade-scale` | +| Specs | `specs sync`, `spec init`, `spec validate` | +| Runs | `run`, `run status`, `runs list`, `runs replay` | +| Diagnostics | `doctor` | +| Operator | `harness`, `skill-secret`, `agent`, `admin` | + +--- + +## Authentication + +### `zombiectl login` + +Authenticate via Clerk OIDC device flow. Opens your default browser to complete sign-in. + +```bash +zombiectl login +``` + +Use `--no-open` to print the auth URL instead of opening a browser automatically. + +### `zombiectl logout` + +Clear the local session token. + +```bash +zombiectl logout +``` + +--- + +## Workspaces + +### `zombiectl workspace add ` + +Connect a GitHub repository as a workspace. + +```bash +zombiectl workspace add https://github.com/org/repo +``` + +### `zombiectl workspace list` + +List all configured workspaces. + +```bash +zombiectl workspace list +``` + +### `zombiectl workspace remove ` + +Remove a workspace binding. + +```bash +zombiectl workspace remove ws_abc123 +``` + +### `zombiectl workspace upgrade-scale` + +Upgrade a workspace from Hobby to Scale plan. + +```bash +zombiectl workspace upgrade-scale [--workspace-id ] +``` + +--- + +## Specs + +### `zombiectl specs sync` + +Sync `PENDING_*.md` spec files from a connected repository. + +```bash +zombiectl specs sync +``` + +### `zombiectl spec init` + +Generate a spec from a natural language description. An agent explores your repo via the [agent relay](/how-it-works#agent-relay-model), reads manifest files and project structure, and drafts a complete milestone spec. + +```bash +zombiectl spec init --describe "Add rate limiting per API key with Redis backend" +``` + +**What happens:** +1. CLI sends your description + tool definitions to `zombied` +2. Agent explores your repo via tool calls (read_file, list_dir, glob) — files are read locally, never uploaded +3. Agent drafts a spec with the right language, gate commands, and acceptance criteria +4. CLI writes the spec to `docs/spec/v1/pending/` + +``` +🧟 analyzing your repo... +→ listed ./ (root structure) +→ read go.mod (Go 1.21, github.com/acme/api) +→ read Makefile (lint, test, build) +→ listed src/ (4 dirs, 23 files) + +🧟 drafting spec... + +✓ wrote docs/spec/v1/pending/M5_001_RATE_LIMITING.md + 4.8s | 4 reads | 15.6K tokens | $0.09 +``` + +Requires authentication. The agent uses your workspace's configured LLM provider. + +### `zombiectl spec validate ` + +Validate a spec file locally before submission. + +```bash +zombiectl spec validate specs/PENDING_checkout_flow.md +``` + +--- + +## Runs + +### `zombiectl run --spec ` + +Submit a spec and start a run. + +```bash +zombiectl run --spec specs/PENDING_checkout_flow.md +``` + +### `zombiectl run --spec --preview` + +Preview the blast radius before executing. An agent reads your spec and explores the repo via the [agent relay](/how-it-works#agent-relay-model) to predict which files will be touched. + +```bash +zombiectl run --spec specs/M5_001_RATE_LIMITING.md --preview +``` + +``` +🧟 analyzing your repo against spec... +→ read M5_001_RATE_LIMITING.md +→ listed src/http/ (middleware, handlers) +→ read src/http/middleware.go +→ listed src/redis/ (client) + +● src/http/middleware.go high +● src/redis/client.go high +◆ src/config/config.go medium +◆ docker-compose.yml medium +○ src/http/handler.go low + +5 file(s) in blast radius + 5.2s | 5 reads | 18.3K tokens | $0.11 +``` + +Use `--preview-only` to preview without starting a run. + +### `zombiectl run --spec --watch` + +Submit a spec and stream gate progress via SSE. + +```bash +zombiectl run --spec specs/PENDING_checkout_flow.md --watch +``` + +The `--watch` flag is planned for a future release. + +### `zombiectl runs list` + +List runs with status and scores. + +```bash +zombiectl runs list +``` + +### `zombiectl run status ` + +Fetch run detail with scorecard. + +```bash +zombiectl run status run_abc123 +``` + +### `zombiectl runs replay ` + +Structured failure narrative for a completed run. + +```bash +zombiectl runs replay run_abc123 +``` + +This command is planned for a future release. + +--- + +## Diagnostics + +### `zombiectl doctor` + +Verify API connectivity, authentication, and local configuration. Does not require authentication. + +```bash +zombiectl doctor +``` + +--- + +## Operator commands + +### `zombiectl harness` + +Agent harness lifecycle management. + +```bash +zombiectl harness source put [--workspace-id ] +zombiectl harness compile [--workspace-id ] +zombiectl harness activate [--workspace-id ] +zombiectl harness active [--workspace-id ] +``` + +All subcommands accept `--workspace-id` or use the current workspace from context. + +### `zombiectl skill-secret` + +Manage agent skill secrets per workspace. + +```bash +zombiectl skill-secret put --workspace-id --skill-ref --key --value [--scope sandbox] +zombiectl skill-secret delete --workspace-id --skill-ref --key +``` + +Required flags: `--workspace-id`, `--skill-ref`, `--key`. The `put` action also requires `--value`. The `--scope` flag defaults to `sandbox`. + +### `zombiectl agent` + +Agent introspection — view profiles, scores, proposals, and improvement reports. + +```bash +zombiectl agent profile +zombiectl agent scores +zombiectl agent proposals +zombiectl agent improvement-report +``` + + + Coming soon: `zombiectl agent dashboard` will show all agents in a workspace with ASCII sparklines, current tier, trust status, and pending proposals in a single command. + + +### `zombiectl admin` + +Administrative workspace operations. + +```bash +zombiectl admin +``` diff --git a/concepts.mdx b/concepts.mdx new file mode 100644 index 0000000..4bbf653 --- /dev/null +++ b/concepts.mdx @@ -0,0 +1,71 @@ +--- +title: "Key concepts" +description: "Core terminology and mental model for UseZombie." +--- + +## Core terminology + +These are the building blocks of UseZombie. Understanding them will help you navigate the docs, configure workspaces, and interpret run results. + + + + A markdown file describing what you want built. Specs are format-agnostic — structured sections, free-form prose, or bullet lists all work. The agent reads natural language and infers implementation intent from your codebase. A spec is the input to every run. + + + + A single execution of a spec. Each run has a lifecycle state: `QUEUED`, `RUNNING`, `COMPLETED`, or `FAILED`. A completed run produces artifacts (the implementation diff, gate logs) and a scorecard. Runs are immutable once finished — to retry, create a new run. + + + + The sequential validation cycle that runs after the agent finishes implementing: `make lint`, then `make test`, then `make build`. If any gate fails, the agent reads the error output, self-repairs, and re-runs the full gate sequence. The loop runs up to 3 times by default. This is what makes UseZombie runs self-repairing. + + + + Quality metrics attached to every completed run. Four weighted dimensions: + + - **Completion** (40%) — Did the agent implement everything the spec asked for? + - **Error rate** (30%) — How many gate failures occurred before passing? + - **Latency** (20%) — Wall-clock time from enqueue to PR. + - **Resource efficiency** (10%) — Token and compute usage relative to task complexity. + + Scores map to tiers: **Bronze** (0--39), **Silver** (40--69), **Gold** (70--89), **Elite** (90--100). + + + + A bound GitHub repository with associated entitlements, billing configuration, and agent settings. When you run `zombiectl workspace add`, you create a workspace. All runs, harnesses, and billing are scoped to a workspace. + + + + Workspace-scoped agent configuration. A harness defines how agents operate within a workspace: which source repositories to read, which compiler/runtime versions to use, and which agent profiles are available. Operators store source references, compile version constraints, and activate profiles through the harness. + + + + A named configuration within a harness that defines the model, repair strategy, and gate settings for runs. Each profile accumulates its own score history across runs, making it easy to compare configurations. Switch profiles to experiment with different agent behaviors without changing workspace settings. + + + + An isolated git working directory created for each run. The agent implements the spec in a worktree branched from the workspace's default branch, ensuring no changes touch main until you approve the PR. Worktrees are cleaned up automatically after run completion. + + + + The embedded agent runtime that executes specs inside the sandbox. NullClaw receives the spec, injected codebase context, and tool access, then produces an implementation. It operates within the worktree and has no access outside the sandbox boundary. + + + + The sidecar process (`zombied-executor`) that owns the sandbox lifecycle. The executor spawns NullClaw agents, manages worktree creation and cleanup, enforces resource limits (CPU, memory, time), and reports execution results back to the worker. + + + + The process of providing relevant codebase context — file contents, module structure, dependency graphs — to agents alongside the spec. Good context injection is what separates accurate implementations from hallucinated ones. This is a key area of ongoing improvement in UseZombie. + + + + A stateless communication model where `zombied` acts as a pass-through between `zombiectl` and the workspace's LLM provider. The CLI sends tool definitions (read_file, list_dir, glob) with each request. The model decides which files to read via tool calls. The CLI executes tools locally on the developer's laptop and sends results back. Files never leave the machine unless the model explicitly asks for them. + + Used by `spec init` and `run --preview` for lightweight, interactive agent sessions. The pipeline model (sandbox + worker) is used for full `run` execution. + + + + An agent-to-agent workflow where an external coding agent (Claude Code, Codex, OpenCode) submits specs to UseZombie. The external agent handles planning and spec authoring; UseZombie handles execution with verified gates. This lets you compose agent capabilities: one agent thinks, another builds with guardrails. + + diff --git a/contributing/architecture.mdx b/contributing/architecture.mdx new file mode 100644 index 0000000..01236d9 --- /dev/null +++ b/contributing/architecture.mdx @@ -0,0 +1,55 @@ +--- +title: "Source tree" +description: "Codebase structure and module organization." +--- + +## Module map + +```mermaid +graph TD + subgraph "zombied (Zig)" + Main[src/main.zig] --> CMD[src/cmd/] + CMD --> Serve[serve.zig] + CMD --> WorkerCmd[worker.zig] + CMD --> Doctor[doctor.zig] + CMD --> Run[run.zig] + CMD --> Migrate[migrate.zig] + CMD --> Reconcile[reconcile.zig] + Main --> HTTP[src/http/] + Main --> Pipeline[src/pipeline/] + Main --> Executor[src/executor/] + Main --> DB[src/db/] + Main --> Queue[src/queue/] + Main --> Auth[src/auth/] + Main --> Config[src/config/] + Main --> Observability[src/observability/] + end + subgraph "zombiectl (TypeScript)" + CLI[zombiectl/src/cli.js] --> Commands[src/commands/] + CLI --> Program[src/program/] + end + subgraph "UI (React/TypeScript)" + Website[ui/packages/website/] + App[ui/packages/app/] + DesignSystem[ui/packages/design-system/] + end +``` + +## Top-level directories + +| Directory | Language | Description | +|-----------|----------|-------------| +| `src/` | Zig | zombied server and worker. The core of the platform. | +| `src/cmd/` | Zig | CLI subcommands for zombied (`serve`, `worker`, `doctor`, `run`, `migrate`, `reconcile`). | +| `src/http/` | Zig | HTTP route handlers for the REST API. | +| `src/pipeline/` | Zig | Spec parsing, gate evaluation, and scorecard generation. | +| `src/executor/` | Zig | Run execution engine. Manages sandbox lifecycle and agent orchestration. | +| `src/db/` | Zig | Database layer. Migrations, queries, and connection pooling (Postgres). | +| `src/queue/` | Zig | Job queue backed by Redis. Handles run scheduling and worker dispatch. | +| `src/auth/` | Zig | Authentication and authorization. Clerk token validation, workspace-scoped access. | +| `src/config/` | Zig | Configuration loading from environment, files, and defaults. | +| `src/observability/` | Zig | Structured logging, metrics, and tracing. | +| `zombiectl/` | TypeScript | CLI tool for developers. Wraps the REST API with ergonomic commands. | +| `ui/packages/website/` | React/TS | Marketing website and documentation. | +| `ui/packages/app/` | React/TS | Dashboard application for workspace and run management. | +| `ui/packages/design-system/` | React/TS | Shared component library across UI packages. | diff --git a/contributing/development.mdx b/contributing/development.mdx new file mode 100644 index 0000000..a38a389 --- /dev/null +++ b/contributing/development.mdx @@ -0,0 +1,71 @@ +--- +title: "Development setup" +description: "Set up your local development environment." +--- + +## Prerequisites + +| Tool | Version | Purpose | +|------|---------|---------| +| Zig | Latest stable | zombied server and worker | +| Node.js | 18+ | zombiectl CLI | +| Docker | Latest | Postgres and Redis for local dev | +| Make | Any | Task runner for all components | + +## Start services + +Bring up Postgres and Redis in Docker: + +```bash +make up +``` + +## Run zombied in dev mode + +```bash +make dev +``` + +This starts the server with hot reload, connected to the local Postgres and Redis instances. + +## Lint + +```bash +make lint +``` + +Runs all linters and type checks across the full codebase (Zig, TypeScript, and UI). + +## Test + +```bash +make test +``` + +Runs the full unit test suite. Requires no external services. + +## Build + +```bash +make build +``` + +Compiles production binaries for zombied and runs the TypeScript build for zombiectl. + +## zombiectl development + +The CLI lives in its own directory with a separate dependency tree: + +```bash +cd zombiectl +npm install +npm run dev +``` + +## Stop services + +```bash +make down +``` + +Stops and removes the Docker containers started by `make up`. diff --git a/contributing/overview.mdx b/contributing/overview.mdx new file mode 100644 index 0000000..a1a2856 --- /dev/null +++ b/contributing/overview.mdx @@ -0,0 +1,41 @@ +--- +title: "Contributing" +description: "How to contribute to UseZombie." +--- + +UseZombie is open source under the [MIT License](https://github.com/usezombie/usezombie/blob/main/LICENSE). + +## What we welcome + +- Bug reports with reproduction steps. +- Feature requests with a clear use case. +- Documentation improvements. +- Performance optimizations with benchmarks. +- New gate implementations for the pipeline. +- CLI improvements and new commands. + +## Pull request process + +1. Fork the repository. +2. Create a feature branch from `main`. +3. Make your changes. Follow the existing code style. +4. Run `make lint` and `make test` -- both must pass. +5. Open a PR against `main` with a clear description of what and why. + +## Code of conduct + +We follow the [Contributor Covenant Code of Conduct](https://www.contributor-covenant.org/version/2/1/code_of_conduct/). Be respectful, constructive, and inclusive. + +## Commit style + +Use [Conventional Commits](https://www.conventionalcommits.org/): + +``` +feat(pipeline): add retry logic for transient gate failures +fix(cli): correct workspace list output alignment +docs(api): update error code reference +``` + +## Questions + +Open a GitHub Discussion or reach out on Discord for questions that are not bug reports or feature requests. diff --git a/contributing/testing.mdx b/contributing/testing.mdx new file mode 100644 index 0000000..9afc5be --- /dev/null +++ b/contributing/testing.mdx @@ -0,0 +1,66 @@ +--- +title: "Testing" +description: "Test strategy and commands." +--- + +## Unit tests + +```bash +make test +``` + +Runs all unit tests. No external services required. These tests cover pure logic: parsers, state machines, serialization, and utility functions. + +## Integration tests + +```bash +make test-integration +``` + +Requires Postgres and Redis running locally (start them with `make up`). Integration tests exercise the full request path: HTTP handler to database to queue and back. + +## Memory leak tests + +On Linux, run the Valgrind-based memory leak detector: + +```bash +make test-memleak +``` + +This compiles a debug build and runs the test suite under Valgrind. Any leaked allocation fails the test. + +## Cross-compilation check + +Verify that the build succeeds for all target platforms: + +```bash +make build-cross +``` + +This runs the Zig cross-compilation matrix (Linux x86_64, Linux aarch64, macOS x86_64, macOS aarch64). + +## Code conventions + +### Module size + +Every module must stay under **500 lines**. If a module grows past this limit, split it. Smaller modules are easier to test and review. + +### Zig database conventions + +When writing or reviewing Zig code that interacts with the database: + +- **Always call `.drain()` before `.deinit()`** on `conn.query()` results. This prevents connection pool corruption. +- **Prefer `conn.exec()`** when no result rows are needed. It handles cleanup automatically. + +```zig +// Good: drain before deinit +var result = try conn.query("SELECT id FROM runs WHERE status = $1", .{"QUEUED"}); +defer result.deinit(); +// ... process rows ... +result.drain(); + +// Better when no rows needed: use exec +try conn.exec("UPDATE runs SET status = $1 WHERE id = $2", .{ "RUNNING", run_id }); +``` + +Run `make check-pg-drain` to verify all query call sites follow this convention. diff --git a/development.mdx b/development.mdx deleted file mode 100644 index ac633ba..0000000 --- a/development.mdx +++ /dev/null @@ -1,94 +0,0 @@ ---- -title: 'Development' -description: 'Preview changes locally to update your docs' ---- - - - **Prerequisites**: - - Node.js version 19 or higher - - A docs repository with a `docs.json` file - - -Follow these steps to install and run Mintlify on your operating system. - - - - -```bash -npm i -g mint -``` - - - - -Navigate to your docs directory where your `docs.json` file is located, and run the following command: - -```bash -mint dev -``` - -A local preview of your documentation will be available at `http://localhost:3000`. - - - - -## Custom ports - -By default, Mintlify uses port 3000. You can customize the port Mintlify runs on by using the `--port` flag. For example, to run Mintlify on port 3333, use this command: - -```bash -mint dev --port 3333 -``` - -If you attempt to run Mintlify on a port that's already in use, it will use the next available port: - -```md -Port 3000 is already in use. Trying 3001 instead. -``` - -## Mintlify versions - -Please note that each CLI release is associated with a specific version of Mintlify. If your local preview does not align with the production version, please update the CLI: - -```bash -npm mint update -``` - -## Validating links - -The CLI can assist with validating links in your documentation. To identify any broken links, use the following command: - -```bash -mint broken-links -``` - -## Deployment - -If the deployment is successful, you should see the following: - - - Screenshot of a deployment confirmation message that says All checks have passed. - - -## Code formatting - -We suggest using extensions on your IDE to recognize and format MDX. If you're a VSCode user, consider the [MDX VSCode extension](https://marketplace.visualstudio.com/items?itemName=unifiedjs.vscode-mdx) for syntax highlighting, and [Prettier](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode) for code formatting. - -## Troubleshooting - - - - - This may be due to an outdated version of node. Try the following: - 1. Remove the currently-installed version of the CLI: `npm remove -g mint` - 2. Upgrade to Node v19 or higher. - 3. Reinstall the CLI: `npm i -g mint` - - - - - Solution: Go to the root of your device and delete the `~/.mintlify` folder. Then run `mint dev` again. - - - -Curious about what changed in the latest CLI version? Check out the [CLI changelog](https://www.npmjs.com/package/mintlify?activeTab=versions). diff --git a/docs.json b/docs.json index 46b44cc..66241f9 100644 --- a/docs.json +++ b/docs.json @@ -1,11 +1,11 @@ { "$schema": "https://mintlify.com/docs.json", "theme": "mint", - "name": "Mint Starter Kit", + "name": "UseZombie", "colors": { - "primary": "#16A34A", - "light": "#07C983", - "dark": "#15803D" + "primary": "#d96b2b", + "light": "#e78a3c", + "dark": "#c45a1f" }, "favicon": "/favicon.svg", "navigation": { @@ -18,31 +18,49 @@ "pages": [ "index", "quickstart", - "development" + "how-it-works", + "concepts" ] }, { - "group": "Customization", + "group": "Specs", "pages": [ - "essentials/settings", - "essentials/navigation" + "specs/writing-specs", + "specs/supported-formats", + "specs/validation" ] }, { - "group": "Writing content", + "group": "Runs", "pages": [ - "essentials/markdown", - "essentials/code", - "essentials/images", - "essentials/reusable-snippets" + "runs/submitting", + "runs/gate-loop", + "runs/scorecard", + "runs/pr-output", + "runs/troubleshooting" ] }, { - "group": "AI tools", + "group": "Workspaces", "pages": [ - "ai-tools/cursor", - "ai-tools/claude-code", - "ai-tools/windsurf" + "workspaces/overview", + "workspaces/managing" + ] + }, + { + "group": "Billing and cost control", + "pages": [ + "billing/plans", + "billing/budgets" + ] + }, + { + "group": "CLI reference", + "pages": [ + "cli/install", + "cli/zombiectl", + "cli/flags", + "cli/configuration" ] } ] @@ -51,18 +69,36 @@ "tab": "API reference", "groups": [ { - "group": "API documentation", + "group": "Overview", "pages": [ "api-reference/introduction" ] }, { - "group": "Endpoint examples", + "group": "Runs", + "pages": [ + "api-reference/endpoint/create-run", + "api-reference/endpoint/get-run", + "api-reference/endpoint/retry-run" + ] + }, + { + "group": "Agent relay", + "pages": [ + "api-reference/endpoint/agent-stream" + ] + }, + { + "group": "Specs", + "pages": [ + "api-reference/endpoint/list-specs" + ] + }, + { + "group": "Workspaces", "pages": [ - "api-reference/endpoint/get", - "api-reference/endpoint/create", - "api-reference/endpoint/delete", - "api-reference/endpoint/webhook" + "api-reference/endpoint/pause-workspace", + "api-reference/endpoint/sync-workspace" ] } ] @@ -71,13 +107,13 @@ "global": { "anchors": [ { - "anchor": "Documentation", - "href": "https://mintlify.com/docs", - "icon": "book-open-cover" + "anchor": "GitHub", + "href": "https://github.com/usezombie/usezombie", + "icon": "github" }, { "anchor": "Blog", - "href": "https://mintlify.com/blog", + "href": "https://usezombie.com/blog", "icon": "newspaper" } ] @@ -91,32 +127,32 @@ "links": [ { "label": "Support", - "href": "mailto:hi@mintlify.com" + "href": "mailto:support@usezombie.com" } ], "primary": { "type": "button", - "label": "Dashboard", - "href": "https://dashboard.mintlify.com" + "label": "Mission Control", + "href": "https://app.usezombie.com" } }, "contextual": { "options": [ - "copy", - "view", - "chatgpt", - "claude", - "perplexity", - "mcp", - "cursor", - "vscode" - ] + "copy", + "view", + "chatgpt", + "claude", + "perplexity", + "mcp", + "cursor", + "vscode" + ] }, "footer": { "socials": { - "x": "https://x.com/mintlify", - "github": "https://github.com/mintlify", - "linkedin": "https://linkedin.com/company/mintlify" + "x": "https://x.com/usezombie", + "github": "https://github.com/usezombie", + "discord": "https://discord.gg/usezombie" } } } diff --git a/essentials/code.mdx b/essentials/code.mdx deleted file mode 100644 index ae2abbf..0000000 --- a/essentials/code.mdx +++ /dev/null @@ -1,35 +0,0 @@ ---- -title: 'Code blocks' -description: 'Display inline code and code blocks' -icon: 'code' ---- - -## Inline code - -To denote a `word` or `phrase` as code, enclose it in backticks (`). - -``` -To denote a `word` or `phrase` as code, enclose it in backticks (`). -``` - -## Code blocks - -Use [fenced code blocks](https://www.markdownguide.org/extended-syntax/#fenced-code-blocks) by enclosing code in three backticks and follow the leading ticks with the programming language of your snippet to get syntax highlighting. Optionally, you can also write the name of your code after the programming language. - -```java HelloWorld.java -class HelloWorld { - public static void main(String[] args) { - System.out.println("Hello, World!"); - } -} -``` - -````md -```java HelloWorld.java -class HelloWorld { - public static void main(String[] args) { - System.out.println("Hello, World!"); - } -} -``` -```` diff --git a/essentials/images.mdx b/essentials/images.mdx deleted file mode 100644 index 1144eb2..0000000 --- a/essentials/images.mdx +++ /dev/null @@ -1,59 +0,0 @@ ---- -title: 'Images and embeds' -description: 'Add image, video, and other HTML elements' -icon: 'image' ---- - - - -## Image - -### Using Markdown - -The [markdown syntax](https://www.markdownguide.org/basic-syntax/#images) lets you add images using the following code - -```md -![title](/path/image.jpg) -``` - -Note that the image file size must be less than 5MB. Otherwise, we recommend hosting on a service like [Cloudinary](https://cloudinary.com/) or [S3](https://aws.amazon.com/s3/). You can then use that URL and embed. - -### Using embeds - -To get more customizability with images, you can also use [embeds](/writing-content/embed) to add images - -```html - -``` - -## Embeds and HTML elements - - - -
- - - -Mintlify supports [HTML tags in Markdown](https://www.markdownguide.org/basic-syntax/#html). This is helpful if you prefer HTML tags to Markdown syntax, and lets you create documentation with infinite flexibility. - - - -### iFrames - -Loads another HTML page within the document. Most commonly used for embedding videos. - -```html - -``` diff --git a/essentials/markdown.mdx b/essentials/markdown.mdx deleted file mode 100644 index a45c1d5..0000000 --- a/essentials/markdown.mdx +++ /dev/null @@ -1,88 +0,0 @@ ---- -title: 'Markdown syntax' -description: 'Text, title, and styling in standard markdown' -icon: 'text-size' ---- - -## Titles - -Best used for section headers. - -```md -## Titles -``` - -### Subtitles - -Best used for subsection headers. - -```md -### Subtitles -``` - - - -Each **title** and **subtitle** creates an anchor and also shows up on the table of contents on the right. - - - -## Text formatting - -We support most markdown formatting. Simply add `**`, `_`, or `~` around text to format it. - -| Style | How to write it | Result | -| ------------- | ----------------- | --------------- | -| Bold | `**bold**` | **bold** | -| Italic | `_italic_` | _italic_ | -| Strikethrough | `~strikethrough~` | ~strikethrough~ | - -You can combine these. For example, write `**_bold and italic_**` to get **_bold and italic_** text. - -You need to use HTML to write superscript and subscript text. That is, add `` or `` around your text. - -| Text Size | How to write it | Result | -| ----------- | ------------------------ | ---------------------- | -| Superscript | `superscript` | superscript | -| Subscript | `subscript` | subscript | - -## Linking to pages - -You can add a link by wrapping text in `[]()`. You would write `[link to google](https://google.com)` to [link to google](https://google.com). - -Links to pages in your docs need to be root-relative. Basically, you should include the entire folder path. For example, `[link to text](/writing-content/text)` links to the page "Text" in our components section. - -Relative links like `[link to text](../text)` will open slower because we cannot optimize them as easily. - -## Blockquotes - -### Singleline - -To create a blockquote, add a `>` in front of a paragraph. - -> Dorothy followed her through many of the beautiful rooms in her castle. - -```md -> Dorothy followed her through many of the beautiful rooms in her castle. -``` - -### Multiline - -> Dorothy followed her through many of the beautiful rooms in her castle. -> -> The Witch bade her clean the pots and kettles and sweep the floor and keep the fire fed with wood. - -```md -> Dorothy followed her through many of the beautiful rooms in her castle. -> -> The Witch bade her clean the pots and kettles and sweep the floor and keep the fire fed with wood. -``` - -### LaTeX - -Mintlify supports [LaTeX](https://www.latex-project.org) through the Latex component. - -8 x (vk x H1 - H2) = (0,1) - -```md -8 x (vk x H1 - H2) = (0,1) -``` diff --git a/essentials/navigation.mdx b/essentials/navigation.mdx deleted file mode 100644 index 60adeff..0000000 --- a/essentials/navigation.mdx +++ /dev/null @@ -1,87 +0,0 @@ ---- -title: 'Navigation' -description: 'The navigation field in docs.json defines the pages that go in the navigation menu' -icon: 'map' ---- - -The navigation menu is the list of links on every website. - -You will likely update `docs.json` every time you add a new page. Pages do not show up automatically. - -## Navigation syntax - -Our navigation syntax is recursive which means you can make nested navigation groups. You don't need to include `.mdx` in page names. - - - -```json Regular Navigation -"navigation": { - "tabs": [ - { - "tab": "Docs", - "groups": [ - { - "group": "Getting Started", - "pages": ["quickstart"] - } - ] - } - ] -} -``` - -```json Nested Navigation -"navigation": { - "tabs": [ - { - "tab": "Docs", - "groups": [ - { - "group": "Getting Started", - "pages": [ - "quickstart", - { - "group": "Nested Reference Pages", - "pages": ["nested-reference-page"] - } - ] - } - ] - } - ] -} -``` - - - -## Folders - -Simply put your MDX files in folders and update the paths in `docs.json`. - -For example, to have a page at `https://yoursite.com/your-folder/your-page` you would make a folder called `your-folder` containing an MDX file called `your-page.mdx`. - - - -You cannot use `api` for the name of a folder unless you nest it inside another folder. Mintlify uses Next.js which reserves the top-level `api` folder for internal server calls. A folder name such as `api-reference` would be accepted. - - - -```json Navigation With Folder -"navigation": { - "tabs": [ - { - "tab": "Docs", - "groups": [ - { - "group": "Group Name", - "pages": ["your-folder/your-page"] - } - ] - } - ] -} -``` - -## Hidden pages - -MDX files not included in `docs.json` will not show up in the sidebar but are accessible through the search bar and by linking directly to them. diff --git a/essentials/reusable-snippets.mdx b/essentials/reusable-snippets.mdx deleted file mode 100644 index 376e27b..0000000 --- a/essentials/reusable-snippets.mdx +++ /dev/null @@ -1,110 +0,0 @@ ---- -title: "Reusable snippets" -description: "Reusable, custom snippets to keep content in sync" -icon: "recycle" ---- - -import SnippetIntro from '/snippets/snippet-intro.mdx'; - - - -## Creating a custom snippet - -**Pre-condition**: You must create your snippet file in the `snippets` directory. - - - Any page in the `snippets` directory will be treated as a snippet and will not - be rendered into a standalone page. If you want to create a standalone page - from the snippet, import the snippet into another file and call it as a - component. - - -### Default export - -1. Add content to your snippet file that you want to re-use across multiple - locations. Optionally, you can add variables that can be filled in via props - when you import the snippet. - -```mdx snippets/my-snippet.mdx -Hello world! This is my content I want to reuse across pages. My keyword of the -day is {word}. -``` - - - The content that you want to reuse must be inside the `snippets` directory in - order for the import to work. - - -2. Import the snippet into your destination file. - -```mdx destination-file.mdx ---- -title: My title -description: My Description ---- - -import MySnippet from '/snippets/path/to/my-snippet.mdx'; - -## Header - -Lorem impsum dolor sit amet. - - -``` - -### Reusable variables - -1. Export a variable from your snippet file: - -```mdx snippets/path/to/custom-variables.mdx -export const myName = 'my name'; - -export const myObject = { fruit: 'strawberries' }; -``` - -2. Import the snippet from your destination file and use the variable: - -```mdx destination-file.mdx ---- -title: My title -description: My Description ---- - -import { myName, myObject } from '/snippets/path/to/custom-variables.mdx'; - -Hello, my name is {myName} and I like {myObject.fruit}. -``` - -### Reusable components - -1. Inside your snippet file, create a component that takes in props by exporting - your component in the form of an arrow function. - -```mdx snippets/custom-component.mdx -export const MyComponent = ({ title }) => ( -
-

{title}

-

... snippet content ...

-
-); -``` - - - MDX does not compile inside the body of an arrow function. Stick to HTML - syntax when you can or use a default export if you need to use MDX. - - -2. Import the snippet into your destination file and pass in the props - -```mdx destination-file.mdx ---- -title: My title -description: My Description ---- - -import { MyComponent } from '/snippets/custom-component.mdx'; - -Lorem ipsum dolor sit amet. - - -``` diff --git a/essentials/settings.mdx b/essentials/settings.mdx deleted file mode 100644 index 884de13..0000000 --- a/essentials/settings.mdx +++ /dev/null @@ -1,318 +0,0 @@ ---- -title: 'Global Settings' -description: 'Mintlify gives you complete control over the look and feel of your documentation using the docs.json file' -icon: 'gear' ---- - -Every Mintlify site needs a `docs.json` file with the core configuration settings. Learn more about the [properties](#properties) below. - -## Properties - - -Name of your project. Used for the global title. - -Example: `mintlify` - - - - - An array of groups with all the pages within that group - - - The name of the group. - - Example: `Settings` - - - - The relative paths to the markdown files that will serve as pages. - - Example: `["customization", "page"]` - - - - - - - - Path to logo image or object with path to "light" and "dark" mode logo images - - - Path to the logo in light mode - - - Path to the logo in dark mode - - - Where clicking on the logo links you to - - - - - - Path to the favicon image - - - - Hex color codes for your global theme - - - The primary color. Used for most often for highlighted content, section - headers, accents, in light mode - - - The primary color for dark mode. Used for most often for highlighted - content, section headers, accents, in dark mode - - - The primary color for important buttons - - - The color of the background in both light and dark mode - - - The hex color code of the background in light mode - - - The hex color code of the background in dark mode - - - - - - - - Array of `name`s and `url`s of links you want to include in the topbar - - - The name of the button. - - Example: `Contact us` - - - The url once you click on the button. Example: `https://mintlify.com/docs` - - - - - - - - - Link shows a button. GitHub shows the repo information at the url provided including the number of GitHub stars. - - - If `link`: What the button links to. - - If `github`: Link to the repository to load GitHub information from. - - - Text inside the button. Only required if `type` is a `link`. - - - - - - - Array of version names. Only use this if you want to show different versions - of docs with a dropdown in the navigation bar. - - - - An array of the anchors, includes the `icon`, `color`, and `url`. - - - The [Font Awesome](https://fontawesome.com/search?q=heart) icon used to feature the anchor. - - Example: `comments` - - - The name of the anchor label. - - Example: `Community` - - - The start of the URL that marks what pages go in the anchor. Generally, this is the name of the folder you put your pages in. - - - The hex color of the anchor icon background. Can also be a gradient if you pass an object with the properties `from` and `to` that are each a hex color. - - - Used if you want to hide an anchor until the correct docs version is selected. - - - Pass `true` if you want to hide the anchor until you directly link someone to docs inside it. - - - One of: "brands", "duotone", "light", "sharp-solid", "solid", or "thin" - - - - - - - Override the default configurations for the top-most anchor. - - - The name of the top-most anchor - - - Font Awesome icon. - - - One of: "brands", "duotone", "light", "sharp-solid", "solid", or "thin" - - - - - - An array of navigational tabs. - - - The name of the tab label. - - - The start of the URL that marks what pages go in the tab. Generally, this - is the name of the folder you put your pages in. - - - - - - Configuration for API settings. Learn more about API pages at [API Components](/api-playground/demo). - - - The base url for all API endpoints. If `baseUrl` is an array, it will enable for multiple base url - options that the user can toggle. - - - - - - The authentication strategy used for all API endpoints. - - - The name of the authentication parameter used in the API playground. - - If method is `basic`, the format should be `[usernameName]:[passwordName]` - - - The default value that's designed to be a prefix for the authentication input field. - - E.g. If an `inputPrefix` of `AuthKey` would inherit the default input result of the authentication field as `AuthKey`. - - - - - - Configurations for the API playground - - - - Whether the playground is showing, hidden, or only displaying the endpoint with no added user interactivity `simple` - - Learn more at the [playground guides](/api-playground/demo) - - - - - - Enabling this flag ensures that key ordering in OpenAPI pages matches the key ordering defined in the OpenAPI file. - - This behavior will soon be enabled by default, at which point this field will be deprecated. - - - - - - - A string or an array of strings of URL(s) or relative path(s) pointing to your - OpenAPI file. - - Examples: - - ```json Absolute - "openapi": "https://example.com/openapi.json" - ``` - ```json Relative - "openapi": "/openapi.json" - ``` - ```json Multiple - "openapi": ["https://example.com/openapi1.json", "/openapi2.json", "/openapi3.json"] - ``` - - - - - - An object of social media accounts where the key:property pair represents the social media platform and the account url. - - Example: - ```json - { - "x": "https://x.com/mintlify", - "website": "https://mintlify.com" - } - ``` - - - One of the following values `website`, `facebook`, `x`, `discord`, `slack`, `github`, `linkedin`, `instagram`, `hacker-news` - - Example: `x` - - - The URL to the social platform. - - Example: `https://x.com/mintlify` - - - - - - Configurations to enable feedback buttons - - - - Enables a button to allow users to suggest edits via pull requests - - - Enables a button to allow users to raise an issue about the documentation - - - - - - Customize the dark mode toggle. - - - Set if you always want to show light or dark mode for new users. When not - set, we default to the same mode as the user's operating system. - - - Set to true to hide the dark/light mode toggle. You can combine `isHidden` with `default` to force your docs to only use light or dark mode. For example: - - - ```json Only Dark Mode - "modeToggle": { - "default": "dark", - "isHidden": true - } - ``` - - ```json Only Light Mode - "modeToggle": { - "default": "light", - "isHidden": true - } - ``` - - - - - - - - - A background image to be displayed behind every page. See example with - [Infisical](https://infisical.com/docs) and [FRPC](https://frpc.io). - diff --git a/favicon.svg b/favicon.svg index b785c73..06e1bb0 100644 --- a/favicon.svg +++ b/favicon.svg @@ -4,16 +4,16 @@ - - + + - - + + - - + + diff --git a/how-it-works.mdx b/how-it-works.mdx new file mode 100644 index 0000000..89e929c --- /dev/null +++ b/how-it-works.mdx @@ -0,0 +1,151 @@ +--- +title: "How it works" +description: "The spec-to-PR lifecycle with self-repairing agents." +--- + +## The spec-to-PR lifecycle + +UseZombie turns a markdown spec into a validated pull request through a deterministic pipeline: validate, implement, gate, score, ship. + +```mermaid +flowchart LR + A[Write spec] --> B[Submit via CLI/API] + B --> C[Validate spec] + C --> D[Agent implements] + D --> E[Gate loop] + E -->|Pass| F[Score run] + E -->|Fail| G[Self-repair] + G --> E + F --> H[Open PR with scorecard] +``` + +## Step by step + + + + A spec is a markdown file describing what you want built. It can follow any format — structured sections, free-form prose, bullet lists. The agent reads natural language and infers intent from your codebase context. + + You describe **what** to build. The agent figures out **how**. + + + + Submit via `zombiectl run --spec ` or the REST API (`POST /v1/runs`). On submission, UseZombie validates that referenced files exist in the workspace, deduplicates against in-flight runs, and enqueues the work. + + + + The NullClaw agent runtime picks up the run and works inside an **isolated git worktree** — a fresh working directory branched from your default branch. The agent receives the spec plus injected codebase context (relevant file contents, module structure) to produce an accurate implementation. + + No changes touch your main branch until you approve the PR. + + + + After implementation, the agent runs your project's standard validation gates in sequence: + + 1. `make lint` — linting and type checks + 2. `make test` — unit tests + 3. `make build` — production build + + If any gate fails, the agent reads the error output, diagnoses the issue, and self-repairs. This loop runs up to **3 times** by default. If all repair attempts fail, the run is marked `FAILED` with full error context. + + + The repair limit is configurable per agent profile. See [Gate loop](/runs/gate-loop) for details. + + + + + Every completed run receives a **scorecard** with four weighted dimensions: + + | Dimension | Weight | What it measures | + |-----------|--------|------------------| + | Completion | 40% | Did the agent implement everything the spec asked for? | + | Error rate | 30% | How many gate failures occurred before passing? | + | Latency | 20% | Wall-clock time from enqueue to PR. | + | Resource efficiency | 10% | Token and compute usage relative to task complexity. | + + Scores map to tiers: + + | Tier | Score range | + |------|-------------| + | Bronze | 0 -- 39 | + | Silver | 40 -- 69 | + | Gold | 70 -- 89 | + | Elite | 90 -- 100 | + + + + The agent pushes a branch named `zombie//` and opens a pull request on GitHub. The PR body contains an agent-generated explanation of what was implemented and why. A scorecard comment is posted with the quality metrics. + + From here, it's a normal code review. Approve, request changes, or close. + + + +## Runtime architecture + +Under the hood, the CLI, API server, queue, worker, and executor coordinate to move a run from submission to PR. + +```mermaid +sequenceDiagram + participant CLI as zombiectl + participant API as zombied API + participant Q as Redis Streams + participant W as zombied worker + participant E as zombied-executor + participant GH as GitHub + + CLI->>API: POST /v1/runs (spec) + API->>Q: enqueue run_id + Q->>W: claim work + W->>E: StartStage (agent config, tools) + E->>E: NullClaw agent implements + E->>W: ExecutionResult + W->>W: Gate loop (lint/test/build) + W->>GH: push branch + open PR + W->>GH: post scorecard comment +``` + +**Component responsibilities:** + +- **zombiectl** — CLI client. Submits specs, checks status, streams logs. +- **zombied API** — HTTP server. Validates specs, manages runs and workspaces, serves the dashboard. +- **Redis Streams** — Work queue. Durable, ordered, with consumer group semantics for worker fleet scaling. +- **zombied worker** — Claim runs, orchestrate the gate loop, push results to GitHub. Supports drain and rolling deploys. +- **zombied-executor** — Sidecar process that owns the sandbox lifecycle. Spawns NullClaw agents, manages worktrees, enforces resource limits. +- **GitHub** — Target forge. Branch push, PR creation, scorecard comments. + +## Agent relay model + +For lightweight, interactive agent sessions (`spec init`, `run --preview`), UseZombie uses a different execution model: the **agent relay**. Instead of queuing work for a sandbox, `zombied` acts as a stateless pass-through between the CLI and the workspace's LLM provider. + +```mermaid +sequenceDiagram + participant CLI as zombiectl + participant API as zombied API + participant LLM as LLM Provider + + CLI->>API: POST /v1/agent/stream (mode, tools, messages) + API->>LLM: Forward with system prompt + API key + LLM-->>API: tool_use: list_dir(".") + API-->>CLI: SSE: event: tool_use + Note over CLI: Executes locally on laptop + CLI->>API: POST /v1/agent/stream (messages + tool_result) + API->>LLM: Forward accumulated messages + LLM-->>API: tool_use: read_file("go.mod") + API-->>CLI: SSE: event: tool_use + Note over CLI: Reads file locally + CLI->>API: POST /v1/agent/stream (messages + tool_result) + API->>LLM: Forward accumulated messages + LLM-->>API: text: "# M5_001: Rate Limiting..." + API-->>CLI: SSE: event: text_delta + done {usage} +``` + +**Key differences from the pipeline model:** + +| | Pipeline (full runs) | Agent relay (spec init, preview) | +|---|---|---| +| **Execution** | Sandbox on worker, queued | Direct handler, no queue | +| **File access** | Agent reads files in sandbox | CLI reads files locally, sends to model on demand | +| **Duration** | 1-5 minutes | 3-8 seconds | +| **State** | Durable (DB + Redis) | Stateless (CLI manages conversation) | +| **Provider** | Configured per workspace | Same, resolved by `zombied` | + +The relay model is inspired by how Claude Code and OpenCode work: the CLI holds tool definitions, the model calls them on demand, and the API layer is a relay. The difference is `zombied` sits between CLI and provider because API keys are server-side secrets managed per-workspace. diff --git a/index.mdx b/index.mdx index 15c23fb..77c7009 100644 --- a/index.mdx +++ b/index.mdx @@ -1,97 +1,54 @@ --- -title: "Introduction" -description: "Welcome to the new home for your documentation" +title: Introduction +description: Submit a spec. Get a validated PR. --- -## Setting up + + 🧟 **Early Access Preview** · Launching **April 5, 2026** -Get your documentation site up and running in minutes. + You're reading the docs for an unreleased product. Everything here works today in dev, but APIs, CLI surface, and agent behavior may shift before GA. [Join the waitlist →](https://usezombie.com) + - - Follow our three step quickstart guide. +## Getting started + +UseZombie turns markdown specs into validated pull requests with self-repairing agents, run quality scoring, and evidence-backed scorecards so teams ship with confidence, not babysitting. + + + Install the CLI and submit your first spec in under 5 minutes. ## Make it yours -Design a docs site that looks great and empowers your users. +Connect your GitHub repo, configure your workspace, and let agents ship validated PRs while you focus on specs and reviews. - - Edit your docs locally and preview them in real time. + + Install zombiectl via npm, npx, or the install script. - - Customize the design and colors of your site to match your brand. + + Understand the spec-to-PR lifecycle with self-repair gate loops. - - Organize your docs to help users find what they need and succeed with your product. + + Author specs in markdown. Any format works. - - Auto-generate API documentation from OpenAPI specifications. + + View runs, scorecards, and workspace settings in the dashboard. -## Create beautiful pages - -Everything you need to create world-class documentation. +## Explore the docs - - Use MDX to style your docs pages. + + Full command reference for zombiectl. - - Add sample code to demonstrate how to use your product. + + REST API for programmatic spec submission and run management. - - Display images and other media. + + Deploy and operate the UseZombie control plane. - - Write once and reuse across your docs. + + Contribute to UseZombie on GitHub. - -## Need inspiration? - - - Browse our showcase of exceptional documentation sites. - diff --git a/logo/dark.svg b/logo/dark.svg index 8b343cd..3d4e294 100644 --- a/logo/dark.svg +++ b/logo/dark.svg @@ -6,16 +6,16 @@ - - + + - - + + - - + + diff --git a/logo/light.svg b/logo/light.svg index 03e62bf..5cc564d 100644 --- a/logo/light.svg +++ b/logo/light.svg @@ -3,19 +3,19 @@ - + - - + + - - + + - - + + diff --git a/mise.toml b/mise.toml new file mode 100644 index 0000000..6ea5a7e --- /dev/null +++ b/mise.toml @@ -0,0 +1,2 @@ +[tools] +node = "24" diff --git a/operator/configuration/credentials.mdx b/operator/configuration/credentials.mdx new file mode 100644 index 0000000..6731529 --- /dev/null +++ b/operator/configuration/credentials.mdx @@ -0,0 +1,56 @@ +--- +title: "Credentials" +description: "Credential injection and lifecycle." +--- + +## Overview + +UseZombie handles three categories of credentials, each with a different injection path and lifecycle. No credential is ever exposed as an environment variable to the executor process. + +## Anthropic API key + +The Anthropic API key powers the agent runtime (Claude). It is stored in your secret manager and deployed to worker machines via the `.env` file. + +| Property | Value | +|----------|-------| +| Storage | Secret manager vault | +| Injection | `.env` file on worker machine | +| Scope | Per-worker | +| Rotation | Manual, via secret manager | +| Agent exposure | **None** — the key is never set in the executor environment. It is passed inside the `startStage` JSON-RPC payload so the executor can make API calls on behalf of the agent without leaking the key to agent code. | + +The key never appears in logs, error messages, or PR artifacts. The worker strips it from all output before recording results. + +## GitHub App installation token + +The UseZombie GitHub App is installed on target repositories. The worker requests short-lived installation tokens scoped to the specific repository for each run. + +| Property | Value | +|----------|-------| +| Storage | GitHub App private key in secret manager vault | +| Injection | Worker signs a JWT using the private key at runtime | +| Scope | Per-repository, per-run | +| TTL | 1 hour (GitHub default) | +| Auto-refresh | Worker refreshes at 55 minutes if the run is still active | +| Permissions | Contents (read/write), Pull requests (read/write), Metadata (read) | + +Token lifecycle: + +1. Run is claimed by a worker. +2. Worker signs a JWT using the GitHub App private key. +3. Worker exchanges the JWT for an installation token scoped to the target repo. +4. Token is used for clone, push, and PR creation. +5. If the run exceeds 55 minutes, the worker requests a fresh token. +6. Token expires naturally after 1 hour if not refreshed. + +## Package registry credentials + +For workloads that need to install dependencies during execution (when `EXECUTOR_NETWORK_POLICY=registry_allowlist`), registry access is handled in two phases: + +### Phase 1: Explicit allowlist (current) + +Network policy allows outbound connections only to known registry hosts. Public registries (npm, PyPI, crates.io, Go proxy) do not require authentication. Private registries are not supported in Phase 1. + +### Phase 2: Internal mirror (planned) + +An internal package mirror will cache approved packages. The executor will be configured to use the mirror as its sole registry source. This eliminates direct internet access from the sandbox entirely and enables support for private registries through the mirror's credential store. diff --git a/operator/configuration/environment.mdx b/operator/configuration/environment.mdx new file mode 100644 index 0000000..b3f8a90 --- /dev/null +++ b/operator/configuration/environment.mdx @@ -0,0 +1,80 @@ +--- +title: "Environment variables" +description: "All configuration variables by role." +--- + +## Overview + +UseZombie components are configured through environment variables. Each role (API, Worker, Executor) requires a different set of variables. On bare-metal deployments, variables are loaded from `/opt/zombie/.env` via the systemd `EnvironmentFile` directive. + +UseZombie uses **role-separated database and Redis URLs**. The API and worker each get their own connection string with role-appropriate permissions. + +## API server + +Variables required by `zombied serve`. + +| Variable | Required | Default | Description | +|----------|----------|---------|-------------| +| `DATABASE_URL_API` | Yes | — | PostgreSQL connection string for the API role. | +| `REDIS_URL_API` | Yes | — | Redis connection string for the API role. | +| `CLERK_SECRET_KEY` | Yes | — | Clerk backend API secret key for JWT verification. | +| `ENCRYPTION_MASTER_KEY` | Yes | — | Master encryption key for secrets at rest. | +| `PORT` | No | `3000` | HTTP API listen port. | +| `API_HTTP_THREADS` | No | `1` | HTTP thread pool size. | +| `API_HTTP_WORKERS` | No | `1` | HTTP worker count. | +| `API_MAX_CLIENTS` | No | `1024` | Maximum concurrent client connections. | +| `API_MAX_IN_FLIGHT_REQUESTS` | No | `256` | Maximum in-flight request limit. | +| `RATE_LIMIT_CAPACITY` | No | `30` | Token bucket capacity for rate limiting. | +| `RATE_LIMIT_REFILL_PER_SEC` | No | `5.0` | Token bucket refill rate per second. | +| `DEFAULT_MAX_ATTEMPTS` | No | `3` | Default maximum run retry attempts. | +| `APP_URL` | No | `https://app.usezombie.com` | Application base URL for links. | +| `GIT_CACHE_ROOT` | No | `/tmp/zombie-git-cache` | Git bare repo cache directory. | + +### OIDC (optional, alternative to Clerk) + +| Variable | Required | Default | Description | +|----------|----------|---------|-------------| +| `OIDC_PROVIDER` | No | — | OIDC provider name (enables OIDC auth path). | +| `OIDC_JWKS_URL` | No | — | JWKS endpoint URL for token verification. | +| `OIDC_ISSUER` | No | — | Expected token issuer. | +| `OIDC_AUDIENCE` | No | — | Expected token audience. | + +## Worker + +Variables required by `zombied worker`. + +| Variable | Required | Default | Description | +|----------|----------|---------|-------------| +| `DATABASE_URL_WORKER` | Yes | — | PostgreSQL connection string for the worker role. | +| `REDIS_URL_WORKER` | Yes | — | Redis connection string for work queue claims. | +| `SANDBOX_BACKEND` | No | `host` | Sandbox backend: `host` (no isolation) or `bubblewrap`. | +| `WORKER_CONCURRENCY` | No | `1` | Number of concurrent runs this worker can claim. | +| `RUN_TIMEOUT_MS` | No | `300000` | Maximum wall-clock time for a single run (5 min default). | +| `EXECUTOR_SOCKET_PATH` | No | `/run/zombie/executor.sock` | Path to the executor Unix socket. | +| `DRAIN_TIMEOUT_MS` | No | `270000` | Graceful shutdown timeout when draining active runs. | +| `EXECUTOR_STARTUP_TIMEOUT_MS` | No | `5000` | How long to wait for executor sidecar to become available. | +| `EXECUTOR_LEASE_TIMEOUT_MS` | No | `30000` | Executor lease validity period (heartbeat interval). | + +## Executor + +Variables required by `zombied-executor`. + +| Variable | Required | Default | Description | +|----------|----------|---------|-------------| +| `SANDBOX_BACKEND` | No | `host` | Sandbox backend: `host` or `bubblewrap`. Must match the worker setting. | +| `EXECUTOR_MEMORY_LIMIT_MB` | No | `512` | Memory limit per agent execution in megabytes. | +| `EXECUTOR_CPU_LIMIT_PERCENT` | No | `100` | CPU limit as a percentage of one core. | +| `SANDBOX_KILL_GRACE_MS` | No | `5000` | Grace period before force-killing a sandbox after timeout. | + + + Network policy (deny-by-default) is applied automatically by the executor's sandbox layer. There is no environment variable to configure it. The policy is hardcoded in `network.zig` and denies all egress by default. + + +## Shared variables + +These variables are used by multiple roles. + +| Variable | Used by | Description | +|----------|---------|-------------| +| `SANDBOX_BACKEND` | Worker, Executor | Must be set to the same value on both. | +| `LOG_LEVEL` | All | Log verbosity: `debug`, `info`, `warn`, `error`. Default: `info`. | diff --git a/operator/configuration/sandbox.mdx b/operator/configuration/sandbox.mdx new file mode 100644 index 0000000..4532b4a --- /dev/null +++ b/operator/configuration/sandbox.mdx @@ -0,0 +1,68 @@ +--- +title: "Sandbox configuration" +description: "Resource limits and isolation policies." +--- + +## Overview + +Every agent execution runs inside a sandbox that enforces resource limits and isolation policies. These settings control how much compute, memory, network access, and filesystem access an agent is allowed. + +## Memory limit + +| Setting | Variable | Default | +|---------|----------|---------| +| Memory cap | `EXECUTOR_MEMORY_LIMIT_MB` | `512` | + +The executor creates a cgroups v2 memory scope for each agent execution. If the agent exceeds the memory limit, the kernel OOM-kills the process. The executor detects OOM events and records them as `UZ-EXEC-009` failures. + +Setting this too low causes frequent OOM kills on compilation-heavy workloads. Setting it too high risks one runaway agent starving others on multi-concurrency workers. + +## CPU limit + +| Setting | Variable | Default | +|---------|----------|---------| +| CPU cap | `EXECUTOR_CPU_LIMIT_PERCENT` | `100` | + +CPU is limited to a percentage of one core via cgroups v2 CPU bandwidth control. `100` means one full core. `50` means half a core. The limit prevents a single agent from monopolizing the machine. + +## Network policy + +Network policy is **hardcoded** in the executor's sandbox layer (`network.zig`). There is no environment variable to configure it. The default policy denies all egress. + +Two policies exist in the codebase: + +### deny_all + +All outbound network access is blocked. The agent cannot reach the internet. This is the default and the most secure option. Suitable for workloads where all dependencies are pre-installed or vendored. + +### registry_allowlist + +Outbound access is permitted only to a predefined list of package registries: + +| Registry | Hosts | +|----------|-------| +| npm | `registry.npmjs.org` | +| PyPI | `pypi.org`, `files.pythonhosted.org` | +| crates.io | `crates.io`, `static.crates.io` | +| Go modules | `proxy.golang.org`, `sum.golang.org` | + +All other destinations remain blocked. This mode is for workloads that need to install dependencies during execution. + +## Filesystem policy + +Filesystem isolation uses Landlock (Linux 5.13+). The policy is applied per-execution and cannot be changed by the agent. + +| Path | Access | Purpose | +|------|--------|---------| +| Workspace directory | Read-write | The cloned repo where the agent works. | +| `/usr`, `/lib`, `/bin` | Read-only | System binaries and libraries needed for compilation. | +| `/tmp` (private) | Read-write | Temporary files via `PrivateTmp`. Not shared with other processes. | +| Everything else | Denied | No access to host filesystem, other workspaces, or system config. | + +## Kill grace period + +| Setting | Variable | Default | +|---------|----------|---------| +| Grace period | `SANDBOX_KILL_GRACE_MS` | `5000` | + +When a sandbox exceeds its time limit, the executor sends `SIGTERM` and waits for the grace period before sending `SIGKILL`. This gives the agent time to flush partial results. diff --git a/operator/deployment/api-server.mdx b/operator/deployment/api-server.mdx new file mode 100644 index 0000000..9d98fa9 --- /dev/null +++ b/operator/deployment/api-server.mdx @@ -0,0 +1,59 @@ +--- +title: "API server" +description: "Deploy zombied in API mode on Fly.io." +--- + +## Overview + +The API server is started with `zombied serve`. It listens for HTTP requests on port 3000 and exposes Prometheus metrics on port 9091. The server handles authentication, spec validation, run lifecycle, workspace management, and webhook delivery. + +## Network topology + +The API runs on Fly.io inside a Docker container. It is **not** exposed on a public `*.fly.dev` address. Instead, a Cloudflare Tunnel connects the Fly.io instance to Cloudflare's edge network. + +- **HTTPS termination** happens at the Cloudflare edge. +- **Internal traffic** between Cloudflare Tunnel and the Fly.io container is plain HTTP. +- All public traffic routes through `api.usezombie.com` on the Cloudflare edge. + +``` +Client --> Cloudflare Edge (HTTPS) --> Cloudflare Tunnel --> zombied serve (HTTP :3000) +``` + +## Deployment + +The API is deployed as a Docker container on Fly.io. + +```dockerfile +FROM debian:bookworm-slim +COPY zombied /usr/local/bin/zombied +EXPOSE 3000 9091 +CMD ["zombied", "serve"] +``` + +Deploy with the Fly CLI: + +```bash +fly deploy --app zombied-api +``` + +## Ports + +| Port | Purpose | +|------|---------| +| `3000` | HTTP API (default, configurable via `PORT`) | +| `9091` | Prometheus metrics (configurable via `METRICS_PORT`) | + +## Health checks + +The API exposes two health endpoints: + +| Endpoint | Purpose | +|----------|---------| +| `GET /healthz` | Liveness probe — returns `200` if the process is running. | +| `GET /readyz` | Readiness probe — returns `200` if PostgreSQL and Redis are reachable. | + +## Startup checks + +On boot, `zombied serve` validates that all required environment variables are set and that it can connect to PostgreSQL and Redis. If any check fails, the process exits with a non-zero code and a diagnostic message naming the missing dependency. + +See [Environment variables](/operator/configuration/environment) for the full list of API configuration. diff --git a/operator/deployment/architecture.mdx b/operator/deployment/architecture.mdx new file mode 100644 index 0000000..4fe67f8 --- /dev/null +++ b/operator/deployment/architecture.mdx @@ -0,0 +1,66 @@ +--- +title: "Architecture overview" +description: "System components and runtime boundary." +--- + +## System diagram + +UseZombie is a spec-driven agent delivery platform. You submit a spec, and the system produces a pull request. Three runtime components handle this pipeline — all compiled from the same Zig binary (`zombied`) with different subcommands. + +```mermaid +graph TB + CLI[zombiectl CLI] -->|HTTPS| API[zombied API
Fly.io] + ExtAgent[External agents
Claude/Codex/OpenCode] -->|HTTPS| API + API -->|enqueue| Redis[Redis Streams] + Redis -->|claim| Worker[zombied worker
bare-metal] + Worker -->|JSON-RPC
Unix socket| Executor[zombied-executor
sidecar] + Executor -->|NullClaw| Agent[Agent runtime] + Agent -->|sandbox| Sandbox[Landlock + cgroups + network deny] + Worker -->|push branch
open PR| GitHub[GitHub] + API --> PG[(PostgreSQL)] + Worker --> PG + API ---|Cloudflare Tunnel| CF[Cloudflare Edge] + Worker ---|Tailscale| TS[Tailscale Network] +``` + +## Components + +### zombiectl + +The CLI client. Operators and developers use `zombiectl` to submit specs, check run status, manage workspaces, and configure harnesses. It authenticates via Clerk and talks HTTPS to the API server. Distributed as a standalone binary for macOS and Linux. + +### zombied API + +The HTTP API server, started with `zombied serve`. Runs on Fly.io behind a Cloudflare Tunnel — no public `*.fly.dev` address is exposed. Handles authentication, workspace management, spec validation, run creation, and webhook delivery. Exposes Prometheus metrics on a separate port. + +### zombied worker + +The execution orchestrator, started with `zombied worker`. Runs on OVHCloud bare-metal machines connected via Tailscale. Claims work from Redis Streams, manages the run lifecycle (clone, stage execution, gate loop), and pushes branches and opens pull requests on GitHub when runs complete. + +### zombied-executor + +The sandboxed execution sidecar, started as a separate systemd service. Communicates with the worker over a Unix socket using JSON-RPC. Embeds the NullClaw agent runtime and applies sandbox policies (Landlock, cgroups v2, network deny) to every agent execution. This is the only component that runs untrusted agent code. + +### Redis + +Redis Streams serves as the work queue between the API and workers. The API enqueues run payloads, and workers claim them using consumer groups. Redis also powers distributed locking for worker fleet coordination. + +### PostgreSQL + +The persistent store for all domain state: workspaces, runs, stages, scorecards, billing records, and the transactional outbox. Both the API and worker connect directly. Migrations are managed by `zombied migrate`. + +### Clerk + +Third-party authentication provider. Handles user sign-up, sign-in, session tokens, and JWT verification. The API server validates Clerk JWTs on every request. Workspace membership and role assignment are stored in PostgreSQL and enforced server-side. + +### GitHub App + +The UseZombie GitHub App is installed on target repositories. The worker uses it to clone repos, push implementation branches, and open pull requests. Installation tokens are scoped per-repository with one-hour TTL, requested on demand by the worker for each run. + +## Version roadmap + +| Version | Codename | Focus | +|---------|----------|-------| +| **v1** | Ship | End-to-end spec-to-PR pipeline. Bubblewrap sandbox, CLI-first UX, free and paid billing tiers. | +| **v2** | Harden | Firecracker VM isolation, libgit2 native git (no subprocess), semgrep/gitleaks gates on PRs, webhook delivery hardening. | +| **v3** | Scale | Mission Control web UI, team model with org-level billing, multi-region worker fleet, self-hosted worker support. | diff --git a/operator/deployment/executor.mdx b/operator/deployment/executor.mdx new file mode 100644 index 0000000..612e27d --- /dev/null +++ b/operator/deployment/executor.mdx @@ -0,0 +1,77 @@ +--- +title: "Executor sidecar" +description: "The sandboxed execution service." +--- + +## Overview + +`zombied-executor` runs as a separate systemd service alongside the worker. It is the only component that executes untrusted agent code. The executor receives stage payloads from the worker over a Unix socket, applies sandbox policies, runs the agent, and returns execution results. + +## Communication + +The executor listens on a Unix socket at `/run/zombie/executor.sock`. The worker sends JSON-RPC requests over this socket. There is no network listener — the executor is never reachable from the network. + +``` +Worker --[JSON-RPC]--> /run/zombie/executor.sock --> Executor +``` + +### RPC methods + +| Method | Purpose | +|--------|---------| +| `startStage` | Begin executing an agent stage with the provided payload | +| `cancelStage` | Cancel a running stage execution | +| `healthCheck` | Verify the executor is ready to accept work | + +## Agent runtime + +The executor embeds the **NullClaw** agent runtime. NullClaw is the internal agent execution engine that interprets stage payloads and drives the agent through implementation, file edits, and command execution within the sandbox boundary. + +## Sandbox policies + +Every agent execution runs under four isolation layers: + +| Layer | Mechanism | Effect | +|-------|-----------|--------| +| Filesystem | Landlock | Workspace directory is read-write. System paths are read-only. Everything else is denied. | +| Memory and CPU | cgroups v2 | Memory capped at 512 MB (default). CPU limited to one core. OOM kills are detected and recorded. | +| Network | Network namespace deny | All outbound network access is denied by default. Optional allowlist for package registries. | +| Process | Systemd hardening | `PrivateTmp`, `ProtectSystem`, `NoNewPrivileges` restrict the process environment. | + +See [Sandbox enforcement](/operator/security/sandbox) for full details on each layer. + +## Systemd service + +```ini +# zombied-executor.service +[Unit] +Description=zombied executor sidecar + +[Service] +Type=simple +EnvironmentFile=/opt/zombie/.env +ExecStart=/opt/zombie/bin/zombied-executor +Restart=on-failure +RestartSec=3 + +# Hardening +PrivateTmp=true +ProtectSystem=strict +NoNewPrivileges=true + +[Install] +WantedBy=multi-user.target +``` + +## macOS development mode + +On macOS, the executor falls back to in-process execution without sandboxing. Landlock, cgroups, and network namespaces are Linux-only kernel features. This mode is for local development only and must never be used in production. + +```bash +# Dev mode — no sandbox enforcement +zombied-executor # Detects macOS, logs warning, runs without sandbox +``` + +## Failure handling + +If the executor process crashes or becomes unresponsive, the worker detects the failure through socket health checks. The systemd `Restart=on-failure` directive restarts the executor automatically. Any in-flight stage execution is marked as failed with error code `UZ-EXEC-003`. diff --git a/operator/deployment/worker.mdx b/operator/deployment/worker.mdx new file mode 100644 index 0000000..315cbd6 --- /dev/null +++ b/operator/deployment/worker.mdx @@ -0,0 +1,91 @@ +--- +title: "Worker" +description: "Deploy zombied in worker mode on bare-metal." +--- + +## Overview + +The worker is started with `zombied worker`. It claims work from Redis Streams, orchestrates the run lifecycle (repo clone, stage execution, gate loop), and pushes branches and opens pull requests on GitHub when runs complete. + +## Infrastructure + +Workers run on OVHCloud bare-metal machines connected to the control plane via Tailscale. Bare-metal provides the CPU, memory, and disk performance needed for compilation-heavy workloads without the overhead of nested virtualization. + +## Directory structure + +Each worker machine follows a standard layout: + +``` +/opt/zombie/ + bin/ + zombied # Main binary (worker mode) + zombied-executor # Executor sidecar binary + deploy/ + zombied-worker.service # Systemd unit for worker + zombied-executor.service # Systemd unit for executor + deploy.sh # Deployment script + .env # Environment config (from secret manager) +``` + +## Systemd services + +The worker runs as a systemd service with a hard dependency on the executor sidecar. The executor must be running before the worker starts. + +```ini +# zombied-worker.service +[Unit] +Description=zombied worker +Requires=zombied-executor.service +After=zombied-executor.service + +[Service] +Type=simple +EnvironmentFile=/opt/zombie/.env +ExecStart=/opt/zombie/bin/zombied worker +Restart=on-failure +RestartSec=5 + +[Install] +WantedBy=multi-user.target +``` + +## Deployment + +The `deploy.sh` script handles binary replacement, service restart, and health verification: + +```bash +# On the worker machine +cd /opt/zombie +./deploy/deploy.sh +``` + +The script: +1. Stops the worker service (waits for in-flight runs to drain). +2. Replaces binaries in `bin/`. +3. Restarts the executor service. +4. Starts the worker service. +5. Verifies health via `zombied doctor worker`. + +## Run lifecycle + +When a worker claims a run from Redis: + +1. Clone the target repository using the GitHub App installation token. +2. Execute each stage by sending JSON-RPC requests to the executor sidecar. +3. Run the gate loop (`lint` -> `test` -> `build`) after each stage. +4. If gates fail, feed errors back to the agent for self-repair (up to 3 retries). +5. Push the implementation branch and open a pull request. +6. Record the scorecard and mark the run as complete. + +## Networking + +Workers connect to: + +| Destination | Network | Purpose | +|-------------|---------|---------| +| PostgreSQL | Tailscale | Run state, workspace config | +| Redis | Tailscale | Work queue, distributed locks | +| GitHub | Public internet | Clone, push, PR creation | +| Executor | Unix socket | Stage execution | + +See [Fleet management](/operator/operations/fleet) for drain, rollover, and canary deploy procedures. diff --git a/operator/observability/error-codes.mdx b/operator/observability/error-codes.mdx new file mode 100644 index 0000000..e58a226 --- /dev/null +++ b/operator/observability/error-codes.mdx @@ -0,0 +1,46 @@ +--- +title: "Error codes" +description: "UZ-* error code reference." +--- + +## Overview + +UseZombie uses structured error codes prefixed with `UZ-` to classify failures. Every failed run or stage includes an error code in the API response, logs, and metrics. Use this reference to diagnose and remediate failures. + +## Execution errors + +Errors originating from the executor or agent runtime. + +| Code | Meaning | Remediation | +|------|---------|-------------| +| `UZ-EXEC-001` | Stage payload invalid | Check the spec for syntax errors. Re-validate with `zombiectl spec validate`. | +| `UZ-EXEC-002` | Repository clone failed | Verify the GitHub App is installed on the target repo. Check network connectivity from the worker. | +| `UZ-EXEC-003` | Executor unavailable | The executor sidecar is down or unresponsive. Check `systemctl status zombied-executor` and restart if needed. | +| `UZ-EXEC-004` | Agent runtime crash | The agent process crashed unexpectedly. Check executor logs for panic traces. Retry the run. | +| `UZ-EXEC-005` | Gate loop exhausted | All gate retries failed. The agent could not self-repair the lint/test/build failures. Review the gate logs in the run output. | +| `UZ-EXEC-006` | Branch push failed | Failed to push the implementation branch to GitHub. Verify the GitHub App has `contents:write` permission on the repo. | +| `UZ-EXEC-007` | PR creation failed | Failed to open a pull request. Verify the GitHub App has `pull_requests:write` permission. Check for branch protection rules that may block. | +| `UZ-EXEC-008` | Stage timeout | The stage exceeded `RUN_TIMEOUT_MS`. Increase the timeout or simplify the spec. | +| `UZ-EXEC-009` | OOM killed | The agent exceeded `EXECUTOR_MEMORY_LIMIT_MB`. Increase the memory limit or reduce compilation parallelism. | +| `UZ-EXEC-010` | Network policy violation | The agent attempted a network connection that was blocked by policy. If the agent needs registry access, set `EXECUTOR_NETWORK_POLICY=registry_allowlist`. | +| `UZ-EXEC-011` | Filesystem policy violation | The agent attempted to access a path denied by Landlock. This usually indicates the agent tried to write outside the workspace. | +| `UZ-EXEC-012` | Spec validation failed | The spec failed server-side validation. Check for disallowed file paths or unsupported constructs. | +| `UZ-EXEC-013` | Workspace suspended | The workspace is paused or has exceeded its credit budget. Resume the workspace or add credits. | +| `UZ-EXEC-014` | Lease expired | The run's lease expired before completion. This usually means the worker crashed and the reconciler cleaned up the stale run. Retry the run. | + +## Credential errors + +Errors related to credential retrieval or validation. + +| Code | Meaning | Remediation | +|------|---------|-------------| +| `UZ-CRED-001` | GitHub App token request failed | The JWT signature was rejected by GitHub. Verify the GitHub App private key in your secret manager matches the installed app. Re-download the key from GitHub App settings if needed. | +| `UZ-CRED-002` | Anthropic API key invalid | The API key was rejected by Anthropic. Verify the key in your secret manager is current. Rotate it at console.anthropic.com if needed. | + +## Sandbox errors + +Errors related to sandbox setup or enforcement. + +| Code | Meaning | Remediation | +|------|---------|-------------| +| `UZ-SANDBOX-001` | Sandbox initialization failed | The executor could not set up the sandbox (Landlock, cgroups, or network namespace). Check kernel support with `zombied doctor worker`. On older kernels, upgrade or use `SANDBOX_BACKEND=host` for dev only. | diff --git a/operator/observability/metrics.mdx b/operator/observability/metrics.mdx new file mode 100644 index 0000000..2f2f3c8 --- /dev/null +++ b/operator/observability/metrics.mdx @@ -0,0 +1,74 @@ +--- +title: "Metrics reference" +description: "Prometheus metrics exposed by UseZombie." +--- + +## Overview + +All UseZombie components expose Prometheus metrics on the `/metrics` endpoint (default port 9091). Metrics are organized into three categories: session lifecycle, stage execution, and sandbox enforcement. + +## Session lifecycle + +Counters and gauges tracking the overall run pipeline. + +| Metric | Type | Description | +|--------|------|-------------| +| `sessions_created_total` | Counter | Total number of runs created. | +| `sessions_active` | Gauge | Number of runs currently in progress. | +| `failures_total` | Counter | Total number of runs that ended in a failed state. Labels: `reason`. | +| `cancellations_total` | Counter | Total number of runs cancelled by the user or system. | + +## Stage execution + +Metrics tracking individual stage execution within runs. + +| Metric | Type | Description | +|--------|------|-------------| +| `stages_started_total` | Counter | Total number of stages that began execution. | +| `stages_completed_total` | Counter | Total number of stages that completed successfully. | +| `stages_failed_total` | Counter | Total number of stages that failed. Labels: `error_code`. | +| `agent_tokens_total` | Counter | Total LLM tokens consumed across all stages. Labels: `direction` (`input`, `output`). | +| `agent_duration_seconds` | Histogram | Wall-clock duration of agent execution per stage. Buckets: 10s, 30s, 60s, 120s, 300s, 600s. | + +## Sandbox enforcement + +Metrics tracking sandbox policy enforcement and resource limit events. + +| Metric | Type | Description | +|--------|------|-------------| +| `oom_kills_total` | Counter | Total number of agent executions killed by OOM (cgroups memory limit exceeded). | +| `timeout_kills_total` | Counter | Total number of agent executions killed by timeout. | +| `landlock_denials_total` | Counter | Total number of filesystem access attempts denied by Landlock. | +| `resource_kills_total` | Counter | Total number of agent executions killed for any resource violation. | +| `lease_expired_total` | Counter | Total number of runs where the lease expired before completion. | +| `cpu_throttled_ms_total` | Counter | Cumulative milliseconds of CPU throttling applied by cgroups. | +| `memory_peak_bytes` | Gauge | Peak memory usage of the most recent agent execution. | + +## Labels + +Common labels applied across metrics: + +| Label | Description | Applied to | +|-------|-------------|------------| +| `reason` | Failure reason category | `failures_total` | +| `error_code` | UZ-* error code | `stages_failed_total` | +| `direction` | Token direction (`input` or `output`) | `agent_tokens_total` | +| `workspace_id` | Workspace identifier | All metrics when cardinality allows | + +## Grafana dashboard queries + +Example PromQL queries for common operational views: + +```promql +# Run success rate (last 1 hour) +1 - (rate(failures_total[1h]) / rate(sessions_created_total[1h])) + +# P95 stage duration +histogram_quantile(0.95, rate(agent_duration_seconds_bucket[5m])) + +# OOM kill rate +rate(oom_kills_total[5m]) + +# Active runs +sessions_active +``` diff --git a/operator/observability/overview.mdx b/operator/observability/overview.mdx new file mode 100644 index 0000000..90ea021 --- /dev/null +++ b/operator/observability/overview.mdx @@ -0,0 +1,71 @@ +--- +title: "Observability overview" +description: "Three-layer observability: Grafana, PostHog, Langfuse." +--- + +## Overview + +UseZombie observability is organized into three layers, each serving a different audience and concern. + +```mermaid +graph LR + subgraph Infra Layer + Prometheus[Prometheus metrics] + Loki[Structured logs] + Tempo[Distributed traces] + end + subgraph Product Layer + PostHog[PostHog analytics] + end + subgraph AI Layer + Langfuse[Langfuse traces] + end + Prometheus --> Grafana[Grafana Cloud] + Loki --> Grafana + Tempo --> Grafana + PostHog --> PH[PostHog Cloud] + Langfuse --> LF[Langfuse Cloud] +``` + +## Infrastructure layer — Grafana Cloud + +The infrastructure layer covers system health, resource usage, and operational metrics. It is the primary tool for operators monitoring the platform. + +- **Prometheus metrics** — Counters, gauges, and histograms for session lifecycle, stage execution, sandbox enforcement, and system resources. Scraped from the `/metrics` endpoint on port 9091. +- **Structured logs** — JSON-formatted logs shipped to Loki. Every log line includes correlation fields for filtering and tracing. +- **Distributed traces** — OpenTelemetry traces shipped to Tempo. Each run generates a trace spanning API request, queue claim, stage execution, and PR creation. + +## Product layer — PostHog + +The product layer tracks user-facing events and feature adoption. It answers questions like "how many specs were submitted this week" and "what is the P95 time-to-PR." + +- Events are emitted from the API server and the CLI. +- No PII is stored — events reference workspace IDs and anonymized user IDs. +- Used for product decisions, funnel analysis, and feature flags. + +## AI layer — Langfuse + +The AI layer traces agent behavior within each run. It answers questions like "how many tokens did the agent use" and "where did the agent spend the most time." + +- Each stage execution generates a Langfuse trace with prompt/completion pairs. +- Token usage, latency, and model selection are recorded. +- Used for cost analysis, prompt optimization, and agent quality scoring. + +## Correlation fields + +All three layers share a common set of correlation fields that enable cross-layer investigation: + +| Field | Description | Present in | +|-------|-------------|------------| +| `trace_id` | OpenTelemetry trace ID | Grafana, Langfuse | +| `run_id` | UseZombie run identifier | All three layers | +| `workspace_id` | Workspace scoping | All three layers | +| `stage_id` | Individual stage within a run | Grafana, Langfuse | +| `executor_id` | Executor instance that handled the stage | Grafana | + +To investigate a failed run across all layers: + +1. Start with the `run_id` from the API response or CLI output. +2. Search Grafana logs and traces by `run_id`. +3. Search Langfuse traces by `run_id` to see agent behavior. +4. Search PostHog by `run_id` to see the user-facing event timeline. diff --git a/operator/operations/doctor.mdx b/operator/operations/doctor.mdx new file mode 100644 index 0000000..3ef2e93 --- /dev/null +++ b/operator/operations/doctor.mdx @@ -0,0 +1,83 @@ +--- +title: "Doctor" +description: "Health check and diagnostics." +--- + +## Overview + +The `doctor` command runs a suite of health checks to verify that a UseZombie component is correctly configured and can reach its dependencies. There are two modes: API mode and worker mode. + +## API mode + +```bash +zombied doctor +``` + +Checks run in API mode: + +| Check | What it verifies | If it fails | +|-------|-----------------|-------------| +| PostgreSQL connection | Can connect and run a query against the database | Verify `DATABASE_URL` is correct. Check that PostgreSQL is running and reachable from this host. | +| Redis connection | Can connect and ping Redis | Verify `REDIS_URL` is correct. Check that Redis is running and reachable. | +| Clerk configuration | `CLERK_SECRET_KEY` is set and non-empty | Set the variable in your `.env` or environment. Obtain the key from the Clerk dashboard. | +| Required env vars | All required API variables are present | See [Environment variables](/operator/configuration/environment) for the full list. | +| Port availability | API port (default 3000) is not already bound | Stop the conflicting process or change `PORT`. | +| Metrics port availability | Metrics port (default 9091) is not already bound | Stop the conflicting process or change `METRICS_PORT`. | + +## Worker mode + +```bash +zombied doctor worker +``` + +Runs all API-mode checks plus additional worker-specific checks: + +| Check | What it verifies | If it fails | +|-------|-----------------|-------------| +| Executor socket | `/run/zombie/executor.sock` exists and is connectable | Verify `zombied-executor.service` is running: `systemctl status zombied-executor`. | +| Executor health | Executor responds to `healthCheck` RPC | Restart the executor: `systemctl restart zombied-executor`. Check executor logs for startup errors. | +| Sandbox backend | `SANDBOX_BACKEND` is a recognized value (`host` or `bubblewrap`) | Correct the value in `.env`. | +| GitHub App key | GitHub App private key is accessible | Verify the key file path or secret manager reference is valid. Re-download from GitHub App settings if needed. | +| Landlock support | Kernel supports Landlock (Linux 5.13+) | Upgrade the kernel or set `SANDBOX_BACKEND=host` for dev use. | +| cgroups v2 | cgroups v2 is mounted and writable | Verify `/sys/fs/cgroup` is cgroups v2. Some older systems need `systemd.unified_cgroup_hierarchy=1` on the kernel command line. | + +## Example output + +``` +$ zombied doctor worker +[OK] PostgreSQL connection +[OK] Redis connection +[OK] Clerk configuration +[OK] Required env vars (6/6) +[OK] Executor socket (/run/zombie/executor.sock) +[OK] Executor health (responded in 2ms) +[OK] Sandbox backend (bubblewrap) +[OK] GitHub App key +[OK] Landlock support (ABI v3) +[OK] cgroups v2 + +All checks passed. +``` + +``` +$ zombied doctor worker +[OK] PostgreSQL connection +[OK] Redis connection +[FAIL] Clerk configuration: CLERK_SECRET_KEY is empty +[OK] Required env vars (5/6 — missing: CLERK_SECRET_KEY) +[OK] Executor socket (/run/zombie/executor.sock) +[FAIL] Executor health: connection refused +[OK] Sandbox backend (bubblewrap) +[OK] GitHub App key +[OK] Landlock support (ABI v3) +[OK] cgroups v2 + +2 checks failed. Fix the issues above and re-run. +``` + +## Exit codes + +| Code | Meaning | +|------|---------| +| `0` | All checks passed | +| `1` | One or more checks failed | diff --git a/operator/operations/fleet.mdx b/operator/operations/fleet.mdx new file mode 100644 index 0000000..b0deab6 --- /dev/null +++ b/operator/operations/fleet.mdx @@ -0,0 +1,104 @@ +--- +title: "Fleet management" +description: "Worker fleet drain, rollover, and canary deploy." +--- + +## Overview + +Worker fleet management covers how to safely upgrade, scale, and maintain the pool of worker machines. The key constraint is that workers may have in-flight runs that must complete before the worker is shut down. + +## Drain + +Before upgrading a worker, drain it so no new work is claimed and in-flight runs complete gracefully. + +```bash +# Signal the worker to stop claiming new runs +systemctl kill -s SIGUSR1 zombied-worker + +# Wait for in-flight runs to finish (check via logs or metrics) +journalctl -u zombied-worker -f +# Look for: "drain complete, 0 active runs" + +# Stop the worker +systemctl stop zombied-worker +``` + +The worker responds to `SIGUSR1` by entering drain mode: +- Stops claiming new runs from Redis. +- Continues executing in-flight runs to completion. +- Exits cleanly when all runs finish. + +If a run is stuck, the `RUN_TIMEOUT_MS` limit will eventually force it to terminate. + +## Rolling restart + +To upgrade the entire fleet without downtime: + +1. Drain worker A (send `SIGUSR1`, wait for active runs to finish). +2. Stop worker A (`systemctl stop zombied-worker`). +3. Deploy new binaries to worker A. +4. Restart executor on worker A (`systemctl restart zombied-executor`). +5. Start worker A (`systemctl start zombied-worker`). +6. Verify worker A is healthy (`zombied doctor worker`). +7. Repeat for worker B, C, etc. + +Always restart the executor **before** the worker. The worker has a hard dependency on the executor (`Requires=zombied-executor.service`). + +## Canary deploy + +For high-risk upgrades, deploy to a single worker first and verify before rolling out to the fleet. + +**Step 1 — Deploy canary** + +Pick one worker and deploy the new version: + +```bash +# On the canary worker +cd /opt/zombie +./deploy/deploy.sh +``` + +**Step 2 — Verify canary** + +Run diagnostics and monitor the first few runs: + +```bash +zombied doctor worker +``` + +Watch for: +- All doctor checks pass. +- Runs complete successfully (check `sessions_created_total` and `failures_total` metrics). +- No new error codes in logs. +- Executor sandbox enforcement is working (check `oom_kills_total`, `landlock_denials_total`). + +Let the canary process at least 3-5 runs before proceeding. + +**Step 3 — Roll out** + +If the canary is healthy, proceed with rolling restart across the remaining workers. + +## Systemd ordering + +The executor must always start before the worker. This is enforced by the systemd unit dependency: + +``` +zombied-worker.service + Requires=zombied-executor.service + After=zombied-executor.service +``` + +If the executor crashes, systemd will also stop the worker. When the executor restarts, the worker must be manually started (or configure `PartOf=` for automatic restart propagation). + +## Scaling + +To add a new worker to the fleet: + +1. Provision a bare-metal machine on OVHCloud. +2. Join it to the Tailscale network. +3. Deploy the standard directory structure to `/opt/zombie/`. +4. Configure `.env` from your secret manager. +5. Enable and start both systemd services. +6. Verify with `zombied doctor worker`. + +The new worker will automatically start claiming runs from Redis. No registration step is needed — Redis consumer groups handle worker discovery. diff --git a/operator/operations/reconcile.mdx b/operator/operations/reconcile.mdx new file mode 100644 index 0000000..f052060 --- /dev/null +++ b/operator/operations/reconcile.mdx @@ -0,0 +1,74 @@ +--- +title: "Reconciliation" +description: "Dead-letter reconciliation for stale runs." +--- + +## Overview + +`zombied reconcile` processes stale outbox rows and recovers runs stuck in non-terminal states. It is designed to be run as a periodic cron job or scheduled task. + +## What it does + +The reconciler scans for: + +- **Stale outbox rows** — Transactional outbox entries that were written to PostgreSQL but never picked up or acknowledged. These can occur when a worker crashes between writing the outbox row and completing the downstream action (webhook delivery, status update). +- **Stuck runs** — Runs that remain in a non-terminal state (`QUEUED`, `RUNNING`) beyond the expected timeout window. These can occur when a worker crashes mid-execution and no other worker reclaims the work. + +For each stale item: + +1. If the run's timeout has elapsed, mark it as `FAILED` with error code `UZ-EXEC-014` (lease expired). +2. If the outbox row has a pending webhook delivery, retry the delivery. +3. Record the reconciliation action in the run's event log. + +## Usage + +```bash +# Run reconciliation once +zombied reconcile + +# Dry run — show what would be reconciled without making changes +zombied reconcile --dry-run +``` + +## Scheduling + +Run the reconciler on a regular schedule. Every 5 minutes is a reasonable default: + +```bash +# crontab entry +*/5 * * * * /opt/zombie/bin/zombied reconcile >> /var/log/zombie/reconcile.log 2>&1 +``` + +Or as a systemd timer: + +```ini +# zombied-reconcile.timer +[Unit] +Description=Run zombied reconciliation every 5 minutes + +[Timer] +OnCalendar=*:0/5 +Persistent=true + +[Install] +WantedBy=timers.target +``` + +```ini +# zombied-reconcile.service +[Unit] +Description=zombied reconciliation + +[Service] +Type=oneshot +EnvironmentFile=/opt/zombie/.env +ExecStart=/opt/zombie/bin/zombied reconcile +``` + +## Idempotency + +The reconciler is idempotent. Running it multiple times on the same stale data produces the same result. It uses database-level locks to prevent concurrent reconciliation from conflicting. + +## Monitoring + +Watch the `reconcile_runs_total` and `reconcile_errors_total` metrics to track reconciliation activity. A sustained increase in reconciled runs may indicate worker instability. diff --git a/operator/security/model.mdx b/operator/security/model.mdx new file mode 100644 index 0000000..1145d1b --- /dev/null +++ b/operator/security/model.mdx @@ -0,0 +1,74 @@ +--- +title: "Security model" +description: "Fail-closed execution with separate runtime boundaries." +--- + +## Overview + +UseZombie's security model is built on two principles: **fail-closed execution** and **separate runtime boundaries**. If the system cannot verify that a security prerequisite is met, it refuses to proceed. + +## Runtime boundaries + +The worker and executor are separate security boundaries, each with a distinct trust level and responsibility. + +### Worker boundary + +The worker is a **trusted** component. It handles: + +- Orchestration — claiming runs, managing lifecycle state. +- Credential management — signing GitHub App JWTs, passing API keys via RPC payload. +- Billing enforcement — checking workspace credits before starting a run. +- PR creation — pushing branches and opening pull requests on behalf of the user. + +The worker has access to all credentials and can make authenticated requests to GitHub, PostgreSQL, and Redis. + +### Executor boundary + +The executor is an **untrusted** boundary. It handles: + +- Agent code execution — running the NullClaw agent runtime. +- Sandbox policy enforcement — Landlock, cgroups, network deny. +- Resource metering — tracking memory, CPU, and token usage. + +The executor does **not** have access to credentials in its environment. The Anthropic API key is passed inside the `startStage` RPC payload and used only for agent API calls. The executor cannot reach the database, Redis, or GitHub directly. + +## Fail-closed behavior + +If any sandbox prerequisite is missing at startup, the executor refuses to start: + +- Landlock not supported by the kernel: executor exits with `UZ-SANDBOX-001`. +- cgroups v2 not available: executor exits with `UZ-SANDBOX-001`. +- Unix socket path not writable: executor exits with a diagnostic error. + +There is no degraded mode in production. Either the full sandbox is enforced, or the executor does not run. + +## Lease-based liveness + +Each run has a lease — a bounded time window during which the worker holds exclusive ownership. If the worker crashes or becomes unresponsive, the lease eventually expires, and the reconciler marks the run as failed (`UZ-EXEC-014`). + +Leases prevent zombie runs from consuming resources indefinitely. They also prevent two workers from accidentally working on the same run. + + +UseZombie v1 is **not mid-session durable**. If a worker crashes during a run, the partial work is lost. The run is marked as failed, and the user must retry. Mid-session checkpointing is planned for v2. + + +## Spec injection threat model + +Specs are user-authored markdown that the agent interprets. The attack surface is an adversarial spec that tries to escape the sandbox or exfiltrate data. + +### Defenses in v1 + +| Layer | Defense | +|-------|---------| +| Spec validation | Server-side validation rejects specs that reference files outside the repository root. Path traversal patterns (`../`, absolute paths) are blocked. | +| Gate loop | Compilation failures from injected code are caught by `make lint`, `make test`, `make build`. The gate loop treats any failure as a signal to self-repair, not to proceed. | +| Sandbox | Even if the agent writes malicious code, the sandbox prevents filesystem escape, network exfiltration, and privilege escalation. | +| Human review | All PRs in v1 require human review before merge. The agent cannot merge its own work. | + +### Planned for v2 + +| Layer | Defense | +|-------|---------| +| Static analysis gates | Semgrep rules and gitleaks scanning on every PR before it is opened. | +| Diff audit | Automated review of the diff for suspicious patterns (credential access, network calls, obfuscation). | +| Sandbox hardening | Firecracker VM isolation replacing Landlock+cgroups for stronger boundary enforcement. | diff --git a/operator/security/rbac.mdx b/operator/security/rbac.mdx new file mode 100644 index 0000000..6f110d3 --- /dev/null +++ b/operator/security/rbac.mdx @@ -0,0 +1,101 @@ +--- +title: "Roles and access control" +description: "Role-based access control for workspaces." +--- + +## Overview + +UseZombie enforces role-based access control (RBAC) on all API operations. Roles are assigned per-workspace and checked server-side on every request. The client never makes authorization decisions. + +## Roles + +Three roles are defined, each a superset of the previous: + +### user + +The default role for workspace members. Covers normal day-to-day operations. + +| Permission | Description | +|-----------|-------------| +| Submit specs | Create and submit specs for execution. | +| View runs | View run status, logs, scorecards, and PR output. | +| Manage workspaces | Add repositories, configure workspace settings. | +| View billing | View current plan, credit balance, and usage history. | + +### operator + +Elevated role for platform operators and workspace administrators. + +| Permission | Description | +|-----------|-------------| +| All `user` permissions | Everything a user can do. | +| Harness management | Configure agent harnesses, source references, and compile constraints. | +| Skill-secret management | Manage workspace-scoped secrets available to agent skills. | +| Scoring views | Access detailed scoring breakdowns and agent performance analytics. | +| Workspace configuration | Advanced workspace settings (concurrency limits, timeout overrides). | + +### admin + +Full platform access for billing and administrative operations. + +| Permission | Description | +|-----------|-------------| +| All `operator` permissions | Everything an operator can do. | +| Billing lifecycle | Change plans, manage payment methods, adjust credit budgets. | +| API key management | Create, rotate, and revoke API-key-backed admin access. | +| Workspace suspension | Pause or suspend workspaces. | + +## Policy dimensions + +Every API endpoint is protected by two policy dimensions: + +### Minimum role + +Each endpoint requires a minimum role. Requests from users with insufficient roles receive `403 Forbidden`. + +| Endpoint pattern | Minimum role | +|-----------------|--------------| +| `POST /runs`, `GET /runs/*` | `user` | +| `POST /specs/*`, `GET /specs/*` | `user` | +| `GET /workspaces/*`, `PUT /workspaces/*` | `user` | +| `PUT /harness/*`, `PUT /secrets/*` | `operator` | +| `GET /scoring/*` | `operator` | +| `POST /billing/*`, `DELETE /api-keys/*` | `admin` | +| `POST /workspaces/*/pause` | `admin` | + +### Credit policy + +Some endpoints additionally require that the workspace has sufficient credits: + +| Credit policy | Meaning | Applied to | +|--------------|---------|------------| +| `none` | No credit check — endpoint is always accessible. | Read operations, workspace management, billing views. | +| `execution_required` | Workspace must have a positive credit balance or be on a plan with included credits. | `POST /runs` — creating a new run. | + +A request that passes the role check but fails the credit check receives `402 Payment Required`. + +## Enforcement + +Authorization is enforced entirely server-side in the API middleware. The flow: + +1. Extract the Clerk JWT from the `Authorization` header. +2. Verify the JWT signature and expiration. +3. Look up the user's role for the target workspace in PostgreSQL. +4. Check the endpoint's minimum role requirement. +5. If the endpoint has a credit policy, check the workspace's credit balance. +6. Allow or deny the request. + + +RBAC enforcement is covered by live HTTP integration tests that verify every role/endpoint combination. These tests run on every CI build. + + +## Role assignment + +Roles are assigned through the API: + +```bash +# Assign operator role to a user in a workspace +zombiectl workspace role set --workspace --user --role operator +``` + +The first user to create a workspace is automatically assigned the `admin` role. Additional users are assigned the `user` role by default when they join. diff --git a/operator/security/sandbox.mdx b/operator/security/sandbox.mdx new file mode 100644 index 0000000..0cbfc12 --- /dev/null +++ b/operator/security/sandbox.mdx @@ -0,0 +1,91 @@ +--- +title: "Sandbox enforcement" +description: "Four isolation layers on every agent execution." +--- + +## Overview + +Every agent execution in UseZombie runs under four isolation layers. These layers are applied by the executor sidecar before the agent begins work and are enforced for the entire duration of the execution. + +```mermaid +sequenceDiagram + participant W as Worker + participant E as Executor + W->>E: startStage(payload) + E->>E: 1. Create cgroup scope + E->>E: 2. Apply Landlock policy + E->>E: 3. Apply network deny + E->>E: 4. NullClaw agent executes + E->>E: 5. Check OOM events + E->>E: 6. Record memory peak + E->>W: ExecutionResult +``` + +## Layer 1: Filesystem isolation (Landlock) + +Landlock is a Linux security module (available since kernel 5.13) that restricts filesystem access at the process level without requiring root privileges. + +### Filesystem policy table + +| Path | Access | Purpose | +|------|--------|---------| +| Workspace directory (`/tmp/zombie/runs//`) | Read-write | The cloned repository where the agent implements changes. | +| `/usr/bin`, `/usr/lib`, `/lib` | Read-only | System binaries and libraries needed for compilation toolchains. | +| `/usr/local/bin` | Read-only | Locally installed tools (compilers, interpreters). | +| `/etc/ssl/certs` | Read-only | TLS certificates for registry access (when allowlisted). | +| `/tmp` (private namespace) | Read-write | Agent temporary files. Isolated via `PrivateTmp`. | +| Everything else | **Denied** | No access to host config, other workspaces, credentials, or system state. | + +If the agent attempts to access a denied path, the system call returns `EACCES`. The denial is logged and increments the `landlock_denials_total` metric. + +## Layer 2: Resource limits (cgroups v2) + +Each agent execution runs in its own cgroups v2 scope with memory and CPU limits. + +### Memory + +- Default limit: 512 MB (`EXECUTOR_MEMORY_LIMIT_MB`). +- Enforcement: kernel OOM killer terminates the process if the limit is exceeded. +- Detection: executor reads cgroup OOM events after execution completes. +- Error code: `UZ-EXEC-009`. + +### CPU + +- Default limit: 100% of one core (`EXECUTOR_CPU_LIMIT_PERCENT`). +- Enforcement: cgroups CPU bandwidth control throttles the process. +- Detection: executor reads `cpu.stat` for throttled time. +- Metric: `cpu_throttled_ms_total`. + +## Layer 3: Network isolation + +Network access is denied by default using a dedicated network namespace with no routes. + +| Policy | Behavior | +|--------|----------| +| `deny_all` (default) | No outbound connections. All `connect()` calls fail with `ENETUNREACH`. | +| `registry_allowlist` | Outbound connections permitted only to allowlisted registry hosts (npm, PyPI, crates.io, Go proxy). All other destinations denied. | + +The network policy is configured via `EXECUTOR_NETWORK_POLICY`. See [Sandbox configuration](/operator/configuration/sandbox) for the allowlist details. + +## Layer 4: Process isolation (systemd hardening) + +The executor systemd service applies additional process-level restrictions: + +| Directive | Effect | +|-----------|--------| +| `PrivateTmp=true` | `/tmp` is a private mount, not shared with other services. | +| `ProtectSystem=strict` | The entire filesystem is read-only except explicitly allowed paths. | +| `NoNewPrivileges=true` | The process cannot gain new privileges via `setuid`, `setgid`, or capabilities. | + +## Failure classification + +When an agent execution fails due to sandbox enforcement, the failure is classified and recorded: + +| Failure class | Error code | Metric | Description | +|---------------|------------|--------|-------------| +| OOM kill | `UZ-EXEC-009` | `oom_kills_total` | Memory limit exceeded, process killed by kernel OOM. | +| Timeout | `UZ-EXEC-008` | `timeout_kills_total` | Execution exceeded `RUN_TIMEOUT_MS`. | +| Filesystem denial | `UZ-EXEC-011` | `landlock_denials_total` | Agent tried to access a denied path. | +| Network denial | `UZ-EXEC-010` | — | Agent tried to make a blocked network connection. | +| Resource kill | — | `resource_kills_total` | Aggregate: any resource-based kill (OOM, timeout, CPU). | +| Lease expired | `UZ-EXEC-014` | `lease_expired_total` | Worker lost the run lease before completion. | diff --git a/quickstart.mdx b/quickstart.mdx index c711458..9b3c5e9 100644 --- a/quickstart.mdx +++ b/quickstart.mdx @@ -1,80 +1,108 @@ --- -title: "Quickstart" -description: "Start building awesome documentation in minutes" +title: Quickstart +description: Submit your first spec and get a validated PR in under 5 minutes. --- -## Get started in three steps + + + If you don't have Node.js installed yet, [download it here](https://nodejs.org). + -Get your documentation site running locally and make your first customization. + + Run the following command in your terminal: -### Step 1: Set up your local environment + ```bash + npm install -g @usezombie/zombiectl + ``` - - - During the onboarding process, you created a GitHub repository with your docs content if you didn't already have one. You can find a link to this repository in your [dashboard](https://dashboard.mintlify.com). - - To clone the repository locally so that you can make and preview changes to your docs, follow the [Cloning a repository](https://docs.github.com/en/repositories/creating-and-managing-repositories/cloning-a-repository) guide in the GitHub docs. - - - 1. Install the Mintlify CLI: `npm i -g mint` - 2. Navigate to your docs directory and run: `mint dev` - 3. Open `http://localhost:3000` to see your docs live! - - Your preview updates automatically as you edit files. - - + Or register as an agent skill: -### Step 2: Deploy your changes + ```bash + npx skills add @usezombie/zombiectl + ``` - - - Install the Mintlify GitHub app from your [dashboard](https://dashboard.mintlify.com/settings/organization/github-app). - - Our GitHub app automatically deploys your changes to your docs site, so you don't need to manage deployments yourself. - - - For a first change, let's update the name and colors of your docs site. + Verify the installation: - 1. Open `docs.json` in your editor. - 2. Change the `"name"` field to your project name. - 3. Update the `"colors"` to match your brand. - 4. Save and see your changes instantly at `http://localhost:3000`. + ```bash + zombiectl --version + ``` + - Try changing the primary color to see an immediate difference! - - + + ```bash + zombiectl login + ``` -### Step 3: Go live + This opens your browser for Clerk authentication. Once complete, your session token is stored locally. + - - 1. Commit and push your changes. - 2. Your docs will update and be live in moments! - + + ```bash + zombiectl workspace add https://github.com/your-org/your-repo + ``` -## Next steps + This installs the UseZombie GitHub App on your repository and creates a workspace. + -Now that you have your docs running, explore these key features: + + Create a markdown file describing what you want built: - + ```markdown docs/spec/add-health-endpoint.md + # Add health check endpoint - - Learn MDX syntax and start writing your documentation. - + ## Goal - - Make your docs match your brand perfectly. - + Add a `GET /healthz` endpoint that returns `{ "status": "ok" }` with a 200 status code. - - Include syntax-highlighted code blocks. - + ## Files to modify - - Auto-generate API docs from OpenAPI specs. - + - `src/http/routes.zig` — add route handler + - `src/http/handlers.zig` — implement handler function - + ## Acceptance criteria + + - Endpoint returns 200 with JSON body `{ "status": "ok" }` + - Response content-type is `application/json` + - Endpoint requires no authentication + ``` + + + + ```bash + zombiectl run --spec docs/spec/add-health-endpoint.md + ``` + + UseZombie validates the spec, enqueues the run, and assigns an agent. + + + + ```bash + zombiectl runs list + zombiectl run status + ``` - - **Need help?** See our [full documentation](https://mintlify.com/docs) or join our [community](https://mintlify.com/community). - + The agent implements your spec, runs `make lint`, `make test`, and `make build` with self-repair, then opens a PR with a scorecard. + + + + Open the PR on GitHub. You will find: + + - An agent-generated explanation of what changed + - A scorecard comment with gate results, repair loop count, wall time, and token consumption + - The full diff ready for review + + + +## What's next? + + + + Understand the full spec-to-PR lifecycle. + + + Learn how to write effective specs for agents. + + + See how agents self-repair through lint, test, and build gates. + + diff --git a/runs/gate-loop.mdx b/runs/gate-loop.mdx new file mode 100644 index 0000000..2b082e4 --- /dev/null +++ b/runs/gate-loop.mdx @@ -0,0 +1,86 @@ +--- +title: "Gate loop" +description: "Self-repairing lint, test, and build validation." +--- + +Every run passes through a sequential gate loop that validates the agent's work. If a gate fails, the agent reads the error output, modifies the code, and retries — automatically. + +```mermaid +flowchart TD + A[Agent implements spec] --> B{make lint} + B -->|Pass| C{make test} + B -->|Fail| D[Feed stderr to agent] + D --> E[Agent self-repairs] + E --> B + C -->|Pass| F{make build} + C -->|Fail| G[Feed stderr to agent] + G --> H[Agent self-repairs] + H --> C + F -->|Pass| I[All gates pass - open PR] + F -->|Fail| J[Feed stderr to agent] + J --> K[Agent self-repairs] + K --> F + B -->|Exhausted| L[Run FAILED] + C -->|Exhausted| L + F -->|Exhausted| L +``` + +## Three sequential gates + +The gate loop runs three gates in strict order. A gate must pass before the next one starts. + +| Gate | Make target | Purpose | +| --- | --- | --- | +| Lint | `make lint` | Static analysis, formatting, type checks | +| Test | `make test` | Unit tests | +| Build | `make build` | Production compilation or bundling | + +These are your repo's own Make targets. UseZombie does not impose its own linting rules or test framework — it runs whatever your project already uses. + +## Self-repair cycle + +When a gate fails: + +1. The executor captures `stdout` and `stderr` from the failing Make target. +2. The captured output is fed back to the agent as a new conversation turn. +3. The agent reads the error, modifies the code, and the gate is retried. + +This happens automatically. No human intervention is needed unless repair loops are exhausted. + +## Repair loop limits + +Each gate has a configurable maximum number of repair attempts. The default is **3 loops per gate**. + +| Scenario | Behavior | +| --- | --- | +| Gate passes on first attempt | Move to next gate | +| Gate fails, agent repairs, retry passes | Move to next gate | +| Gate fails 3 times | Run marked `FAILED` | + +You can override the default per agent profile. Higher limits give the agent more chances but consume more tokens. + +## Gate exhaustion + +If all repair loops for any gate are exhausted, the run is marked `FAILED` with a structured gate failure record containing: + +- Which gate failed (`lint`, `test`, or `build`) +- The number of repair attempts made +- The final `stderr`/`stdout` capture from the last attempt + +Use `zombiectl run status ` to inspect the failure details. + +## How gates are executed + +Gate tools are **NullClaw tool definitions** passed to the agent via executor RPC. The executor is agent-agnostic — it does not know or care which LLM is behind the agent. It sends a tool call, gets a result, and feeds it back. + +This means gate execution works identically regardless of the underlying model. + +## Loop recording + +Each loop iteration is recorded with: + +- **Gate name** — which gate was running +- **Outcome** — `PASS` or `FAIL` +- **Captured output** — the full `stdout`/`stderr` from the Make target + +These records are available for replay and debugging. They form the backbone of the run's audit trail. diff --git a/runs/pr-output.mdx b/runs/pr-output.mdx new file mode 100644 index 0000000..2753fd3 --- /dev/null +++ b/runs/pr-output.mdx @@ -0,0 +1,80 @@ +--- +title: "PR output" +description: "What the generated pull request contains." +--- + +When a run completes successfully, UseZombie opens a pull request on the connected GitHub repository. Here is what that PR looks like and how it is constructed. + +## Branch naming + +Every run gets a dedicated branch: + +``` +zombie// +``` + +For example: + +``` +zombie/01JQ7K/add-healthz-endpoint +``` + +The branch is created from the base branch HEAD at the time the run starts. The exact base commit SHA is recorded on the run row for traceability. + +## Isolated worktree + +Each run executes in an **isolated git worktree** checked out from the base branch HEAD. This means: + +- Runs cannot interfere with each other, even when running in parallel. +- The worktree is cleaned up automatically after the run completes. +- No leftover branches or temporary files persist on the worker. + +## PR body + +The PR body contains an **agent-generated plain-English explanation** of what changed and why. This is not a diff summary — the agent writes a narrative description of the implementation decisions it made, based on the spec it was given. + +Example: + +```markdown +## What this PR does + +Adds a `GET /healthz` endpoint that returns the server status and package +version. The version is read from `package.json` at startup and cached. + +A unit test covers the happy path — verifying the 200 status code and +JSON body shape. +``` + +## Scorecard comment + +A separate PR comment is posted with the run's scorecard: + +| Field | Value | +| --- | --- | +| Score | 87 (Gold) | +| Completion | 100 | +| Error rate | 85 | +| Latency | 72 | +| Resource efficiency | 50 | +| Wall time | 3m 12s | +| Token consumption | 14,200 | + +The comment also includes a gate results table: + +| Gate | Result | Loops | +| --- | --- | --- | +| lint | PASS | 1 | +| test | PASS | 2 | +| build | PASS | 1 | + +## Human review required + +In v1, all PRs require human review. There is no auto-merge. You review the PR like any other — approve, request changes, or close it. + + + Coming soon: the PR description will include a quality badge showing the agent's tier and score, for example "This PR was produced by a Gold-tier agent (87/100)." + + + + Score-gated auto-merge is planned for v2. When enabled, runs that exceed a configurable score threshold will be merged automatically. + diff --git a/runs/scorecard.mdx b/runs/scorecard.mdx new file mode 100644 index 0000000..803e4c0 --- /dev/null +++ b/runs/scorecard.mdx @@ -0,0 +1,94 @@ +--- +title: "Scorecard" +description: "Quality scoring for every agent run." +--- + +Every run produces a deterministic **0-100 quality score**. The scorecard measures how well the agent executed the spec, not just whether it finished. + +## Scoring axes + +The score is a weighted sum of four axes: + +| Axis | Weight | What it measures | +| --- | --- | --- | +| Completion | 40% | Did the agent finish the spec? | +| Error rate | 30% | Ratio of passing stages to total stages | +| Latency | 20% | Wall time compared to workspace rolling p50 baseline | +| Resource efficiency | 10% | Compute and memory usage (stubbed until Firecracker) | + +### Completion (40%) + +| Outcome | Score | +| --- | --- | +| `COMPLETED` — all gates pass | 100 | +| `BLOCKED` — partial progress with retries | 30 | +| `FAILED` — no usable output | 0 | + +### Error rate (30%) + +Calculated as the ratio of stages that passed on their first attempt to total stages executed. A run where lint and build pass immediately but test requires two repair loops scores lower than a clean sweep. + +### Latency (20%) + +Measured against the workspace's rolling p50 baseline. A run that finishes in half the typical time scores higher. A run that takes 3x the baseline scores near zero on this axis. + +The baseline is computed from the last 20 completed runs in the same workspace. + +### Resource efficiency (10%) + + + Resource efficiency scoring is stubbed at a flat 50/100 until Firecracker-based sandboxing is available. Once sandboxing ships, this axis will measure actual CPU and memory consumption. + + +## Tier labels + +Scores map to named tiers: + +| Tier | Score range | +| --- | --- | +| Unranked | No score available | +| Bronze | 0 - 39 | +| Silver | 40 - 69 | +| Gold | 70 - 89 | +| Elite | 90 - 100 | + +## Fail-safe design + +Scoring is **fail-safe**. It never blocks a run and always produces a score, even if an axis cannot be computed. If a data source is unavailable (e.g., no baseline for latency), that axis defaults to 50/100 rather than failing the run. + +## Analytics + +Every scored run emits a PostHog event: + +``` +agent.run.scored +``` + +The event payload includes the run ID, final score, per-axis breakdown, and tier label. + +## Storage and API access + +Scores persist in the `agent_run_scores` table and are queryable via the API: + +```bash +curl https://api.usezombie.com/v1/runs/run_01JQ7K.../score \ + -H "Authorization: Bearer $ZOMBIE_TOKEN" +``` + +```json +{ + "run_id": "run_01JQ7K...", + "score": 87, + "tier": "Gold", + "axes": { + "completion": 100, + "error_rate": 85, + "latency": 72, + "resource_efficiency": 50 + } +} +``` + +## Future: competitive scoring + +In a future release, multiple agents will be able to compete on the same spec. Each agent produces its own branch and scorecard. The highest-scoring agent's PR is the one that gets opened for human review. diff --git a/runs/submitting.mdx b/runs/submitting.mdx new file mode 100644 index 0000000..b193fc9 --- /dev/null +++ b/runs/submitting.mdx @@ -0,0 +1,109 @@ +--- +title: "Submitting runs" +description: "How to submit a spec and start a run." +--- + +## CLI submission + +The fastest way to start a run is `zombiectl run`: + +```bash +zombiectl run --spec +``` + +The command validates the spec locally, uploads it to the API, and returns a `run_id` immediately. + +``` +Run created: run_01JQ7K... +Status: QUEUED → RUNNING +``` + +### Watch mode (future) + + + Watch mode is planned but not yet available. When shipped, it will stream gate progress over SSE so you can follow the run in real time. + + +```bash +zombiectl run --spec --watch +``` + +## API submission + +You can also submit a run directly via the API: + +```bash +curl -X POST https://api.usezombie.com/v1/runs \ + -H "Authorization: Bearer $ZOMBIE_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "workspace_id": "ws_01JQ7K...", + "spec": "# Add health check endpoint\n\nAdd a GET /healthz endpoint..." + }' +``` + +The response includes the `run_id` and initial status: + +```json +{ + "run_id": "run_01JQ7K...", + "status": "QUEUED", + "created_at": "2026-03-29T12:00:00Z" +} +``` + +## What happens after submission + +Once a run is submitted: + +1. The API validates the spec content and workspace reference. +2. A run row is written to the database with status `QUEUED`. +3. The run is enqueued for the next available worker to pick up. +4. The worker transitions the run to `RUNNING` and begins the gate loop. + +## Run states + +Every run moves through a linear state machine: + +| State | Meaning | +| --- | --- | +| `QUEUED` | Run accepted, queued for execution | +| `RUNNING` | Worker picked up the run, agent is executing | +| `COMPLETED` | All gates passed, PR opened | +| `FAILED` | Gate loop exhausted or unrecoverable error | + +## Checking run status + +List your recent runs: + +```bash +zombiectl runs list +``` + +``` +ID STATUS SPEC CREATED +run_01JQ7K... COMPLETED add-healthz-endpoint 2m ago +run_01JQ8M... RUNNING fix-auth-redirect 30s ago +run_01JQ6P... FAILED refactor-db-layer 1h ago +``` + +Check a specific run: + +```bash +zombiectl run status +``` + +``` +Run: run_01JQ7K... +Status: COMPLETED +Spec: add-healthz-endpoint +Branch: zombie/01JQ7K/add-healthz-endpoint +PR: https://github.com/your-org/your-repo/pull/42 +Score: 87 (Gold) +Duration: 3m 12s + +Gates: + lint PASS (1 loop) + test PASS (2 loops) + build PASS (1 loop) +``` diff --git a/runs/troubleshooting.mdx b/runs/troubleshooting.mdx new file mode 100644 index 0000000..fb9c3c5 --- /dev/null +++ b/runs/troubleshooting.mdx @@ -0,0 +1,87 @@ +--- +title: "Troubleshooting runs" +description: "Common failure modes and how to resolve them." +--- + +When a run fails or behaves unexpectedly, start here. The most common issues and their fixes are listed below. + + + + **Symptom:** Run status is `FAILED` and the gate results show 3 failed loops for a single gate. + + **Cause:** The agent attempted to self-repair 3 times (the default limit) and could not produce code that passes the gate. + + **Fix:** + - Run `zombiectl run status ` and inspect the stderr from the final loop attempt. + - Check whether the failing Make target (`make lint`, `make test`, or `make build`) works locally on a clean checkout. + - Simplify the spec — smaller, more focused specs produce higher pass rates. + - If your repo's Make targets have environment-specific dependencies, ensure they work in a clean Linux container. + + + + **Symptom:** Run fails immediately with a validation error before any gates execute. + + **Cause:** The spec references files or paths that do not exist in the target repository, or the spec content is empty/malformed. + + **Fix:** + - Validate the spec locally before submitting: + + ```bash + zombiectl spec validate + ``` + + - Ensure all file paths referenced in the spec exist on the base branch HEAD. + - Check that the spec is valid markdown with at least a title and one requirement. + + + + **Symptom:** Run status is `FAILED` with a timeout error. No gate exhaustion — the run simply ran out of time. + + **Cause:** The run exceeded its wall time limit. This can happen with large specs, slow test suites, or complex build steps. + + **Fix:** + - Reduce the scope of the spec. Break large features into multiple smaller specs. + - Check whether your `make test` or `make build` targets are unusually slow. + - If you need more time, increase the budget in your agent profile settings. + + + + **Symptom:** Run fails with an out-of-memory error. The worker process was killed. + + **Cause:** The agent exceeded the memory limit (512 MB default). This typically happens with large file operations, massive dependency installs, or memory-intensive build steps. + + **Fix:** + - Avoid specs that require processing very large files. + - Check whether your build step has a known memory spike (e.g., TypeScript compilation of a very large project). + - Contact support if you consistently hit OOM on normal-sized repos. + + + + **Symptom:** Run submission is rejected with a billing error. No run is created. + + **Cause:** Your free plan $10 credit has been used up. + + **Fix:** + - Check your current balance in the [dashboard](https://app.usezombie.com/billing). + - Upgrade to the Scale plan for higher limits and per-run billing. + + + + **Symptom:** Run submission is rejected with a duplicate error. + + **Cause:** The same spec content, repository, and base commit SHA already has an active run (status `QUEUED` or `RUNNING`). + + **Fix:** + - Wait for the existing run to complete or fail. + - If you want to re-run the same spec, push a new commit to the base branch so the base commit SHA changes. + - Alternatively, modify the spec content (even a whitespace change) to make it unique. + + + +## Run replay (future) + + + Run replay is planned but not yet available. + + +When shipped, `zombiectl runs replay ` will produce a structured narrative of everything that happened during a run — one section per gate, one entry per loop iteration, with the full captured output at each step. diff --git a/snippets/snippet-intro.mdx b/snippets/snippet-intro.mdx deleted file mode 100644 index e20fbb6..0000000 --- a/snippets/snippet-intro.mdx +++ /dev/null @@ -1,4 +0,0 @@ -One of the core principles of software development is DRY (Don't Repeat -Yourself). This is a principle that applies to documentation as -well. If you find yourself repeating the same content in multiple places, you -should consider creating a custom snippet to keep your content in sync. diff --git a/specs/supported-formats.mdx b/specs/supported-formats.mdx new file mode 100644 index 0000000..56c0c09 --- /dev/null +++ b/specs/supported-formats.mdx @@ -0,0 +1,81 @@ +--- +title: "Supported formats" +description: "UseZombie accepts specs in any markdown format." +--- + + + Zero configuration. No GitHub Actions to configure. No CI YAML to write. Just specs in, PRs out. + + +UseZombie does not mandate a specific spec format. Any well-structured markdown file is a valid spec. The following formats are tested and supported. + +## UseZombie milestone specs + +The native spec format organizes work into a hierarchy: Milestones, Workstreams, Sections, and Dimensions. + +```markdown +# M4_002: Publish npm package + +**Prototype:** v1.0.0 +**Milestone:** M4 +**Workstream:** 002 +**Date:** Mar 29, 2026 +**Status:** PENDING +**Priority:** P0 — Critical path for CLI distribution +**Batch:** B1 — parallel execution group +**Depends on:** M4_001 + +--- + +## 1.0 Package configuration + +**Status:** PENDING + +Configure package.json, entry points, and build output for npm publish. + +**Dimensions:** +- 1.1 PENDING package.json has correct name, version, and bin field +- 1.2 PENDING Build output matches declared entry points +- 1.3 PENDING README included in published package + +--- + +## 2.0 Acceptance criteria + +**Status:** PENDING + +- [ ] 2.1 npm pack produces a valid tarball +- [ ] 2.2 npx zombiectl --version prints the correct version +- [ ] 2.3 Global install resolves the binary to PATH +``` + +Workstreams within the same batch can execute in parallel. Batches are sequential — B2 starts only after all B1 workstreams complete. + +## OpenSpec by Fission AI + +[OpenSpec](https://github.com/Fission-AI/OpenSpec) is an open-source spec framework that stores structured artifacts in git. Specs are organized as proposal, design, and task documents in markdown. + +UseZombie reads OpenSpec artifacts and uses them as agent context. No conversion step needed — submit the spec file directly. + +## gstack /autoplan + +gstack's `/autoplan` command produces structured plans through a CEO, engineering, and design review pipeline. The output is a markdown plan with sections, tasks, and acceptance criteria. + +Submit the generated plan markdown as a spec. UseZombie treats it like any other spec file. + +## Plain markdown + +No framework required. A plain markdown file describing what you want is a valid spec. + +```markdown +# Add dark mode toggle to the settings page + +Users should be able to switch between light and dark themes +from the settings page. The preference should persist in +localStorage and apply immediately without a page reload. + +The toggle should be a switch component matching the existing +design system in src/components/ui/Switch.tsx. +``` + +As long as the file describes what to build and provides enough context for an agent to act, it works. diff --git a/specs/validation.mdx b/specs/validation.mdx new file mode 100644 index 0000000..9e630a2 --- /dev/null +++ b/specs/validation.mdx @@ -0,0 +1,89 @@ +--- +title: "Spec validation" +description: "How UseZombie validates specs before execution." +--- + +## Validation before execution + +UseZombie validates every spec at submission time, before any agent tokens are spent. If validation fails, the run is rejected immediately with actionable error messages. + +## File reference checking + +When your spec mentions file paths like `src/routes/api/jobs.ts`, validation confirms that file exists in the repository at the specified base commit. + +``` +Error: File reference not found + + spec line 12: src/routes/api/jbos.ts + closest match: src/routes/api/jobs.ts + + Fix: correct the file path in your spec. +``` + +This catches typos and stale references before they waste agent time. + +## Structure validation + +Specs are checked for well-formed markdown sections. Validation looks for: + +- A top-level heading (the spec title) +- At least one section with descriptive content +- No empty sections (a heading with no content beneath it) + +``` +Error: Empty section + + spec line 24: ## Acceptance criteria + (no content follows this heading) + + Fix: add acceptance criteria or remove the empty section. +``` + +## Deduplication + +Every spec submission produces a composite key: + +``` +sha256(spec_markdown) + repo + base_commit_sha +``` + +This key determines uniqueness: + +| Scenario | Result | +| --- | --- | +| Same spec, same repo, same base commit | Returns the existing run ID | +| Same spec, same repo, different base commit | New run (code changed) | +| Modified spec, same repo, same base commit | New run (spec changed) | + +Deduplication prevents accidental re-runs when submitting the same spec multiple times. If you need to force a re-run on the same base, make any change to the spec content. + +## Local pre-flight validation + +Run validation locally before submitting: + +```bash +zombiectl spec validate path/to/spec.md +``` + +This runs the same checks that the server performs at submission time: file references, structure, and deduplication lookup. Fix issues locally before they hit the server. + + + Run `zombiectl spec validate` in CI as a pre-merge check to catch broken specs before they land on the default branch. + + +## Validation error reference + +| Error | Cause | Fix | +| --- | --- | --- | +| File reference not found | Spec mentions a path that does not exist in the repo | Correct the file path; check for typos | +| Empty section | A heading has no content beneath it | Add content or remove the heading | +| Missing title | No top-level heading found | Add a `# Title` as the first line | +| Duplicate run | Same spec + repo + base commit already submitted | Change the spec or rebase to a new base commit | + +## Semantic validation + + + Validation catches structural issues. Semantic correctness — does the spec make sense? Is it implementable? Are the acceptance criteria testable? — is a known hard problem being addressed in v2. + + +Structural validation ensures your spec is well-formed. Whether the spec describes something an agent can actually build is a separate, harder question. Today, that responsibility is on the spec author. Future versions will add semantic checks such as detecting contradictory acceptance criteria and flagging specs that reference APIs or libraries not present in the repository. diff --git a/specs/writing-specs.mdx b/specs/writing-specs.mdx new file mode 100644 index 0000000..7ccdada --- /dev/null +++ b/specs/writing-specs.mdx @@ -0,0 +1,79 @@ +--- +title: "Writing specs" +description: "How to write a spec that agents can implement." +--- + +## What is a spec? + +A spec is a markdown file that describes what you want built. It is the only input UseZombie needs to produce a validated pull request. No chat prompts, no ticket descriptions, no Slack threads. One file, checked into git, describing the work. + +## Example spec + +```markdown +# Add rate limiting to the /api/jobs endpoint + +## Goal + +Prevent abuse by adding token-bucket rate limiting to the jobs endpoint. +Limit unauthenticated requests to 20/min and authenticated requests to 100/min. + +## Files to modify + +- src/middleware/rateLimit.ts (new file) +- src/routes/api/jobs.ts (add middleware) +- src/config/defaults.ts (add rate limit constants) +- tests/middleware/rateLimit.test.ts (new file) + +## Acceptance criteria + +- Unauthenticated requests exceeding 20/min receive HTTP 429 +- Authenticated requests exceeding 100/min receive HTTP 429 +- Rate limit headers (X-RateLimit-Remaining, X-RateLimit-Reset) are present on every response +- Existing /api/jobs tests continue to pass +- New unit tests cover both authenticated and unauthenticated limits +``` + +## Tips for effective specs + +**Be specific about file paths.** Instead of "add a middleware," write `src/middleware/rateLimit.ts`. Explicit paths give the agent a clear target and improve context injection (see below). + +**Describe expected behavior, not implementation steps.** Write "unauthenticated requests exceeding 20/min receive HTTP 429" rather than "use an if-statement to check the counter." Agents are better at choosing implementation details when you define the outcome. + +**Include acceptance criteria.** These become the agent's verification targets. Each criterion is something the agent will attempt to prove before marking the run complete. + +**Reference existing patterns.** If your codebase already has a middleware pattern, mention it: "Follow the same pattern as `src/middleware/auth.ts`." This anchors the agent to your conventions. + +## Context injection + +UseZombie extracts file references from your spec markdown and matches them against the repository file tree. Every matching file is provided as context to the agent alongside the spec content. + +This is critical for accurate implementations. When the spec says `src/routes/api/jobs.ts`, the agent receives that file's full contents and can see the existing code structure, imports, and conventions before making changes. + +The more specific your file references, the better the agent's context window. Vague references like "the routes folder" provide no context. Explicit paths like `src/routes/api/jobs.ts` provide the exact file the agent needs. + + + Reference files that the agent should read but not modify, too. Mentioning `src/middleware/auth.ts` as a pattern reference injects it into context even if the spec does not ask the agent to change it. + + +## Generating a spec template + +```bash +zombiectl spec init +``` + + + `zombiectl spec init` is a future feature. Today, create specs as plain markdown files in your repository. + + +## Spec as the single source of truth + +Specs are versioned in git. There is no prompt archaeology, no chat history to reconstruct, no "what did we tell the AI last time?" Every run references a specific spec file at a specific commit. If the spec changes, the next run uses the new version. + +This means: + +- **Reviewable.** Specs go through pull request review like any other code. +- **Reproducible.** Re-running the same spec on the same base commit produces the same run. +- **Auditable.** Git history shows what was requested, when, and by whom. +- **Handoff-friendly.** A new team member reads the spec to understand what was built and why. + +No chat transcripts. No prompt files buried in `.ai/` directories. The spec is the contract between you and the agent. diff --git a/workspaces/managing.mdx b/workspaces/managing.mdx new file mode 100644 index 0000000..78864d0 --- /dev/null +++ b/workspaces/managing.mdx @@ -0,0 +1,55 @@ +--- +title: "Managing workspaces" +description: "Create, list, and remove workspaces." +--- + +## Add a workspace + +```bash +zombiectl workspace add +``` + +For example: + +```bash +zombiectl workspace add https://github.com/your-org/your-repo +``` + +This connects the GitHub repository as a workspace: + +1. If the UseZombie GitHub App is not yet installed on the organization, opens the installation flow in your browser. +2. Creates the workspace and runs an initial spec sync automatically. + +If the GitHub App is already installed on the target organization, the command skips the installation step and connects the repository directly. + +## List workspaces + +```bash +zombiectl workspace list +``` + +Displays all workspaces in your account with their status, connected repository, and plan tier. + +``` +ID REPO STATUS PLAN +ws_01J8XV9R2M acme/backend ACTIVE Scale +ws_01J8XVKA3M acme/frontend ACTIVE Hobby +ws_01J8XVKB7N acme/infra PAUSED Scale +``` + +## Remove a workspace + +```bash +zombiectl workspace remove +``` + +Removing a workspace: + +- Stops accepting new runs immediately. +- Allows in-flight runs to complete. +- Retains run history for 30 days. +- Uninstalls the GitHub App connection for that repository. + + + This action cannot be undone. You will need to re-add the workspace and reinstall the GitHub App to reconnect. + diff --git a/workspaces/overview.mdx b/workspaces/overview.mdx new file mode 100644 index 0000000..076addd --- /dev/null +++ b/workspaces/overview.mdx @@ -0,0 +1,32 @@ +--- +title: "Workspaces" +description: "What a workspace is and how it works." +--- + +A workspace binds a GitHub repository to UseZombie. It is the fundamental unit of organization, billing, and access control. + +## What a workspace contains + +Each workspace has its own: + +- **Repository connection** -- a 1:1 binding to a GitHub repo via the UseZombie GitHub App. +- **Entitlements** -- determined by the plan tier (Hobby or Scale). +- **Billing** -- credit balance, usage tracking, and budget controls are scoped per workspace. +- **Agent configuration** -- harness settings, execution profiles, and environment variables. +- **Isolation** -- runs in one workspace cannot access specs, artifacts, or credentials from another. + +## Plan limits + +| Feature | Hobby (Free) | Scale | +|---------|-------------|-------| +| Workspaces | 1 | Unlimited | +| Specs | Unlimited | Unlimited | +| Self-repair gate loop | Included | Included | +| Priority queue | -- | Included | + +## Workspace lifecycle + +1. **Create** -- connect a GitHub repo with `zombiectl workspace add`. +2. **Active** -- specs are synced, runs are accepted. +3. **Paused** -- new runs are rejected; in-flight runs complete normally. +4. **Removed** -- workspace is deleted. Run history is retained for 30 days.