Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 64 additions & 0 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
## Summary

<!-- Brief description of what this PR changes and why. -->

## Type of Change

<!-- Mark the relevant option(s) with an "x" -->

- [ ] Bug fix
- [ ] New feature
- [ ] Breaking change (contract or API)
- [ ] Documentation update
- [ ] Code refactoring
- [ ] Test addition or update
- [ ] Dev UX / tooling

## Related Issues

<!-- Link related issues here using #issue-number -->

Fixes #
Related to #

## Changes

<!-- List the main changes by area (control plane, runtime client, web UI, docs, tests). -->

-
-
-

## Test Plan

<!-- How the changes were validated locally. -->

- [ ] `cargo test --features serde`
- [ ] `cargo clippy --features serde --all-targets -- -D warnings`
- [ ] `cargo fmt --all -- --check`
- [ ] Web UI typecheck (`cd web/void-control-ux && npx tsc -b --noEmit`)
- [ ] Web UI build (`cd web/void-control-ux && npm run build`) — if UI changed
- [ ] Live contract gate against a running void-box daemon — if runtime client changed
- [ ] Manual execution run (swarm / supervision) — if orchestration behavior changed

### Commands run

```bash
# Paste the commands and their summarized output.
```

## Documentation

- [ ] Updated README.md / CLAUDE.md if user-facing behavior changed
- [ ] Updated examples/ if orchestration or runtime template behavior changed
- [ ] Updated spec/ if the contract changed
- [ ] Updated skills/void-control/SKILL.md if the operator workflow changed

## Compatibility

<!-- Does this PR require a specific void-box baseline? Contract version bump?
Breaking change in persisted run state? Call it out here. -->

## Notes for Reviewers

<!-- Anything reviewers should focus on, known follow-ups, or out-of-scope items. -->
14 changes: 7 additions & 7 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,12 @@
package-lock.json
package.json
node_modules
.claude
!.claude/
!.claude/skills/
!.claude/skills/void-control/
!.claude/skills/void-control/SKILL.md
# Claude Code local scheduler state (per-machine)
.claude/scheduled_tasks.lock
.idea
.local-utils
CLAUDE.local.md
.claude/*.local.*
# Local-only scratch files (*.local.md, *.local.yaml, etc.)
*.local.*
.worktrees

# Frontend build output and local env files
Expand All @@ -22,6 +19,9 @@ web/void-control-ux/.env.local
web/void-control-ux/.env.*.local
web/void-control-ux/tmp_*.mjs

# TypeScript incremental build cache (per-machine, regenerated by tsc -b)
**/*.tsbuildinfo

.github-release-notes.md

# Keep the UI lockfile tracked for CI/npm ci
Expand Down
24 changes: 23 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,29 @@ VOID_BOX_BASE_URL=http://127.0.0.1:43100 \
cargo test --features serde --test void_box_contract -- --ignored --nocapture
```

Optional policy fixture overrides:
## Environment variables

Control-plane / bridge:

- `VOID_BOX_BASE_URL` — void-box daemon endpoint (default:
`http://127.0.0.1:43100`).
- `VOID_CONTROL_LLM_PROVIDER` — optional global override that patches
`llm.provider` on every runtime template at launch. Set to
`claude-personal` to use OAuth from the macOS Keychain or
`~/.claude/.credentials.json` without editing tracked templates.
Per-candidate `variation.explicit[].overrides` still win.

Web UI:

- `VITE_VOID_BOX_BASE_URL` — daemon URL for the operator dashboard.
Leave unset during local dev so the Vite `/api` proxy is used;
void-box serves no CORS headers, so setting this sends the browser
straight into the CORS pit.
- `VITE_VOID_CONTROL_BASE_URL` — bridge URL for the operator dashboard
(e.g., `http://127.0.0.1:43210`). The bridge sets CORS, so this can
point at a direct origin.

Optional policy fixture overrides (used by contract tests):

- `VOID_BOX_TIMEOUT_SPEC_FILE`
- `VOID_BOX_PARALLEL_SPEC_FILE`
Expand Down
111 changes: 22 additions & 89 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,92 +2,25 @@

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Project Overview

void-control is the control-plane orchestration layer for the `void-box` runtime. It launches/manages runs, tracks run/stage/event lifecycle, and enforces runtime contract compatibility. The project has two main parts: a Rust library/CLI and a React operator dashboard.

## Build & Test Commands

```bash
# Core unit tests (no serde feature)
cargo test

# JSON compatibility + fixture-based tests
cargo test --features serde

# Mocked transport contract tests for void-box client
cargo test --features serde runtime::void_box::

# Live daemon contract tests (requires running void-box on port 43100)
VOID_BOX_BASE_URL=http://127.0.0.1:43100 \
cargo test --features serde --test void_box_contract -- --ignored --nocapture

# Run a single test
cargo test --features serde test_name_here

# Terminal console
cargo run --features serde --bin voidctl

# Bridge server (port 43210)
cargo run --features serde --bin voidctl -- serve
```

**Always validate both paths before PRs:** `cargo test` AND `cargo test --features serde`.

### Web UI (web/void-control-ux/)

```bash
cd web/void-control-ux
npm install
VITE_VOID_BOX_BASE_URL=http://127.0.0.1:43100 npm run dev # dev server on port 5174
npm run build # production build (tsc -b && vite build)
```

## Architecture

### Rust Crate (src/)

Single crate with two main modules, feature-gated with `serde`:

- **`contract/`** — Control-plane type definitions (no runtime dependencies)
- `api.rs` — Request/response types: `StartRequest`, `StopRequest`, `RuntimeInspection`, `ConvertedRunView`
- `state.rs` — `RunState` enum with strict lifecycle transitions: Pending→Starting→Running→{Succeeded|Failed|Canceled}
- `event.rs` — `EventEnvelope`, `EventType` enum, `EventSequenceTracker` (enforces monotonic seq ordering)
- `policy.rs` — `ExecutionPolicy` (max_parallel_microvms, stage_timeout, retry config)
- `error.rs` — `ContractError` with code, message, retryable flag
- `compat.rs` / `compat_json.rs` — Normalization from void-box raw format to canonical types

- **`runtime/`** — Client implementations (behind `serde` feature)
- `void_box.rs` — `VoidBoxRuntimeClient` (HTTP client to void-box daemon)
- `mock.rs` — `MockRuntime` for testing

- **`bin/`** — `voidctl` (console + bridge server), `normalize_fixture`

### Web UI (web/void-control-ux/src/)

React 18 + TypeScript dashboard:

- **State:** Zustand (`store/ui.ts`) for selection state; TanStack Query for server state with tiered polling (active runs 2.5s, terminal 5s, events 1.2s)
- **Key components:** `RunsList`, `RunGraph` (Sigma/Graphology DAG), `EventTimeline`, `NodeInspector`, `LaunchRunModal`
- **API layer:** `lib/api.ts` wraps daemon endpoints (`/v1/runs/*`) and bridge endpoint (`/v1/launch`)
- **Types:** `lib/types.ts` mirrors the Rust contract types

### API Surface

- Daemon (void-box): `/v1/runs`, `/v1/runs/{id}`, `/v1/runs/{id}/events`, `/v1/runs/{id}/stages`, `/v1/runs/{id}/telemetry`, `/v1/runs/{id}/cancel`
- Bridge (voidctl serve): `POST /v1/launch`

## Coding Conventions

- **Naming:** Use boundary-focused names from the spec: `Run`, `Stage`, `Attempt`, `Runtime`, `Controller`
- **Testing:** Keep contract tests in `#[cfg(test)]` blocks near conversion/runtime logic. Fixture-based tests require `--features serde`
- **Feature gating:** JSON serialization, HTTP client, and server code live behind the `serde` feature flag
- **Commits:** Imperative style, format `area: concise action` (e.g., `spec: clarify cancellation semantics`)
- **Specs:** Add new specs to `spec/` with version in filename (e.g., `*-v0.2.md`)

## Environment Variables

- `VOID_BOX_BASE_URL` — void-box daemon endpoint (default: `http://127.0.0.1:43100`)
- `VITE_VOID_BOX_BASE_URL` — daemon URL for web UI
- `VITE_VOID_CONTROL_BASE_URL` — bridge URL for web UI (e.g., `http://127.0.0.1:43210`)
- `VOID_BOX_TIMEOUT_SPEC_FILE`, `VOID_BOX_PARALLEL_SPEC_FILE`, `VOID_BOX_RETRY_SPEC_FILE`, `VOID_BOX_NO_POLICY_SPEC_FILE` — Optional spec file overrides for policy behavior tests
For general development guidelines — architecture, module map, local
commands, testing, environment variables, and commit/PR conventions —
see @AGENTS.md.

## Claude-specific guidance

- Before implementing non-trivial changes, propose a plan and explain
the tradeoffs. Wait for alignment before editing.
- Preserve the control-plane / runtime boundary described in @AGENTS.md.
Runtime-transport and VM-isolation concerns belong to `void-box`, not
here. When in doubt, prefer a normalization layer in `src/contract/`
over leaking runtime details into orchestration.
- Prefer LSP operations (`goToDefinition`, `findReferences`, `hover`)
over Grep/Glob for Rust code navigation. Fall back to Grep/Glob only
for comments, config files, and non-Rust code.
- For UI work in `web/void-control-ux`, use browser automation (MCP or
Playwright) for DOM, layout, resize, console, and network validation.
Screenshots are fallback only.
- Every test target is currently gated on `--features serde`, so
`cargo test --features serde` is the one validation path — plain
`cargo test` runs zero tests.
- Be terse: skip end-of-turn summaries.
37 changes: 33 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,17 +118,38 @@ What to look for:

### 1) Start `void-box` daemon

Linux:

```bash
cargo run --bin voidbox -- serve --listen 127.0.0.1:43100
```

macOS (Apple Silicon, Virtualization.framework):

```bash
# after building the guest kernel + rootfs per void-box's macOS guide
VOID_BOX_KERNEL=target/vmlinuz \
VOID_BOX_INITRAMFS=target/void-box-claude.cpio.gz \
cargo run --bin voidbox -- serve --listen 127.0.0.1:43100
```

macOS requires `VOID_BOX_KERNEL` and `VOID_BOX_INITRAMFS` pointing at the
pre-built guest artifacts. The initramfs filename on macOS is
`target/void-box-claude.cpio.gz` (not the Linux `void-box-rootfs.cpio.gz`).
Running through `cargo run` also applies `codesign` automatically — direct
binary invocation needs manual codesigning. See the `void-box` macOS guide
for full details.

### 2) Run `void-control` tests

```bash
cargo test
cargo test --features serde
```

All test targets are currently gated behind the `serde` feature, so
`cargo test` without it runs zero tests. Use `--features serde` as the
one validation path.

### 3) Run live daemon contract gate

```bash
Expand All @@ -141,9 +162,15 @@ cargo test --features serde --test void_box_contract -- --ignored --nocapture
```bash
cd web/void-control-ux
npm install
VITE_VOID_BOX_BASE_URL=http://127.0.0.1:43100 npm run dev
npm run dev
```

The dev server proxies `/api` to the daemon at `http://127.0.0.1:43100` (see
`vite.config.ts`), so the browser stays same-origin. `void-box` does not set
CORS headers, so do **not** set `VITE_VOID_BOX_BASE_URL` during local dev —
leave it unset to use the proxy. Only override it when the daemon is reachable
from a host that returns CORS (e.g., a reverse proxy in front of void-box).

### 5) Launch from YAML editor/upload (bridge)

Run bridge mode in another terminal:
Expand All @@ -156,11 +183,14 @@ Then start UI with bridge URL:

```bash
cd web/void-control-ux
VITE_VOID_BOX_BASE_URL=http://127.0.0.1:43100 \
VITE_VOID_CONTROL_BASE_URL=http://127.0.0.1:43210 \
npm run dev
```

The bridge serves CORS headers, so `VITE_VOID_CONTROL_BASE_URL` can point
directly at it. Continue to leave `VITE_VOID_BOX_BASE_URL` unset so the
Vite `/api` proxy is used for daemon calls.

### 6) Run the canonical live swarm test

Use the three-candidate swarm as the default validation path:
Expand Down Expand Up @@ -207,7 +237,6 @@ Rust validation:
```bash
cargo fmt --all -- --check
cargo clippy --all-targets --all-features -- -D warnings
cargo test
cargo test --features serde
RUSTDOCFLAGS="-D warnings" cargo doc --no-deps --all-features
```
Expand Down
Loading
Loading