feat: ship agent onboarding runtime

This commit is contained in:
-Puter
2026-08-04 01:12:17 +05:30
parent 48205a5e19
commit b02200b3dc
68 changed files with 10522 additions and 4156 deletions

View File

@@ -1,6 +1,6 @@
# Deployment Architecture Recommendation
> **Status:** Proposed — research completed 2026-07-31. This supersedes the shared-Convex-environment approach in `docs/deployment.md` once implemented.
> **Status:** Partially implemented — staging environment is live as of 2026-08-03. The public Vercel/Convex endpoints and VDS services are operational; the declarative Pulumi/Ansible/CI backlog remains.
>
> **Goal:** retain an instant local iteration loop while operating one stable public staging environment with reproducible, declarative infrastructure.
@@ -12,18 +12,18 @@ Use a deliberately split stack:
Fast iteration (private development) Stable staging (public)
──────────────────────────────────── ────────────────────────
Mac + Tailscale Vercel + Contabo VDS
Web / Flue / Rivet / runner Web SSR Agents VDS
Web / Flue + in-process registry / Rivet Web SSR Agents VDS
│ │ │
personal Convex dev deployment staging Convex deployment
```
| Concern | Iteration | Staging |
| --- | --- | --- |
| User-visible URL | Existing Tailscale URL on the Mac | `https://staging.<domain>` on Vercel |
| User-visible URL | Existing Tailscale URL on the Mac | `https://zopu-staging.vercel.app` on Vercel |
| Product backend | Personal Convex **dev** deployment | Dedicated long-lived Convex deployment |
| Web | Vite HMR on the Mac | Vercel React Router SSR |
| Agent worker | Local Flue, engine, and runner | Contabo VDS: Flue, Rivet Engine, AgentOS runner |
| Delivery | Run local `pnpm dev:tailscale` | CI applies IaC then updates immutable images |
| Agent worker | Local Flue + in-process AgentOS registry and Engine | Contabo VDS: one Flue/registry Node process + Rivet Engine |
| Delivery | Run local `pnpm dev:tailscale` | Locally built release bundle promoted on the VDS |
| State/credentials | Development-only | Independent staging secrets, OAuth app, and volumes |
**Do not create a second remote “fast iteration” stack.** It duplicates the slowest part of the loop—building and replacing container images—while the existing Mac/Tailscale stack already exercises the complete topology from a phone. Staging is the public link and integration gate; local development is the fast environment.
@@ -33,21 +33,21 @@ personal Convex dev deployment staging Convex deployment
The repository has three materially different runtime needs:
1. **Web** is a React Router SSR Node application. Its current production artifact is `react-router build` plus `react-router-serve`, and `apps/web/Dockerfile` already serves it on Node. Vercel supports React Router SSR and streaming, and is the appropriate managed host for this stateless application. [Vercel React Router guide](https://vercel.com/docs/frameworks/frontend/react-router)
2. **Convex** owns authentication, durable product data, workflows, and reactive client projections. It is not an application container to run on the VDS. The architecture explicitly requires clients to communicate only with Convex. (`docs/TECH.md`, §1.)
3. **Flue + Rivet Engine + AgentOS runner** are persistent worker processes. The runner creates Git worktrees, installs dependencies, and host-mounts those directories into AgentOS; they require a long-lived filesystem and must stay co-located with their workspace volume. They are not appropriate for Vercel or Cloudflare Workers. [Flue Node Docker deployment](https://flueframework.com/docs/ecosystem/deploy/docker) · [Rivet runtime modes](https://rivet.dev/docs/general/runtime-modes)
2. **Convex** owns authentication, durable product data, workflows, and reactive client projections. It is not an application container to run on the VDS. The architecture explicitly requires clients to communicate only with Convex.
3. **Flue + in-process AgentOS registry + Rivet Engine** are the worker topology. Flue owns the registry request handler in the same Node process; the Engine remains a separate private service with durable state. Their filesystem-backed workspaces require a long-lived volume, so they are not appropriate for Vercel or Cloudflare Workers. [Flue Node deployment](https://flueframework.com/docs/ecosystem/deploy/node) · [Rivet runtime modes](https://rivet.dev/docs/general/runtime-modes)
Cloudflare remains valuable for authoritative DNS, TLS/DDoS controls, and optionally a later public-edge layer. It is **not** the first frontend runtime choice: moving the current Node SSR artifact to Workers requires a Cloudflare-specific React Router/workerd build and `nodejs_compat`; it does not host the private worker stack. [Cloudflare React Router guide](https://developers.cloudflare.com/workers/framework-guides/web-apps/react-router/)
## Environment isolation — required, not optional
The present shared Convex deployment is incompatible with two simultaneously operating environments. `SITE_URL` is a single deployment-scoped Better Auth base/trusted origin and GitHub OAuth callback origin; switching it between hosts breaks the other host (`docs/deployment.md`, lines 1947).
The present shared Convex deployment is incompatible with two simultaneously operating environments. `SITE_URL` is a single deployment-scoped Better Auth base/trusted origin and GitHub OAuth callback origin; switching it between hosts breaks the other host.
Create these independent Convex deployments:
| Deployment | Type | Purpose |
| --- | --- | --- |
| `dev:<developer>` | Convex dev | Local loop only; each developer owns one |
| `staging` | Long-lived production-type deployment | Public integration/staging; never reused for local testing |
| `staging` (`joyous-cat-297`) | Long-lived production-type deployment | Public integration/staging; never reused for local testing |
| Branch previews | Ephemeral preview deployment | Optional later, only for frontend/backend changes that do not need OAuth |
Convex supports a named long-lived production-type deployment (`convex deployment create staging --type prod`), per-deployment environment variables, and branch preview deployments. [Convex multiple deployments](https://docs.convex.dev/production/multiple-deployments) · [Convex environment variables](https://docs.convex.dev/production/environment-variables)
@@ -55,61 +55,62 @@ Convex supports a named long-lived production-type deployment (`convex deploymen
Every environment gets its own:
- `SITE_URL`, exact browser origin;
- `FLUE_URL` / `AGENT_BACKEND_URL`, pointing at only that environments Flue worker;
- `FLUE_URL` / `AGENT_BACKEND_URL`, pointing at only that environments Flue worker (e.g. `https://agents.zopu.puter.wtf`);
- `FLUE_DB_TOKEN` shared only with that environments agent service;
- model and Git provider credentials;
- GitHub OAuth application/client credentials where GitHub login is enabled.
A GitHub OAuth App permits one callback URL. Use a staging OAuth App with `https://staging.<domain>/api/auth/callback/github`; keep local development on its own OAuth app or login mechanism. Do not promise OAuth on throwaway Vercel preview domains. [GitHub OAuth Apps](https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/creating-an-oauth-app)
A GitHub OAuth App permits one callback URL. Use a staging OAuth App with `https://zopu-staging.vercel.app/api/auth/callback/github`; keep local development on its own OAuth app or login mechanism. Do not promise OAuth on throwaway Vercel preview domains. [GitHub OAuth Apps](https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/creating-an-oauth-app)
## Staging topology
```mermaid
flowchart LR
Browser[Browser] -->|HTTPS| Vercel[Vercel: staging web SSR]
Browser -->|queries & mutations| Convex[Convex: staging]
Vercel -->|/api/auth rewrite| ConvexSite[Convex Site auth routes]
Convex -->|service-authenticated HTTPS| Flue[Contabo: Flue Node service]
Flue --> Engine[Rivet Engine: private Docker network]
Runner[AgentOS runner: private Docker network] --> Engine
Runner --> Volumes[Persistent source mirror + workspaces]
Browser[Browser] -->|HTTPS| Vercel[Vercel: zopu-staging.vercel.app]
Browser -->|queries & mutations| Convex[Convex: joyous-cat-297]
Vercel -->|/api/auth rewrite| ConvexSite[Convex Site: joyous-cat-297.convex.site]
Convex -->|FLUE_DB_TOKEN + org header| Flue[Contabo: Flue + AgentOS registry]
Flue -->|control plane| Engine[Rivet Engine: 127.0.0.1:6420]
Engine -->|serverless callback| Flue
Engine --> Volumes[/srv/zopu/data/rivet]
Flue --> Workspaces[/srv/zopu/workspaces + /srv/zopu/data/flue]
CF[Cloudflare DNS] --> Vercel
CF --> Flue
```
### Public surface
- `staging.<domain>` → Vercel.
- `agents-staging.<domain>` → Contabo Caddy/Traefik → Flue only.
- Only ports 80/443 (and Tailscale/managed SSH) enter the VDS. Do **not** publish Rivet ports `6420` or `6421`, the AgentOS runner, workspace directories, or any engine dashboard.
- `zopu-staging.vercel.app` → Vercel.
- `agents.zopu.puter.wtf` → Contabo VDS (`zopu-staging-1`, `158.220.110.74`) Caddy → Flue only.
- Only ports 80/443 (and Tailscale/managed SSH) enter the VDS. Do **not** publish Rivet ports `6420` or `6421`, registry internals, workspace directories, or any engine dashboard.
- The Flue route is reachable from Convex, but its middleware must continue to require the per-environment bearer `FLUE_DB_TOKEN` and organization/turn correlation headers. This is a private worker protocol exposed narrowly for Convex callbacks—not a browser API.
- Browser clients continue to talk only to Convex. Do not restore browser-to-Flue traffic or a generic `/api/*` agent proxy.
### Private VDS services
The checked-in Compose topology should contain exactly these services:
The current VDS (`zopu-staging-1`, `158.220.110.74`) runs a deliberately small private topology:
| Service | Network exposure | Persistent data | Notes |
| --- | --- | --- | --- |
| `engine` | Internal only | `/data` bind mount | Single-node RocksDB is suitable for staging. Set an admin token; probe `:6420/health`. |
| `agents` | Caddy upstream only | Shared workspace bind mount | Runs the existing Flue Node build and owns worktree preparation; `/health` is a liveness endpoint. |
| `runner` | Internal only | Shared workspace bind mount | Separate from `agents`; needs Bun, Git, and the same worktree paths prepared by `agents`. |
| `caddy` | 80/443 only | Caddy certificate/config bind mounts | TLS for the Flue callback hostname. |
| Service | Runtime | Network exposure | Persistent data | Notes |
| --- | --- | --- | --- | --- |
| `engine` | Existing Docker Compose (`/srv/zopu/compose/docker-compose.yml`) | private loopback `127.0.0.1:6420` | `/srv/zopu/data/rivet` bind mount | Single-node RocksDB backend. Health: `http://127.0.0.1:6420/health`. |
| `agents` | systemd (`/etc/systemd/system/zopu-agents.service`) | Caddy upstream only (`127.0.0.1:3000`) | `/srv/zopu/workspaces` + `/srv/zopu/data/flue` | One Node process runs Flue and its serverless AgentOS registry. `RIVET_ENDPOINT` is the Engine control plane; `RIVET_SERVERLESS_ENDPOINT` is Engine's private callback to `http://127.0.0.1:3000/internal/rivet`. |
| `caddy` | systemd (`/etc/systemd/system/caddy.service`) | 80/443 only | Caddy certificate/config volumes | `/etc/caddy/Caddyfile` terminates TLS for `agents.zopu.puter.wtf` and forwards only the documented Flue paths to `127.0.0.1:3000`. |
Release artifacts are source controlled: `scripts/release-agents.sh` builds a curated bundle locally and atomically promotes `/srv/zopu/current`; `deploy/vds/` contains the systemd unit and secret-free environment template. The VDS materializes Linux production dependencies from the frozen lockfile, so macOS-native binaries are never uploaded.
Rivets filesystem backend is explicitly appropriate for single-node deployments; multi-node/HA later requires PostgreSQL and NATS. Configure the engine with a persistent `/data` bind mount, admin token, resource limits, and a `:6420/health` probe. [Rivet Docker Compose](https://rivet.dev/docs/self-hosting/docker-compose) · [Rivet production checklist](https://rivet.dev/docs/self-hosting/production-checklist)
The runner must receive a **build-time** `RIVET_RUNNER_VERSION` derived from the CI run or immutable release revision. Rivet uses it to route new actors to the new runner and drain old actors; without it, existing actors can continue on old code.
## IaC model: Pulumi + Ansible + Compose, each at the right seam
## IaC model: Pulumi + Ansible + systemd/Compose, each at the right seam
Dokploy is intentionally excluded: it has already proved too slow and imperative for this stack. One tool should not be forced to manage three different concerns poorly.
| Layer | Source of truth | Tool | Reason |
| --- | --- | --- | --- |
| SaaS control plane | `infra/pulumi` | **Pulumi TypeScript** | Declarative Cloudflare DNS and Vercel project/domain/environment configuration; one `staging` stack now, later `production`; encrypted stack secrets and `preview`/`refresh` drift workflows. |
| VDS baseline | `infra/ansible` | **Ansible** | Idempotent OS convergence: service user, Docker, firewall, Tailscale, directories, Caddy prerequisites, and backup timer. No Pulumi SSH-command pseudo-provider. |
| VDS application topology | `deploy/compose` | **Docker Compose** | Explicit services, networks, volumes, healthchecks, images, and restart policy in the repository. This is the deployable unit. |
| Release execution | CI | **Gitea Actions or an equivalent CI runner** | Builds tagged images, pushes them to a registry, runs `ansible-playbook`, then applies a pinned Compose release and checks health. |
| VDS application topology | `deploy/vds` + `/srv/zopu/compose` | **systemd + Docker Compose** | Engine stays in its existing private Compose service; the checked-in systemd unit runs the single Node agents/registry process. |
| Release execution | `scripts/release-agents.sh` now; CI later | **Local release bundle promotion** | Builds a curated cross-platform bundle, installs Linux dependencies on the VDS, atomically swaps `current`, then restarts `zopu-agents`. |
> **Current state (2026-08-03):** the Engine and Caddy retain their manually bootstrapped VDS services. The Node agents process is now represented by source-controlled `deploy/vds` artifacts and `scripts/release-agents.sh`; Pulumi/Ansible and CI are still future work.
Pulumi state is meaningful only with a shared backend—use Pulumi Cloud or a managed/self-hosted state backend, **not** a developer-local `file://` state file. Pulumis state is what enables previews, refreshes, encrypted secret tracking, and drift detection. [Pulumi state and backends](https://www.pulumi.com/docs/iac/concepts/state-and-backends/) · [Pulumi secrets](https://www.pulumi.com/docs/iac/concepts/secrets/)
@@ -126,43 +127,39 @@ Ansible is not redundant: it makes the Contabo host reproducible without pretend
### Staging release
1. A merge to the staging branch (initially `master` if that is the stable branch) starts CI.
2. CI runs type checks and targeted tests.
3. CI builds the web for the **staging Convex deployment** and deploys it to the dedicated staging Vercel project. Build-time `VITE_CONVEX_URL` and `VITE_AUTH_URL` must be staging values.
4. CI builds immutable `agents` and `runner` images tagged with commit SHA; it sets `RIVET_RUNNER_VERSION` from the release identity.
5. CI runs Ansible convergence, updates the Compose release to those exact image digests, and executes `docker compose up -d`.
6. CI verifies: Vercel URL returns 200, Convex auth origin is accepted, agents health endpoint returns 200, engine health returns 200 inside the private network, and a real signed-in staging conversation receives an agent response.
7. Rollback means redeploying the previous image digests and restoring the corresponding Vercel deployment—not rebuilding mutable `latest` images.
1. Run repository validation and build the staging web artifact for the staging Convex deployment (`joyous-cat-297`, `https://joyous-cat-297.convex.cloud`), then deploy it to `zopu-staging` (`https://zopu-staging.vercel.app`).
2. From the repository root, create and promote the agents release with `scripts/release-agents.sh <vds-host> <release-id>`; it installs Linux production dependencies on the VDS, atomically swaps `/srv/zopu/current`, and leaves the running process untouched until restart.
3. Restart `zopu-agents.service` on the VDS. The unit waits for Engine health, then starts the one Node process that exposes Flue and the serverless registry.
4. Verify: `https://zopu-staging.vercel.app` returns 200, Convex auth origin `https://zopu-staging.vercel.app` is accepted, `https://agents.zopu.puter.wtf/health` returns 200, Engine health returns 200 at `http://127.0.0.1:6420/health` inside the private network, and a real signed-in staging conversation receives an agent response.
5. Roll back by atomically repointing `/srv/zopu/current` at the prior release then restarting `zopu-agents.service`.
Do not couple the Vercel deployment to Gitea-native Git integration assumptions. The repository is hosted on Gitea, so start with CI invoking the Vercel CLI/API. If a Git mirror is later introduced, Vercel previews can be enabled separately.
## Required implementation backlog
This research does **not** deploy anything. Before the first staging release, complete these changes in order:
The staging environment is live. Remaining work is to close the gap between the manual, on-host configuration and the declarative, CI-driven target described above:
1. Create the separate `staging` Convex deployment and its deploy key; configure deployment-scoped secrets and `SITE_URL`.
2. Correct the auth/webhook origin seams:
- Add Vercel production rewrites for `/api/auth/*` to the staging Convex Site URL; Vites current proxy only applies to local development.
- Make the Puter webhook target the staging Convex Site HTTP action directly, rather than the web origin.
3. Add Vercel React Router support and a dedicated staging deployment configuration.
4. Create the four-service Compose topology and a runner-capable image. The current agents Dockerfile starts only Flue and does not provide the dedicated Bun/Git runner service.
5. Add health routes/checks, resource limits, volume backup, image-digest deployments, and `RIVET_RUNNER_VERSION`.
6. Add `infra/pulumi` and `infra/ansible`, then CI environments with protected staging secrets.
7. Execute an end-to-end staging smoke: sign in, create/connect a project, send a conversation, and complete a disposable issue-to-PR job.
1. **Convex staging deployment** — ✅ Done as `joyous-cat-297`. `SITE_URL` is set to `https://zopu-staging.vercel.app`, `FLUE_URL`/`AGENT_BACKEND_URL` to `https://agents.zopu.puter.wtf`, and `FLUE_DB_TOKEN` to the VDS agents token.
2. **Auth/webhook origin seams** — ✅ Vercel rewrites `/api/auth/*` to `https://joyous-cat-297.convex.site/api/auth/*` in `vercel.json`. Verify that Puter/Gitea webhooks are configured to post directly to the Convex Site HTTP action URL (`https://joyous-cat-297.convex.site/api/git/webhooks/...`) rather than the web origin.
3. **Vercel React Router staging project** — ✅ Done as `zopu-staging` (`https://zopu-staging.vercel.app`).
4. **VDS agent topology** — ✅ Source-controlled VDS unit, env template, and atomic release bundle now model the single Flue + in-process registry process. Apply them to the VDS and retire the former runner service from its Compose file.
5. **Engine deployment, backups, and CI** — 🔄 Engine health and VDS backups exist. Move the existing Engine Compose service and Caddy configuration into source-controlled provisioning; add CI to run validation, promote release bundles, and deploy the web.
6. **Declarative IaC** — ⏳ Create `infra/pulumi` (Cloudflare DNS + Vercel project) and `infra/ansible` (host convergence).
7. **End-to-end staging smoke** — ⏳ Execute: sign in via GitHub OAuth, create/connect a project, send a conversation, and complete a disposable issue-to-PR job against the live `zopu-staging` + `agents.zopu.puter.wtf` stack.
## Security and operations guardrails
- Keep environment secrets in CI environment secret stores, Pulumi encrypted configuration/ESC where appropriate, Convex deployment variables, and Vercel environment variables. Never commit `.env` files or place secrets in image layers.
- Use unique `FLUE_DB_TOKEN`, Rivet admin token, workspace token, model credential, and Git token per environment.
- The VDS runs code-writing agents. Use a dedicated service user; do not mount the host home directory; expose only scoped repository credentials to individual attempts; and keep staging separate from any production host. This follows the repository-isolation policy in `docs/TECH.md` §11.
- The VDS runs code-writing agents. Use a dedicated service user; do not mount the host home directory; expose only scoped repository credentials to individual attempts; and keep staging separate from any production host.
- Back up Rivet engine state and Caddy configuration. Treat workspaces as reproducible/ephemeral unless an active job requires retention; prune completed worktrees deliberately.
- A single Flue Node instance is the correct initial staging shape. Its durable Convex adapter survives restarts, but each conversation still requires one live owner—do not add replicas until ownership routing is designed. [Flue database guide](https://flueframework.com/docs/guide/database)
## Sources
- Repository architecture: `docs/TECH.md` §§1, 1011; `docs/LOCAL_SETUP.md`; `apps/web/Dockerfile`; `packages/agents/Dockerfile`; `packages/agents/src/runtime/repository-workspace.ts`; `packages/agents/src/runtime/attempt-runner.ts`.
- Repository architecture: `docs/LOCAL_SETUP.md`; `docs/auth-proxy.md`; `vercel.json`; `apps/web/Dockerfile`; `deploy/vds/`; `scripts/release-agents.sh`.
- [Convex environments and deployments](https://docs.convex.dev/production/multiple-deployments), [hosting on Vercel](https://docs.convex.dev/production/hosting/vercel), and [HTTP actions](https://docs.convex.dev/functions/http-actions).
- [Vercel React Router](https://vercel.com/docs/frameworks/frontend/react-router) and [Vercel environments](https://vercel.com/docs/deployments/environments).
- [Rivet self-hosted Docker Compose](https://rivet.dev/docs/self-hosting/docker-compose), [production checklist](https://rivet.dev/docs/self-hosting/production-checklist), and [version upgrades](https://rivet.dev/docs/actors/versions).
- [Flue Node/Docker deployment](https://flueframework.com/docs/ecosystem/deploy/docker) and [durable database ownership](https://flueframework.com/docs/guide/database).
- [Rivet self-hosted Docker Compose](https://rivet.dev/docs/self-hosting/docker-compose), [production checklist](https://rivet.dev/docs/self-hosting/production-checklist), and [runtime modes](https://rivet.dev/docs/general/runtime-modes).
- [Flue Node deployment](https://flueframework.com/docs/ecosystem/deploy/node) and [durable database ownership](https://flueframework.com/docs/guide/database).
- [Pulumi state](https://www.pulumi.com/docs/iac/concepts/state-and-backends/), [Pulumi secrets](https://www.pulumi.com/docs/iac/concepts/secrets/), and [Ansible](https://docs.ansible.com/projects/ansible/latest/getting_started/).

View File

@@ -7,10 +7,9 @@ This guide runs the active Zopu stack locally, including the browser chat and th
```text
Browser (React Router/Vite, :5173)
-> Convex Cloud (durable product state, auth, workflows)
-> Flue agent server (:3585)
-> Rivet Engine guard endpoint (:6420)
-> AgentOS registry runner
-> isolated Pi workspace mounted from the local repository
-> Flue agent server + in-process AgentOS registry (:3585)
-> Rivet Engine control plane (:6420)
-> AgentOS workspace actor (Git + repo clone mounted from the local checkout)
-> Gitea branch and pull request
```
@@ -83,16 +82,15 @@ AGENT_MODEL_MAX_TOKENS=<positive-integer>
### Rivet and AgentOS
```env
# The private Engine control plane this process connects to.
RIVET_ENDPOINT=http://127.0.0.1:6420
RIVET_PUBLIC_ENDPOINT=http://127.0.0.1:6420
RIVET_WORKSPACE_TOKEN=<random-string-at-least-32-characters>
ZOPU_SOURCE_REPOSITORY=/absolute/path/to/zopu-code
# The path where the Engine calls the registry co-hosted in Flue.
RIVET_SERVERLESS_ENDPOINT=http://127.0.0.1:3585/internal/rivet
RIVET_WORKSPACE_TOKEN=<shared-random-workspace-token>
AGENT_WORKSPACE_ROOT=/absolute/path/to/zopu-agent-workspaces
```
Use the same repository checkout for `ZOPU_SOURCE_REPOSITORY`. The harness creates Git worktrees beneath `AGENT_WORKSPACE_ROOT`, copies the source checkout's `.env`, installs dependencies, and mounts the isolated checkout into AgentOS.
Port `6420` is the Rivet Engine guard endpoint used by the application. Port `6421` is an internal engine API-peer port and must not be used as `RIVET_ENDPOINT`.
The registry runs in the Flue process; there is no standalone AgentOS runner. `RIVET_ENDPOINT` and `RIVET_SERVERLESS_ENDPOINT` have opposite directions and are not interchangeable. Port `6421` is an internal Engine API-peer port and must not be used as `RIVET_ENDPOINT`.
### Gitea
@@ -138,7 +136,7 @@ If work execution uses a separate agent URL, set `AGENT_BACKEND_URL`; otherwise
## Start the stack
Run each process in its own terminal. The order matters for the AgentOS path.
Run each process in its own terminal. The Engine must be healthy before Flue starts because Flue serves the in-process registry that uses it.
### 1. Start Convex development
@@ -152,65 +150,26 @@ pnpm dev:server
Both commands run from `packages/backend` and explicitly load the repository-root `.env` with `--env-file=../../.env`. `packages/backend/.env.local` remains Convex CLI metadata for the selected deployment; keep the shared runtime settings in the root `.env`.
### 2. Start the whole stack
### 2. Start the Rivet Engine
The root `dev` script starts Rivet Engine, the AgentOS registry runner, Convex, Flue agents, and the web app in one terminal:
Start exactly one Engine listening on `127.0.0.1:6420` using the local Engine deployment mechanism. Do not use port `6421` as the application endpoint.
### 3. Start Flue and the in-process registry
```bash
pnpm dev
```
# Laptop-only access
pnpm --filter @code/agents dev -- --port 3585
For phone or Tailscale-device access, start the stack with the web server bound to all interfaces:
```bash
pnpm dev:tailscale
```
### 3. Start Rivet Engine
If you prefer separate terminals, start the engine from the agents package:
```bash
cd packages/agents
bun run dev:engine
```
There must be exactly one local engine listening on `127.0.0.1:6420`. Do not start a second engine, and do not use `npx rivetkit dev`; that command is unavailable in the installed version.
### 4. Start the AgentOS registry runner
```bash
cd packages/agents
bun run runner
```
The runner registers the AgentOS workspace actor with Rivet Engine. Keep it running whenever a coding attempt or issue-to-PR request may execute.
### 5. Start Flue agents
For laptop-only access:
```bash
pnpm dev:agents
```
For Convex callbacks or access from another device, bind Flue to all interfaces:
```bash
pnpm dev:tailscale:agents
```
The dev scripts already use port `3585`; the Flue CLI defaults to `3583`, but the current Zopu configuration and Convex environment use `3585`.
If the shell already exports remote Rivet values, shell values override `.env`. Start Flue with explicit local values:
```bash
# Convex callbacks or access from another device
HOST=0.0.0.0 \
RIVET_ENDPOINT=http://127.0.0.1:6420 \
RIVET_PUBLIC_ENDPOINT=http://127.0.0.1:6420 \
bun run dev:tailscale:agents -- --port 3585
RIVET_SERVERLESS_ENDPOINT=http://127.0.0.1:3585/internal/rivet \
pnpm --filter @code/agents dev -- --port 3585
```
### 6. Start the web application
The Flue development server serves both agent routes and the AgentOS registry at `/internal/rivet/*`; never start a separate registry runner. If shell exports override `.env`, set both endpoints explicitly as shown.
### 4. Start the web application
If you are not using the root `dev` script, start the web app separately.
@@ -233,12 +192,12 @@ Open `http://localhost:5173`, or `http://<tailscale-ip>:5173` when using the Tai
Check the listeners:
```bash
curl -I http://127.0.0.1:6420
curl -I http://127.0.0.1:3585
curl -fsS http://127.0.0.1:6420/health
curl -fsS http://127.0.0.1:3585/health
curl -I http://127.0.0.1:5173
```
Any HTTP response confirms the process is reachable; these roots may redirect or return `404` because their functional routes live elsewhere.
The first two commands must return a successful health response; the web root may redirect or return `404` if its functional routes live elsewhere.
Then verify behavior in order:
@@ -285,21 +244,21 @@ Set Convex `SITE_URL` to the exact browser origin. The shared development deploy
### AgentOS fails while mounting the repository
- Confirm both Rivet endpoints use port `6420`.
- Confirm the engine and `bun run runner` are both running.
- Confirm `ZOPU_SOURCE_REPOSITORY` contains `.git`.
- Confirm the Engine is healthy at `http://127.0.0.1:6420/health`.
- Confirm Flue is running with `RIVET_ENDPOINT=http://127.0.0.1:6420`.
- Confirm `RIVET_SERVERLESS_ENDPOINT` points back to the active Flue listener with `/internal/rivet` appended.
- Confirm `AGENT_WORKSPACE_ROOT` is writable.
- Restart the runner after changing AgentOS registry configuration; Flue hot reload is not enough.
- Restart Flue after changing registry configuration; its development server owns the in-process registry.
The harness clients require CBOR encoding for host-directory mount descriptors. Do not remove the `encoding: "cbor"` configuration in `packages/agents/src/runtime/agent-os.ts`.
The harness clients require CBOR encoding for actor RPC. Do not remove the `encoding: "cbor"` configuration in `packages/agents/src/adapters/agentos.ts`.
### AgentOS reports an ACP completed-message resource limit
The workspace registry raises `limits.acp.maxCompletedMessageBytes` for long Pi coding runs. Restart the runner so the updated registry configuration is registered with Rivet Engine.
Restart Flue so the in-process registry picks up its updated actor configuration.
### Flue connects to a remote Rivet deployment unexpectedly
Environment variables exported by the shell override values loaded from `.env`. Start Flue and the runner with explicit `RIVET_ENDPOINT` and `RIVET_PUBLIC_ENDPOINT` values pointing to `127.0.0.1:6420`.
Environment variables exported by the shell override values loaded from `.env`. Start Flue with explicit `RIVET_ENDPOINT=http://127.0.0.1:6420` and `RIVET_SERVERLESS_ENDPOINT=http://127.0.0.1:3585/internal/rivet`.
### Gitea issue or PR commands fail
@@ -327,7 +286,6 @@ For agent-only changes, run `bun run check-types` from `packages/agents` before
See also:
- [`TECH.md`](TECH.md) for architecture and ownership boundaries
- [`deployment.md`](deployment.md) for the shared staging deployment
- [`DEPLOYMENT_PLAN.md`](DEPLOYMENT_PLAN.md) for the staging topology and VDS release path
- [Rivet AgentOS quickstart](https://rivet.dev/docs/agent-os/quickstart)
- [Flue local development](https://flue.dev/docs/cli/dev)
- [Flue Node deployment](https://flueframework.com/docs/ecosystem/deploy/node)

View File

@@ -1,59 +0,0 @@
# Zopu Work OS — Documentation Map
Dense context set for product development and coding agents.
## Read order
```text
1. agent-context.md — bootstrap and non-negotiables
2. product.md — product model and quality doctrine
3. glossary.md — canonical terminology
4. dev-loop.md — normative software delivery process
5. tech.md — architecture, actors, ports, runtimes
6. design.md — user experience
7. slices.md — sequential vertical product increments
8. dev-plan.md — repository execution plan/backlog
9. evaluation.md — quality measurement and improvement
```
For a runnable development environment, see [`LOCAL_SETUP.md`](LOCAL_SETUP.md).
## Use by role
| Role | Minimum context |
| --- | --- |
| Product/definition agent | agent-context, product, glossary, dev-loop |
| Architecture/design agent | agent-context, tech, dev-loop, current Work Definition |
| Coding agent | agent-context, current Design Packet/Slice, tech sections, repository rules |
| Verifier | agent-context, dev-loop, evaluation, Verification Plan |
| Frontend agent | agent-context, design, product, current slice |
| Lead/orchestrator | all documents, current repository state, active decisions |
## Document boundaries
```text
product.md what/why
tech.md system architecture
design.md interaction behavior
dev-loop.md how software Work is delivered
slices.md how Zopu product is incrementally built
dev-plan.md engineering order and release gates
evaluation.md how quality and improvements are measured
glossary.md exact term definitions
agent-context compact instructions for execution agents
```
## First build target
```text
message
→ Signal
→ approved Work Definition
→ approved Design Packet
→ one isolated implementation slice
→ independent verification
→ exact verified PR
→ human question/resume
```
Start at `dev-plan.md` backlog item 01. Do not begin dynamic Kit Builder or fleets before Slices 17 work reliably.

View File

@@ -1,676 +0,0 @@
# Zopu Work OS — Technical Architecture
> **Related:** `agent-context.md` defines agent rules; `dev-loop.md` defines orchestration; `glossary.md` defines terms.
> **Status:** Working technical source of truth
> **Audience:** engineers and implementation agents
> **Read after:** `product.md`
> **Principle:** durable intent/state is separate from disposable execution.
## 1. System topology
```text
Web / desktop / mobile
│ Convex queries, mutations, storage
Convex application backend
├── authentication and tenancy
├── normalized product data
├── conversation turn queue
└── reactive client projections
│ durable Workflow steps + service-authenticated dispatch
Private agent backend
├── FLUE product agents and typed tools
├── AgentOS execution environments
├── Codex implementation harness
└── canonical events/results returned to Convex
│ optional attached full sandbox
Cube/E2B-compatible runtime (later)
```
Clients MUST communicate only with Convex. FLUE and future Rivet/AgentOS services are private workers: Convex admits durable commands, invokes the worker, and stores the product-facing result before clients observe it.
### Ownership
| Layer | Owns |
| --- | --- |
| Convex | authentication, normalized product records, command admission, reactive reads |
| Convex Workflow | durable sequencing, retries, cancellation, scheduling, and completion callbacks |
| Agent backend | private programmable agents and execution adapters |
| Rivet Engine + runners | placement, routing, sleep/wake, and execution ownership for AgentOS workspaces |
| Harness | bounded coding/tool loop |
| Sandbox/runtime | filesystem, processes, network, isolation |
| Git | source revision history |
| Artifact store | durable outputs/evidence |
| External systems | collaboration, delivery, monitoring |
No client, harness, sandbox, or FLUE process owns product state.
### Slice 1 relational core
```text
organizations ──< organizationMembers
organizations ──< projects ──< projectContextDocuments
organizations ──1 conversations ──< conversationTurns
conversationTurns ──< conversationMessages ──< conversationAttachments
projects ──< signals ──< signalConstraints
signals ──< signalSources >── conversationMessages
projects ──< works
signals ──< signalWorkAttachments >── works
works ──< workEvents
```
Convex mutations provide atomic transactions and optimistic serializability. Foreign-key integrity and uniqueness are enforced in the owning mutations; composite indexes back every identity and relation lookup. Flue's adapter tables remain isolated infrastructure persistence and are not product-domain relations.
## 2. Domain boundaries
Recommended packages:
```text
packages/
├── signals/
├── work/
├── design/
├── planning/
├── kits/
├── resolver/
├── verification/
├── artifacts/
├── results/
├── knowledge/
├── runtimes/
├── harnesses/
└── integrations/
```
Each domain SHOULD separate:
```text
domain/ pure state, schemas, invariants
application/ use cases and orchestration
ports/ Effect services
adapters/ infrastructure implementations
```
Avoid package proliferation in v0; logical boundaries may begin as folders.
## 3. Core records
Minimal durable model:
```ts
type Id = string;
interface Message {
id: Id;
projectId: Id;
content: string;
createdAt: number;
}
interface Signal {
id: Id;
projectId: Id;
sourceType: string;
sourceId: string;
sourcePayloadRef?: string;
summary: string;
fingerprint: string;
status: "candidate" | "accepted" | "dismissed";
}
interface Work {
id: Id;
projectId: Id;
title: string;
objective: string;
risk: "low" | "medium" | "high";
status: WorkStatus;
definitionVersion?: number;
designVersion?: number;
createdAt: number;
updatedAt: number;
}
interface Step {
id: Id;
workId: Id;
sliceId?: Id;
kind: "design" | "implement" | "verify" | "integrate" | "publish" | "observe";
objective: string;
dependsOn: readonly Id[];
status: StepStatus;
}
interface Run {
id: Id;
workId: Id;
stepId: Id;
kitVersion: string;
status: RunStatus;
}
interface Attempt {
id: Id;
runId: Id;
number: number;
harness: string;
runtime: string;
sourceRevision: string;
status: AttemptStatus;
startedAt?: number;
endedAt?: number;
}
interface Artifact {
id: Id;
workId: Id;
stepId?: Id;
runId?: Id;
attemptId?: Id;
type: string;
uri?: string;
contentHash?: string;
sourceRevision?: string;
environmentId?: string;
metadata: unknown;
}
interface Question {
id: Id;
workId: Id;
stepId?: Id;
attemptId?: Id;
prompt: string;
recommendation?: string;
alternatives: readonly string[];
status: "open" | "answered" | "withdrawn";
answer?: string;
}
```
All external/model payloads MUST be decoded with schemas at boundaries.
## 4. Work state machine
```text
Proposed
→ Defining
→ Designing
→ Ready
→ ExecutingSlice
→ VerifyingSlice
→ AwaitingSliceReview
→ IntegratedVerification
→ Review
→ Releasing
→ Observing
→ Completed
```
Side/terminal states:
```text
NeedsInput | Blocked | Replanning | Failed | Cancelled
```
Transitions require explicit commands plus evidence. State MUST NOT be inferred from the latest text message.
## 5. Actor model
Start small.
### ProjectActor
Owns:
- project configuration;
- repository/runtime policies;
- active Work index;
- integration endpoints;
- project-level Signal routing.
### WorkActor
Initial central aggregate:
- Work Definition and versions;
- Design Packet and versions;
- slice/step graph;
- Resolver state;
- questions;
- artifact references;
- result.
It serializes lifecycle transitions and commands.
### AttemptActor
Owns one execution attempt:
- runtime lease/sandbox ID;
- harness session;
- scoped credentials;
- event stream/checkpoints;
- cancellation;
- attempt timeout;
- normalized outcome.
### VerificationActor
Split from WorkActor after v0 verification is stable. Owns plan, checks, evidence, verdict.
### IntegrationActor
Added when multiple slices/branches exist. Owns branch composition, conflicts, integrated revision, integrated checks.
### ResultActor
Added after delivery observation exists. Owns rollout observation, original success signals, final outcome, learning proposals.
Do not create an actor per document or tool call. Create actors for durable identity, serialized ownership, independent failure/recovery, or timers.
## 6. Commands, events, and idempotency
Use command/event vocabulary rather than mutable agent prose.
Example commands:
```text
ProcessMessage
AcceptSignal
CreateWork
ApproveDefinition
ApproveDesign
StartNextSlice
RecordHarnessEvent
CompleteAttempt
StartVerification
RecordVerificationCheck
CreateRepairAttempt
PublishPullRequest
RecordHumanDecision
CompleteWork
```
Example events:
```text
SignalCreated
SignalAttached
WorkProposed
DefinitionGenerated
DefinitionApproved
DesignGenerated
DesignApproved
SliceStarted
AttemptStarted
AttemptCompleted
VerificationPassed
VerificationFailed
QuestionOpened
QuestionAnswered
PullRequestCreated
WorkCompleted
```
Every side-effecting command MUST include an idempotency key. Suggested key:
```text
<workId>:<commandType>:<logicalVersion>
```
External artifact creation stores provider ID before transition completion to prevent duplicate PRs/deployments.
## 7. Effect service boundaries
Keep domain/application code provider-neutral.
```ts
interface HarnessRuntime {
open(input: OpenHarnessInput): Effect.Effect<HarnessSession, HarnessError>;
prompt(id: string, content: string): Effect.Effect<void, HarnessError>;
events(id: string): Stream.Stream<HarnessEvent, HarnessError>;
approve(input: PermissionDecision): Effect.Effect<void, HarnessError>;
abort(id: string): Effect.Effect<void, HarnessError>;
close(id: string): Effect.Effect<void, HarnessError>;
}
interface SandboxRuntime {
create(spec: SandboxSpec): Effect.Effect<SandboxLease, SandboxError>;
exec(
lease: SandboxLease,
cmd: Command
): Effect.Effect<CommandResult, SandboxError>;
readFile(
lease: SandboxLease,
path: string
): Effect.Effect<Uint8Array, SandboxError>;
writeFile(
lease: SandboxLease,
path: string,
body: Uint8Array
): Effect.Effect<void, SandboxError>;
pause(lease: SandboxLease): Effect.Effect<void, SandboxError>;
resume(id: string): Effect.Effect<SandboxLease, SandboxError>;
terminate(lease: SandboxLease): Effect.Effect<void, SandboxError>;
}
interface SourceControl {
prepareWorktree(input: WorktreeInput): Effect.Effect<Worktree, GitError>;
diff(worktree: Worktree): Effect.Effect<DiffArtifact, GitError>;
commit(input: CommitInput): Effect.Effect<CommitArtifact, GitError>;
push(input: PushInput): Effect.Effect<BranchArtifact, GitError>;
createPullRequest(
input: PullRequestInput
): Effect.Effect<PullRequestArtifact, GitError>;
}
interface VerificationRuntime {
execute(
plan: VerificationPlan,
env: EnvironmentRef
): Effect.Effect<VerificationResult, VerificationError>;
}
```
Also define:
```text
ArtifactStore | SecretBroker | EventJournal | PreviewRuntime | RuntimePolicy
```
Use `Layer` for adapters, `Scope` for leases/processes, `Stream` for events, `Schedule` for bounded retries, and supervised fibers for long-running consumers. Pin Effect version and isolate beta API churn.
## 8. FLUE responsibilities
Use FLUE for domain-specific agents/workflows:
- conversation response and Signal proposal;
- Work Definition compiler;
- impact analysis;
- architecture/program design;
- vertical-slice planning;
- Resolver decision support;
- verification-plan generation;
- maintainability review;
- result/learning synthesis.
FLUE output MUST be typed proposals/commands, validated by application policy.
FLUE MUST NOT directly:
- mutate arbitrary database tables;
- bypass actor lifecycle;
- grant itself tools;
- merge/deploy outside policy;
- mark Work complete without evidence.
## 9. Harness strategy
Harness is replaceable:
```text
HarnessRuntime
├── OmpHarnessLive
├── OpenCodeHarnessLive
├── CodexHarnessLive
├── PiHarnessLive
└── FlueHarnessLive
```
v0 selects one harness. Prefer mature coding harnesses for repository exploration/edit/test loops; use custom FLUE agents for product-specific reasoning.
Zopu owns Work, branches, worktrees, budgets, artifacts, and completion. Harness owns one bounded implementation loop.
## 10. Runtime strategy
### Convex orchestration
Slice 5 uses the Convex Workflow component as the durable product orchestration layer. Workflow steps call private agent-backend actions, while all user-visible state, events, artifacts, idempotency keys, and cancellation remain canonical in Convex.
### CubeSandbox
Best for full Linux execution:
- native binaries;
- Bun/Node/Python;
- browsers;
- databases/services;
- project builds/tests;
- pause/resume;
- strong isolation.
Use E2B-compatible SDK behind `SandboxRuntime`. OMP runs as a process inside the Cube microVM.
### AgentOS
Best for:
- lightweight durable agent environments;
- ACP-integrated software;
- actor-adjacent orchestration;
- context/files/networking that fit runtime limits.
Slice 5 starts here with one Codex-backed AgentOS actor and one authenticated repository checkout per project. Convex Workflow owns product orchestration; Rivet Engine coordinates placement while a normal runner executes the actor. Use an attached full sandbox through the E2B-compatible boundary when native/heavy tooling is needed.
### Persistent project machine
Later optimization for long-lived caches, large repositories, and developer-customized environments. Use Git worktrees per active Work. Do not make it the only isolation boundary.
### Runtime selection
The Resolver requests capabilities:
```text
writable repo, Bun, browser, PostgreSQL, 8 GB RAM, network policy
```
`RuntimePolicy` chooses provider. Product logic never branches on Cube/AgentOS directly.
## 11. Repository isolation
Initial safe model:
```text
one project repository mirror
one Git worktree per active Work/slice
one mutating attempt per worktree
```
Rules:
- attempts receive scoped worktrees;
- parallel mutating attempts use separate branches;
- credentials are short-lived and repository-scoped;
- host home directories are never mounted;
- model credentials are run-scoped;
- untrusted code runs in sandbox;
- output commits record base and candidate SHA.
## 12. Planning and design artifacts
Required for standard work:
```text
WorkDefinition
ImpactMap
DesignPacket
VerticalSlicePlan
VerificationPlan
```
Program design SHOULD include:
- expected file-tree delta;
- expected call-flow delta;
- key types/signatures;
- dependency direction;
- invariants;
- security/data boundaries;
- known deviations.
The verifier compares candidate code against this design, but metrics are evidence, not absolute truth.
## 13. Verification architecture
Verification layers:
```text
Static
├── format/lint/typecheck
├── dependency policy
├── secret scan
└── static security
Behavior
├── unit
├── integration
├── contract
└── property tests where useful
Product
├── browser/user flow
├── screenshots/video
├── accessibility
└── visual checks
Operational
├── build/start/health
├── migration/rollback
├── logs
└── resource limits
Design
├── expected vs actual files/interfaces
├── dependency graph delta
├── design deviations
└── maintainability review
```
A `VerificationResult` MUST bind checks to candidate commit and environment.
Repair loop:
```text
failure evidence → bounded repair attempt → clean verification rerun
```
Tests added by the implementation SHOULD fail on the base revision and pass on the candidate when practical.
## 14. Delivery and integration
Publishing order:
```text
slice checks pass
→ integrated candidate created
→ impacted checks on integrated SHA
→ commit/push
→ PR
→ review package
```
Never create a “verified PR” from a different SHA than the verified candidate.
Review package:
- original intent;
- current definition/design;
- slice narrative;
- meaningful diffs;
- screenshots/video;
- verification evidence;
- deviations/risks;
- exact commit/PR.
Manual merge remains policy in v0.
## 15. Preview and release
Preview is an artifact, not an open random port. `PreviewRuntime` may use:
- sandbox-exposed app;
- agentOS Apps for compatible generated HTTP apps;
- external staging/deployment.
Production release requires explicit policy, rollout plan, health signals, and rollback trigger.
## 16. Security baseline
- private control-plane endpoints;
- authenticated Cube/Rivet APIs;
- scoped runtime tokens;
- no long-lived model/Git secrets in workspace files;
- network egress policy;
- artifact access authorization;
- immutable audit trail for all Work events;
- tool allowlist per Kit;
- destructive tools denied by default;
- merge/deploy human-gated initially;
- cleanup of terminated sandboxes and credentials.
## 17. Observability
Record product-level events, not only infrastructure logs.
Required dimensions:
```text
projectId workId sliceId runId attemptId actorId
kitVersion harness runtime model baseSha candidateSha
```
Track:
- state-transition latency;
- attempt duration/outcome;
- retries/replans;
- verification checks;
- human wait time;
- token/compute cost;
- duplicate side effects;
- abandoned/stale Work;
- post-release failures.
Raw harness logs are retained as artifacts; UI consumes normalized events.
## 18. Initial deployment shape
```text
Web + API + Convex
Bun/Effect daemon
Rivet cluster/actors
├── FLUE agents/workflows
└── SandboxRuntime
└── CubeSandbox on dedicated/VDS host
└── OMP + repo + tests
```
Keep Cube control APIs private; expose only authorized preview paths. Rivet and execution daemon may share the VPS initially but remain separate deployable processes.
## 19. Technical acceptance for v0
The architecture is proven when one real repository supports:
```text
message → Signal → proposed Work → ready Work
→ one slice → isolated harness run → independent verification
→ verified commit → real PR → human response/resume
```
With:
- durable recovery;
- no duplicate Work/PR;
- bounded retries;
- exact evidence;
- cancellation;
- explicit terminal states.

View File

@@ -1,334 +0,0 @@
# Zopu Work OS — Agent Context
> **Purpose:** compact bootstrap context for Codex/OMP/OpenCode/FLUE workers contributing to Zopu.
> **Usage:** read this first, then load only task-relevant documents and repository files.
## 1. Mission
Build Zopu: a Work OS that turns conversation and external Signals into durable, verified outcomes.
```text
Signal → Work → Definition → Design → Vertical Slices
→ Resolver → Attempts → Verification → Delivery → Result → Learning
```
The product is not chat threads, an issue tracker, a harness UI, or an autonomous PR factory.
## 2. Source-of-truth order
```text
1. current accepted Work/Question/Decision
2. approved Work Definition
3. approved Design Packet + current Slice
4. repository tests/types/code
5. product.md
6. tech.md
7. design.md
8. dev-loop.md
9. slices.md
10. dev-plan.md
11. glossary.md
12. evaluation.md
```
When sources conflict, report the conflict. Do not silently choose a convenient interpretation.
## 3. Current product target
Initial user:
```text
technical founder / small engineering team
one project + one Git repository
web chat + Work cards
manual merge
```
Initial complete loop:
```text
message
→ Signal
→ approved Work
→ approved Design Packet
→ one bounded coding slice
→ independent verification
→ exact verified commit
→ real PR
→ human intervention/resume
```
## 4. Non-negotiable invariants
1. Work is a durable outcome, not a chat/session/issue/PR.
2. Exact Signal provenance is preserved.
3. State transitions are explicit commands/events.
4. FLUE/model output is validated proposal data, not authority.
5. Rivet actors own serialized lifecycle/recovery.
6. Harnesses and runtimes are replaceable adapters.
7. One mutating Attempt owns one isolated worktree.
8. Every Attempt is bounded and terminally classified.
9. Builder output remains candidate output until independent verification.
10. Evidence binds to exact revision and environment.
11. Published PR head equals verified integrated SHA.
12. Human questions/approvals are durable objects.
13. Retry/restart must not duplicate Work, commits, PRs, or deployments.
14. Merge/deploy remain human-gated initially.
15. Completion requires outcome evidence, not an agents “done.”
## 5. System ownership
```text
Work OS durable intent/state/evidence/policy
Rivet actors identity, serialization, timers, recovery
FLUE domain agents/workflows and typed proposals
Harness bounded coding/tool loop
Sandbox filesystem/process/network isolation
Git source history
ArtifactStore durable output/evidence
UI/Buzz interaction surfaces, not workflow truth
```
## 6. Initial actor shape
```text
ProjectActor
├── project config + active Work index
WorkActor
├── definition/design/slices
├── resolver state
├── questions/approvals
├── artifacts/result
AttemptActor
├── runtime lease
├── harness session
├── normalized events
├── timeout/cancellation/outcome
```
Add Verification/Integration/Result actors only when their lifecycles justify separation.
## 7. Initial adapters
Preferred first implementation:
```text
FLUE definition/design/resolver support
Rivet actors
CubeSandbox full Linux runtime
OMP first coding harness, replaceable
Git forge branch/commit/PR
Convex durable application data/projections where used
Effect application ports/layers/errors/scopes/streams
```
Do not import provider-specific types into domain modules.
## 8. Working protocol for every code task
### Before editing
1. Identify current vertical slice and acceptance criteria.
2. Read relevant docs and repository rules.
3. Inspect current architecture, tests, and existing abstractions.
4. State impacted modules and risks.
5. Produce/confirm a small implementation plan.
6. Ask a Question only when ambiguity changes behavior, security, data, or architecture.
### During implementation
1. Stay within the current slice.
2. Preserve dependency direction.
3. Prefer existing patterns over parallel frameworks.
4. Add typed boundaries and tagged errors.
5. Make external effects idempotent.
6. Add cancellation/timeouts for long-running operations.
7. Record exact revisions/provider identifiers.
8. Add tests with the implementation.
9. Do not weaken unrelated tests or hide failures.
10. Explain intentional Design Packet deviations.
### Before declaring candidate ready
Run task-relevant checks:
```text
format/lint/typecheck
focused unit/integration tests
behavioral/live check
security/secret check where relevant
design-conformance review
```
Report:
```text
files changed
behavior implemented
checks run + results
known risks/deviations
artifacts/revision
remaining blockers
```
Never claim verified unless the independent verification path ran.
## 9. Code organization rule
Use logical separation:
```text
domain/ pure types/state/invariants
application/ use cases/commands
ports/ Effect services
adapters/ FLUE/Rivet/Cube/OMP/Git implementations
```
Keep the initial package count practical. A folder boundary is enough until independent reuse/lifecycle exists.
## 10. State modeling rule
Prefer explicit state machines over booleans.
Bad:
```ts
isRunning: boolean;
isDone: boolean;
hasError: boolean;
```
Good:
```ts
type AttemptStatus =
| "queued"
| "running"
| "succeeded"
| "retryable_failure"
| "needs_input"
| "blocked"
| "verification_failed"
| "budget_exhausted"
| "cancelled"
| "permanent_failure";
```
Transitions must validate current version/state.
## 11. Model/agent output rule
Agents return schemas such as:
```text
SignalProposal
WorkDefinitionProposal
DesignPacketProposal
ResolverDecisionProposal
VerificationAssessment
LearningProposal
```
Application code decodes, authorizes, and executes commands. Agents do not directly mutate arbitrary state.
## 12. Quality rule
For every behavior, identify:
```text
acceptance criterion
implementation evidence
independent check
exact candidate revision
review requirement
```
Tests prove immediate behavior. Design review protects maintainability and future change cost. Both matter.
## 13. UI rule
Expose:
```text
outcome
phase
latest meaningful update
next action/blocker
slice progress
evidence
delivery/result
```
Hide low-level harness noise by default. Preserve raw logs in diagnostics.
## 14. Scope control
Do not implement unless required by the current slice:
- dynamic Kit Builder;
- multiple harness providers;
- large fleets;
- autonomous merge/deploy;
- universal workflows;
- automatic canonical-memory mutation;
- sophisticated multi-tenancy.
Design replaceable boundaries, then ship one path.
## 15. Task completion template
```md
## Implemented
- ...
## Behavior
- ...
## Verification run
- `command` — pass/fail
- ...
## Evidence
- candidate SHA/artifacts
- ...
## Design deviations
- none / ...
## Remaining risks or blockers
- none / ...
```
## 16. Required reading by task type
| Task | Read |
| ------------------ | ---------------------------------------- |
| product/domain | product.md, glossary.md |
| UI | design.md, product.md |
| actors/state | tech.md, glossary.md |
| orchestration | dev-loop.md, tech.md |
| current sequencing | slices.md, dev-plan.md |
| verification/evals | evaluation.md, dev-loop.md |
| provider adapter | tech.md plus provider docs |
| broad feature | agent-context.md + all relevant sections |
## 17. Stop and escalate when
- accepted definition/design is missing or contradictory;
- required permission is unavailable;
- worktree/revision identity is uncertain;
- a destructive action is outside policy;
- verification cannot bind to exact candidate;
- implementation requires expanding scope materially;
- a retry would repeat a non-transient failure;
- repository state suggests another active mutating owner.
Return a precise Question or blocker, not a guess.

35
docs/agents/domain.md Normal file
View File

@@ -0,0 +1,35 @@
# Domain Docs
How the engineering skills should consume this repo's domain documentation when exploring the codebase.
## Before exploring, read these
- **`CONTEXT.md`** at the repo root
- **`docs/adr/`** — read ADRs that touch the area you're about to work in
If any of these files don't exist, **proceed silently**. Don't flag their absence; don't suggest creating them upfront. The `/domain-modeling` skill creates them lazily when terms or decisions actually get resolved.
## File structure
Single-context repo:
```
/
├── CONTEXT.md
├── docs/adr/
│ ├── 0001-event-sourced-orders.md
│ └── 0002-postgres-for-write-model.md
└── src/
```
## Use the glossary's vocabulary
When your output names a domain concept (in an issue title, a refactor proposal, a hypothesis, a test name), use the term as defined in `CONTEXT.md`. Don't drift to synonyms the glossary explicitly avoids.
If the concept you need isn't in the glossary yet, that's a signal — either you're inventing language the project doesn't use (reconsider) or there's a real gap (note it for `/domain-modeling`).
## Flag ADR conflicts
If your output contradicts an existing ADR, surface it explicitly rather than silently overriding:
> _Contradicts ADR-0007 (event-sourced orders) — but worth reopening because…_

View File

@@ -0,0 +1,43 @@
# Issue tracker: Gitea + local markdown
This repo uses a hybrid workflow: issues are drafted and triaged locally as markdown files under `.scratch/`, then published as polished issues to Gitea (`git.openputer.com:2222/puter/zopu-code`) when they are ready for the public tracker.
## Local markdown conventions
- One feature per directory: `.scratch/<feature-slug>/`
- The spec/PRD is `.scratch/<feature-slug>/spec.md`
- Implementation issues are one file per ticket at `.scratch/<feature-slug>/issues/<NN>-<slug>.md`, numbered from `01` — never a single combined tickets file
- Triage state is recorded as a `Status:` line near the top of each issue file (see `triage-labels.md` for the role strings)
- Comments and conversation history append to the bottom of the file under a `## Comments` heading
## Gitea conventions
Use the Gitea web UI or API at `https://git.openputer.com/puter/zopu-code/issues`.
- Create a polished issue from a local draft: copy the title and body from the final local file, then create the issue in Gitea.
- Read an issue: open the issue in the Gitea UI or fetch it via the API.
- List issues: use the Gitea UI or API with label/state filters.
- Comment on an issue: add a comment through the Gitea UI or API.
- Apply / remove labels: edit labels through the Gitea UI or API.
- Close an issue: close through the Gitea UI or API.
## When a skill says "publish to the issue tracker"
1. If the output is a draft, spec, or work-in-progress, write it under `.scratch/<feature-slug>/`.
2. If the output is a final, polished issue, create it in Gitea and, if helpful, archive the local draft with a link to the Gitea issue number.
## When a skill says "fetch the relevant ticket"
- For local drafts: read the file at the referenced path.
- For Gitea issues: open or fetch the issue by its Gitea number.
## Wayfinding operations
Used by `/wayfinder`. The **map** is a file with one **child** file per ticket.
- **Map**: `.scratch/<effort>/map.md` — the Notes / Decisions-so-far / Fog body.
- **Child ticket**: `.scratch/<effort>/issues/NN-<slug>.md`, numbered from `01`, with the question in the body. A `Type:` line records the ticket type (`research`/`prototype`/`grilling`/`task`); a `Status:` line records `claimed`/`resolved`.
- **Blocking**: a `Blocked by: NN, NN` line near the top. A ticket is unblocked when every file it lists is `resolved`.
- **Frontier**: scan `.scratch/<effort>/issues/` for files that are open, unblocked, and unclaimed; first by number wins.
- **Claim**: set `Status: claimed` and save before any work.
- **Resolve**: append the answer under an `## Answer` heading, set `Status: resolved`, then append a context pointer (gist + link) to the map's Decisions-so-far in `map.md`.

View File

@@ -0,0 +1,15 @@
# Triage Labels
The skills speak in terms of five canonical triage roles. This file maps those roles to the actual label strings used in this repo's issue tracker.
| Label in mattpocock/skills | Label in our tracker | Meaning |
| --- | --- | --- |
| `needs-triage` | `needs-triage` | Maintainer needs to evaluate this issue |
| `needs-info` | `needs-info` | Waiting on reporter for more information |
| `ready-for-agent` | `ready-for-agent` | Fully specified, ready for an AFK agent |
| `ready-for-human` | `ready-for-human` | Requires human implementation |
| `wontfix` | `wontfix` | Will not be actioned |
When a skill mentions a role (e.g. "apply the AFK-ready triage label"), use the corresponding label string from this table.
Edit the right-hand column to match whatever vocabulary you actually use.

View File

@@ -16,8 +16,8 @@ At the public application domain, route:
```caddy
zopu.example.com {
handle /api/auth/* {
reverse_proxy https://befitting-dalmatian-161.convex.site {
header_up Host befitting-dalmatian-161.convex.site
reverse_proxy https://joyous-cat-297.convex.site {
header_up Host joyous-cat-297.convex.site
}
}
@@ -51,7 +51,7 @@ npx convex env set SITE_URL 'https://zopu.example.com'
npx convex env set SITE_URL 'http://100.101.157.28:5173'
# Staging
npx convex env set SITE_URL 'https://zopu.cheaptricks.puter.wtf'
npx convex env set SITE_URL 'https://zopu-staging.vercel.app'
```
This value is Better Auth's public `baseURL` and `trustedOrigins`. It must be the browser origin because Better Auth writes OAuth state cookies and receives provider callbacks through the same-origin proxy. `CONVEX_SITE_URL` remains the internal Convex HTTP site URL.

View File

@@ -1,233 +0,0 @@
# Deployment Notes
Two active environments share one Convex deployment (`dev:befitting-dalmatian-161`). Each runs its own web server, Flue agents process, and `.env` file.
## Environments
| | Local Mac Dev | Cheaptricks Staging |
| --- | --- | --- |
| SSH alias | (local) | `cheaptricks` |
| Repo path | `/Users/puter/Workspace/zopu/code` | `/workspace/code` |
| Tailscale IPv4 | `100.101.157.28` | `100.122.185.111` |
| Web URL | `http://100.101.157.28:5173` | `https://zopu.cheaptricks.puter.wtf` |
| Flue URL (internal) | `http://100.101.157.28:3585` | `http://127.0.0.1:3585` |
| Flue URL (browser-facing) | `http://100.101.157.28:3585` | `https://zopu.cheaptricks.puter.wtf/api` |
| Caddy | none (direct Tailscale) | `zopu.cheaptricks.puter.wtf` HTTPS termination |
| Bun | installed directly | symlink at `/workspace/.bun` |
| Process manager | manual `bun run dev:tailscale:*` | `nohup` into `/tmp/zopu-*.log` |
## Shared Convex deployment
Deployment name: `dev:befitting-dalmatian-161` Convex URL: `https://befitting-dalmatian-161.convex.cloud` Convex Site URL: `https://befitting-dalmatian-161.convex.site`
Convex env vars are deployment-scoped, not per-machine. Authenticate from any machine with `npx convex dev` inside `packages/backend`.
Key Convex env var:
```
SITE_URL = <the origin Better Auth should trust>
```
This controls `trustedOrigins` in the Better Auth config (`packages/backend/convex/auth.ts`). It must match the URL the browser actually visits, or sign-in fails silently with a CORS rejection.
When switching between environments:
```bash
cd packages/backend
# For local Mac testing:
npx convex env set SITE_URL 'http://100.101.157.28:5173'
# For Cheaptricks staging:
npx convex env set SITE_URL 'https://zopu.cheaptricks.puter.wtf'
```
### OAuth consequence
`SITE_URL` drives both Better Auth's public callback origin and the single GitHub OAuth App callback. When switching the shared deployment between the local and staging origins, also update the GitHub OAuth App's **Authorization callback URL** to `<SITE_URL>/api/auth/callback/github` before attempting a GitHub connection. GitHub OAuth Apps allow one callback URL; use distinct Convex deployments and OAuth Apps if both environments must operate concurrently.
## Local Mac Dev `.env`
```env
CONVEX_DEPLOYMENT=dev:befitting-dalmatian-161
CONVEX_URL=https://befitting-dalmatian-161.convex.cloud
CONVEX_SITE_URL=https://befitting-dalmatian-161.convex.site
SITE_URL=http://100.101.157.28:5173
NATIVE_APP_URL=code://
VITE_CONVEX_URL=https://befitting-dalmatian-161.convex.cloud
VITE_CONVEX_SITE_URL=https://befitting-dalmatian-161.convex.site
VITE_FLUE_URL=http://100.101.157.28:3585
DAEMON_ID=local-macbook
DAEMON_NAME=Local MacBook
DAEMON_VERSION=0.0.0
DAEMON_HEARTBEAT_MS=15000
DAEMON_COMMAND_LEASE_MS=60000
FLUE_DB_TOKEN=<from secrets manager>
AGENT_MODEL_PROVIDER=xiaomi
AGENT_MODEL_NAME=mimo-v2.5
AGENT_MODEL_API=openai-completions
AGENT_MODEL_BASE_URL=<cheaptricks gateway base url>
AGENT_MODEL_API_KEY=<cheaptricks api key>
AGENT_MODEL_CONTEXT_WINDOW=1048576
AGENT_MODEL_MAX_TOKENS=131072
GITEA_URL=https://git.openputer.com
GITEA_TOKEN=<gitea personal access token>
```
### Start local dev
```bash
bun run dev:tailscale:agents -- --port 3585 &
bun run dev:tailscale:web &
```
Both bind `0.0.0.0` so they are reachable over Tailscale from a phone.
Before testing locally, flip the Convex `SITE_URL`:
```bash
cd packages/backend && npx convex env set SITE_URL 'http://100.101.157.28:5173'
```
## Cheaptricks Staging `.env`
Located at `/workspace/code/.env` on the `cheaptricks` host.
```env
CONVEX_DEPLOYMENT=dev:befitting-dalmatian-161
CONVEX_URL=https://befitting-dalmatian-161.convex.cloud
CONVEX_SITE_URL=https://befitting-dalmatian-161.convex.site
SITE_URL=https://zopu.cheaptricks.puter.wtf
NATIVE_APP_URL=code://
VITE_CONVEX_URL=https://befitting-dalmatian-161.convex.cloud
VITE_CONVEX_SITE_URL=https://befitting-dalmatian-161.convex.site
VITE_FLUE_URL=https://zopu.cheaptricks.puter.wtf/api
VITE_ZOPU_SERVER_URL=https://zopu.cheaptricks.puter.wtf/api
PORT=3590
DAEMON_ID=local-macbook
DAEMON_NAME=Local MacBook
DAEMON_VERSION=0.0.0
DAEMON_HEARTBEAT_MS=15000
DAEMON_COMMAND_LEASE_MS=60000
FLUE_DB_TOKEN=<from secrets manager>
AGENT_MODEL_PROVIDER=xiaomi
AGENT_MODEL_NAME=mimo-v2.5
AGENT_MODEL_API=openai-completions
AGENT_MODEL_BASE_URL=https://ai.cheaptricks.puter.wtf/v1
AGENT_MODEL_API_KEY=<cheaptricks api key>
AGENT_MODEL_CONTEXT_WINDOW=1048576
AGENT_MODEL_MAX_TOKENS=131072
GITEA_URL=https://git.openputer.com
GITEA_TOKEN=<gitea personal access token>
```
### Caddy config
File: `/etc/caddy/Caddyfile` on `cheaptricks`
```
zopu.cheaptricks.puter.wtf {
bind 135.181.82.179 2a01:4f9:c013:4a64::1
encode zstd gzip
# Must precede the generic /api route: preserves /api/auth/* and cookies.
handle /api/auth/* {
reverse_proxy https://befitting-dalmatian-161.convex.site {
header_up Host befitting-dalmatian-161.convex.site
}
}
handle_path /api/* {
reverse_proxy 127.0.0.1:3585
}
handle {
reverse_proxy 127.0.0.1:5173
}
}
```
`/api/auth/*` is the same-origin Better Auth proxy to Convex. The generic `/api/*` route serves Flue at port `3585`; it must be evaluated only after the authentication route.
Reload after changes:
```bash
sudo systemctl reload caddy
```
### Vite allowedHosts
`apps/web/vite.config.ts` must include `server: { allowedHosts: true }` or Vite rejects requests arriving through the Caddy domain.
### Start staging dev
```bash
ssh cheaptricks
export PATH=$PATH:/workspace/.bun/bin
cd /workspace/code
# Pull latest
git pull origin feat/web-integrarion
bun install
# Start both processes with nohup so they survive SSH disconnect
nohup bun run dev:tailscale:agents -- --port 3585 > /tmp/zopu-agents.log 2>&1 &
nohup bun run dev:tailscale:web > /tmp/zopu-web.log 2>&1 &
```
Before testing on staging, flip the Convex `SITE_URL`:
```bash
cd packages/backend && npx convex env set SITE_URL 'https://zopu.cheaptricks.puter.wtf'
```
### Verify staging
```bash
curl -sS -o /dev/null -w '%{http_code}\n' https://zopu.cheaptricks.puter.wtf/
# expect: 200
curl -sS -D - -o /dev/null \
'https://befitting-dalmatian-161.convex.site/api/auth/get-session' \
-H 'Origin: https://zopu.cheaptricks.puter.wtf' | grep access-control-allow-origin
# expect: access-control-allow-origin: https://zopu.cheaptricks.puter.wtf
```
## Model configuration
Both environments use the same model via the Cheaptricks AI gateway:
- Provider identity: `xiaomi` (Flue catalog maps this to MiMo multimodal metadata)
- Model: `mimo-v2.5`
- API protocol: `openai-completions`
- Context window: `1048576`
- Max output tokens: `131072`
- Multimodal: text + image input
The `AGENT_MODEL_BASE_URL` differs:
- Local Mac: uses the external gateway URL
- Cheaptricks: uses `https://ai.cheaptricks.puter.wtf/v1` (local to the box)
## Known gotchas
1. **Convex `SITE_URL` is single-valued and is the OAuth callback origin.** Switch it together with the GitHub OAuth App's single callback URL when moving between local and staging. Running both origins concurrently requires separate Convex deployments and OAuth Apps.
2. **Vite blocks unknown hosts by default.** Caddy domain must be allowed via `server.allowedHosts` in `apps/web/vite.config.ts`.
3. **Flue port changed from 3583 to 3585.** The old Caddy config pointed at 3583/13100. Current ports are 3585 (Flue) and 5173 (web).
4. **`.env` is gitignored.** Each machine maintains its own copy. The repo ships `.env.example` as the template.
5. **Convex CLI auth is per-machine.** Run `npx convex dev` once inside `packages/backend` on each new machine to authenticate the CLI.

View File

@@ -1,445 +0,0 @@
# Zopu Work OS — Development Plan
> **Related:** `slices.md` defines product increments; `dev-loop.md` defines the normative delivery loop; `evaluation.md` defines promotion gates.
> **Purpose:** concrete implementation order for the repository.
> **Strategy:** one vertical product loop, contracts first, provider adapters second, generalization last.
## 1. Target demonstration
Use one real repository and one deterministic Work:
> Add `GET /health` returning `{ status: "ok", commit: "<sha>" }`, with automated and live HTTP verification, then create a verified PR.
The first release is successful only when the full path works from chat to exact PR evidence.
## 2. Engineering rules
1. Land domain contracts before parallel adapter/UI work.
2. Keep FLUE, Rivet, Cube, AgentOS, and harness types behind ports.
3. One side effect has one idempotency key.
4. One mutating attempt owns one worktree.
5. Every state transition has a command, event, actor, timestamp, and evidence reference.
6. Every async process has timeout, cancellation, retry policy, and terminal classification.
7. Builder output is candidate output until independent verification passes.
8. Do not generalize until two real use cases require it.
9. Do not add an agent role without an evaluation showing quality gain.
10. Keep manual merge/deploy gates initially.
## 3. Repository workstreams
Recommended logical ownership:
```text
A. Domain/actors
B. FLUE definition/design agents
C. Resolver/runtime/harness
D. Verification/Git
E. Web experience
F. Deployment/operations
```
Only parallelize after shared schemas and event contracts are merged.
## 4. Phase 0 — Baseline and contracts
### Deliverables
- repository architecture inventory;
- one canonical glossary;
- schemas for Message, Signal, Work, WorkDefinition, DesignPacket, VerticalSlice, Run, Attempt, Artifact, Question;
- Work/Attempt state machines;
- command/event list;
- port interfaces;
- static `CodingKitV0`;
- deterministic fixture project/task.
### Tests
- schema round trips;
- state transition property/table tests;
- idempotency tests;
- fake adapters;
- actor restart/replay tests.
### Exit gate
A fake end-to-end test can create Work, approve definition/design, run fake slices, verify, and produce a fake PR artifact.
## 5. Milestone A — Work exists (Slices 12)
### Issue order
1. Persist project messages.
2. Implement `ProcessMessage`.
3. FLUE structured Signal/Work proposal.
4. Signal fingerprinting and attach/create rules.
5. WorkActor/projection.
6. Reactive Work card.
7. Work Definition schema/versioning.
8. Definition compiler agent.
9. definition edit/approval/questions UI.
10. approval invalidation tests.
### Release gate
```text
message → exact Signal → proposed card → approved Work Definition
```
No runtime integration.
## 6. Milestone B — Work is executable (Slices 35)
### Issue order
1. ImpactMap/DesignPacket schemas.
2. design compiler and slice planner.
3. design review/version UI.
4. Resolver decision function as pure domain logic.
5. WorkActor resolver commands/events.
6. FakeHarness/FakeSandbox adapters.
7. AttemptActor lifecycle.
8. CubeSandbox adapter.
9. selected harness adapter (OMP first unless spike rejects it).
10. Git worktree preparation.
11. normalized activity stream.
12. cancellation/timeout/cleanup.
### Contract freeze before adapter work
```text
HarnessEvent
SandboxLease
AttemptInput/Outcome
Artifact metadata
```
### Release gate
```text
approved design → one real slice → isolated repository changes
```
No PR claim yet.
## 7. Milestone C — Work is trustworthy (Slices 67)
### Issue order
1. VerificationPlan schema.
2. command-check runner.
3. HTTP-check runner.
4. revision/environment binding.
5. verifier role/process.
6. repair-attempt loop.
7. design-conformance evidence.
8. Git commit/push adapter.
9. PR creation adapter with idempotency.
10. review-package generator/UI.
11. exact-SHA invariant tests.
### Required quality fixture
For the health task, prove:
```text
new test fails on base
candidate passes focused tests
service starts
GET /health returns expected body
candidate SHA equals PR head SHA
```
### Release gate
A human can review and merge a real verified PR without reading raw agent logs.
## 8. Milestone D — Work is steerable (Slice 8)
### Issue order
1. Question/Decision lifecycle.
2. harness permission/question normalization.
3. attention UI.
4. contextual answer routing.
5. same-session resume and restarted-session recovery.
6. stale-question/version protections.
### Release gate
A deliberate ambiguity blocks Work, receives an answer, and resumes without duplicate execution.
## 9. Milestone E — Work survives complexity (Slices 910)
### Issue order
1. multi-slice commit model.
2. IntegrationActor/use case.
3. conflict/overlap analysis.
4. integrated verification.
5. preview adapter.
6. release/rollback policy.
7. observation and WorkResult.
8. incident/result-to-Signal loop.
### Release gate
Two slices pass independently, integrate on one SHA, publish preview, and produce an observed result.
## 10. Milestone F — System improvement (Slices 1112)
### Issue order
1. post-run evaluation schema.
2. learning synthesis.
3. knowledge proposal/diff workflow.
4. Kit registry/versioning.
5. Kit compiler.
6. tool/skill registry.
7. role evaluation harness.
8. controlled parallel fleet.
9. policy fallback to static Kit.
### Release gate
A completed run proposes a reviewable Kit/knowledge improvement, and a second run demonstrably benefits.
## 11. Actor rollout
### Initial
```text
ProjectActor
WorkActor
AttemptActor
```
WorkActor contains Resolver state and verification orchestration initially.
### Split only when justified
```text
VerificationActor — independent check lifecycle becomes complex
IntegrationActor — multi-branch/slice composition exists
ResultActor — deployment observation exists
ResolverActor — scheduling lifecycle needs independent ownership
```
Avoid actor-per-record designs.
## 12. Test architecture
### Domain
- transition tables;
- invariants;
- retry/budget logic;
- dependency graph readiness;
- approval/version invalidation;
- completion rules.
### Application
- command → event → projection;
- idempotent side effects;
- crash/restart recovery;
- timeout/cancellation;
- stale command rejection.
### Adapter contract tests
Run same suite against fake and live adapters:
```text
HarnessRuntime
SandboxRuntime
SourceControl
ArtifactStore
```
### End-to-end
Maintain fixtures:
```text
happy path
casual message/no Work
duplicate message
definition revision
design rejection
harness transient failure
human question/resume
verification repair
retry exhaustion
PR duplicate callback
actor restart mid-attempt
cancellation
```
### Quality evaluation
Before adding a new model/role/Kit, compare:
- success rate;
- escaped defect rate;
- review changes requested;
- retries;
- human attention;
- cost/time;
- design deviation.
## 13. Operational rollout
### Local
- fake adapters;
- local Rivet/AgentOS;
- one local repository fixture.
### Internal VPS
- deployed Rivet actors;
- Bun/Effect daemon;
- private Cube API;
- one OMP template;
- test forge repository.
### Dogfood repository
- real branch/PR;
- manual review;
- no production deploy;
- collect every failure.
### Controlled release
- one project/user;
- quotas;
- kill switch;
- audit trail;
- explicit external side-effect gates.
## 14. Security checklist before real repositories
```text
private control-plane networking
Cube/Rivet authentication
short-lived Git/model credentials
sandbox egress policy
no host HOME mounts
tool allowlist
artifact authorization
secret redaction
attempt timeout/cleanup
manual merge/deploy
audit approvals
```
## 15. Branch/PR sequencing
Suggested integration branch:
```text
dogfood/v0
```
Per-slice branches:
```text
dogfood/s01-work
dogfood/s02-definition
...
```
Within a slice, parallel branches may cover:
```text
contracts/domain
backend/actor
frontend
adapter
tests
```
Merge order:
```text
contracts → domain/actor → adapters/UI → integration tests
```
Every branch must target the current slice integration branch, not independently invent shared schemas.
## 16. First backlog
Execute exactly in this order:
```text
01 glossary + schemas
02 Work/Attempt state machines
03 commands/events/idempotency
04 fake E2E harness
05 message persistence
06 Signal extraction
07 Work card
08 Work Definition
09 definition approval
10 Design Packet
11 vertical-slice planner
12 design approval
13 Resolver with fake harness
14 AttemptActor
15 CubeSandbox adapter
16 OMP adapter/spike
17 real repository mutation
18 VerificationRuntime
19 repair loop
20 Git commit/push/PR
21 review package
22 contextual question/resume
23 integration verification
24 preview/release/result
25 learning proposals
26 dynamic Kit Builder
```
## 17. First production-quality acceptance test
```text
Given:
- one configured project/repository;
- one actionable user message;
- no pre-existing Work.
When:
- Zopu processes the message;
- the user approves definition/design;
- the Resolver executes all slices;
- verification passes;
- publication is requested.
Then:
- one Signal and one Work exist;
- exact provenance is preserved;
- every attempt has a terminal outcome;
- one verified candidate SHA exists;
- all required checks bind to that SHA;
- one PR exists at that SHA;
- the Work card explains intent, design, changes, evidence, and next action;
- actor/process restarts produce no duplicate Work, attempts, commits, or PRs.
```
## 18. Stop conditions
Pause feature expansion when any is true:
- stale “running” Work exists;
- duplicate external side effects occur;
- verification cannot identify exact SHA/environment;
- human cannot understand why a Work is “done”;
- retries are unbounded;
- agents bypass actor/application commands;
- provider-specific assumptions leak into domain;
- new roles add cost without measured quality improvement.
Fix the completion system before adding more autonomy.

File diff suppressed because it is too large Load Diff

View File

@@ -1,65 +0,0 @@
{
"read_order": [
"agent-context.md",
"product.md",
"glossary.md",
"dev-loop.md",
"tech.md",
"design.md",
"slices.md",
"dev-plan.md",
"evaluation.md"
],
"files": {
"README.md": {
"bytes": 1957,
"lines": 57,
"sha256": "9f732b8c40e1714767bbf31ef8ea6263c64b572775555f00527d775c6204f9c2"
},
"agent-context.md": {
"bytes": 7930,
"lines": 328,
"sha256": "6f2e97bc7e2606e84792391beb2d5bb8f4c23a720c277e4b22976cab2cfaf730"
},
"design.md": {
"bytes": 8470,
"lines": 469,
"sha256": "4d42edbcf6f723b2e845350b5b99925b78fc2fa09783d726f02fec5389bf0a9c"
},
"dev-loop.md": {
"bytes": 15737,
"lines": 705,
"sha256": "3fe99dcbf05cecfd2403b289fd9d0545372309e41c02973875739ace5822eb04"
},
"dev-plan.md": {
"bytes": 10138,
"lines": 445,
"sha256": "4e1e68fe91a9f442c1f662d133a27efb9c74223ed92f2f2634cff2aa36d8e429"
},
"evaluation.md": {
"bytes": 8310,
"lines": 405,
"sha256": "22037e62ae0b7abd1b639c3bd0b6e1a19328b919dd6a7c3fc4a1b7a62c9cac7e"
},
"glossary.md": {
"bytes": 3800,
"lines": 44,
"sha256": "79abd47b212d3b3c160238f703360a841090da6c9787b9c9f8871e14588aef74"
},
"product.md": {
"bytes": 10423,
"lines": 422,
"sha256": "020e1b7dd00be90aa1061f1ab2cd1a0416f48735c281a079b1e995dc333c5b75"
},
"slices.md": {
"bytes": 10132,
"lines": 493,
"sha256": "7ee7500685e5789f2aebf29d6a3b7d14866edc7c0f85b2f2c727edb669e66b00"
},
"tech.md": {
"bytes": 15349,
"lines": 663,
"sha256": "bcef9fdd0166ead4ff9f1c898016098871089680956de79e990e66d5d472e4d8"
}
}
}

View File

@@ -1,498 +0,0 @@
# Zopu Work OS — Vertical Slices
> **Related:** `dev-loop.md` defines the software-building process; `dev-plan.md` defines implementation sequencing.
> **Purpose:** ordered product increments. Complete each slice end-to-end before starting the next.
> **Reference task:** “Add `GET /health` returning status and current build commit.”
> **Rule:** every slice must produce visible product behavior, durable state, and automated acceptance coverage.
## Global definition of done
For every slice:
- schema/state migrations applied;
- commands/events are idempotent;
- actor restart does not corrupt state;
- frontend handles loading/error/empty states;
- important actions are audited;
- unit/integration tests cover invariants;
- no provider-specific types leak into domain;
- docs updated when contracts change.
## Slice 1 — Conversation → Signal → Work card
### User outcome
A user message creates one actionable Signal and one proposed Work card with exact provenance.
### Backend
```text
Message
Signal
Work
WorkEvent
ProcessMessage → structured FLUE proposal → validated command
```
Implement:
- persist exact message;
- Signal fingerprint/idempotency;
- create vs attach decision;
- proposed Work state;
- Work event feed.
### Frontend
- continuous chat;
- inline Work creation notice;
- collapsed reactive Work card;
- source-message link.
### Acceptance
- duplicate processing creates no duplicate Signal/Work;
- casual message creates no Work;
- actionable message creates one Work;
- card survives refresh/restart;
- exact source text is recoverable.
### Explicitly exclude
Planning, sandboxes, Git, verification.
---
## Slice 2 — Work Definition and approval
### User outcome
The proposed Work becomes a testable outcome contract that can be edited and approved.
### Backend
Add:
```text
WorkDefinition(versioned)
DefinitionApproval
Question
RiskClass
```
FLUE compiles:
- problem;
- desired outcome;
- scope/non-goals;
- acceptance criteria;
- assumptions/questions;
- risk.
Work state:
```text
Proposed → Defining → AwaitingDefinitionApproval → Designing
```
### Frontend
Expanded Outcome section; edit/approve/request revision; unresolved-question cards.
### Acceptance
- high-impact unresolved question blocks approval;
- approval binds exact version;
- revision invalidates stale approval;
- risk lane is visible;
- definition can be reconstructed from event history.
---
## Slice 3 — Design Packet and vertical slices
### User outcome
The user can review expected architecture/code shape and approve a bounded slice plan before coding.
### Backend
Add versioned:
```text
ImpactMap
DesignPacket
VerticalSlice
VerificationPlan
DesignApproval
```
Required Design Packet fields:
- affected systems/files;
- architecture summary;
- expected file-tree/call-flow changes;
- key types/invariants;
- risks/trade-offs;
- 14 vertical slices;
- evidence requirements per slice.
### Frontend
Design section with diagram, file tree, call flow, slices, version diff, approval.
### Acceptance
- horizontal “backend/frontend/test later” plan rejected;
- each slice is independently observable/verifiable;
- approval binds definition+design versions;
- changed definition invalidates dependent design;
- Work reaches `Ready`.
---
## Slice 4 — Resolver state machine with fake harness
### User outcome
Starting Work visibly advances slices, retries bounded failures, and surfaces blockers without real code execution.
### Backend
Add:
```text
Run
Attempt
ResolverDecision
KitVersion
HarnessRuntime port
FakeHarnessLive
```
Static `CodingKitV0`.
Implement durable loop:
```text
Ready → ExecutingSlice → VerifyingSlice → NextSlice
```
Attempt outcomes:
```text
Succeeded | RetryableFailure | NeedsInput | Blocked
| VerificationFailed | BudgetExhausted | Cancelled | PermanentFailure
```
### Frontend
Slice progress, meaningful activity, retry count, stop/retry controls, question state.
### Acceptance
- injected transient failure retries up to policy;
- restart resumes from durable state;
- no executable step produces explicit failure;
- cancellation terminates attempt;
- no Work remains permanently “running.”
---
## Slice 5 — Real sandbox + implementation harness
### User outcome
Zopu implements one approved slice in an isolated repository environment and streams useful progress.
### Backend
Initial adapter choice:
```text
SandboxRuntime = AgentOsSandboxLive
HarnessRuntime = CodexHarnessLive
Durable orchestration = Convex Workflow
```
Flow:
```text
load the project's authenticated Git connection
→ start durable Convex workflow
→ create AgentOS execution environment
→ clone the single configured repo
→ inject context
→ run one slice
→ normalize events
→ collect diff/artifacts
→ pause/terminate
```
Security:
- GitHub OAuth or self-hosted Gitea PAT, scoped to one project;
- scoped Git/model tokens passed only to private execution;
- isolated HOME/worktree;
- one mutating attempt per worktree;
- timeout/cancel cleanup.
### Frontend
Project selector/settings, Git connection status, current activity, changed files, artifact links, expandable raw logs, cancel/retry, and manual Git delivery controls.
### Acceptance
- real repository file changes occur;
- another Work cannot see/modify checkout;
- cancellation stops process;
- provider failure becomes classified attempt outcome;
- exact base/candidate revision recorded.
Rivet Engine coordinates AgentOS actor placement through a normal runner, but does not own product orchestration; Convex Workflow remains canonical for the Run lifecycle. Cube/Kubernetes sandbox support remains behind `SandboxRuntime` and can be mounted through AgentOS incrementally.
---
## Slice 6 — Independent verification and repair
### User outcome
The card shows objective evidence; failed checks trigger a bounded repair loop.
### Backend
Implement `VerificationRuntime` and verifier role.
Initial checks:
```text
format/lint/typecheck
focused tests
service start
HTTP behavior
secret scan
expected vs actual files/interfaces
test weakening/deletion detection
```
Bind result to candidate SHA/environment.
Repair:
```text
failure evidence → repair attempt → clean rerun
```
### Frontend
Evidence checklist, exact failures, candidate SHA, repair status.
### Acceptance
- implementer cannot self-mark passed;
- failed check stores output/exit code;
- repair receives exact evidence;
- max attempts enforced;
- final verdict is Passed/Failed/Inconclusive.
---
## Slice 7 — Git publication and review package
### User outcome
A verified candidate becomes a real branch/commit/PR with an understandable review package.
### Backend
`SourceControl` adapter:
```text
commit → push → create PR
```
Idempotent artifact creation; verify PR head SHA equals verified SHA.
Generate review package:
- intent/definition/design;
- slice narrative;
- important diffs;
- screenshots/API evidence;
- checks;
- deviations/risks.
### Frontend
Delivery section, PR action, narrative review package.
### Acceptance
- no duplicate PR after retry;
- exact verified SHA published;
- PR link survives restart;
- merge remains human-controlled;
- request-changes action returns Work to appropriate state.
**Milestone:** first useful product.
---
## Slice 8 — Contextual human intervention
### User outcome
A blocked agent asks one precise question; the user answers from the Work card; the same Work resumes.
### Backend
Durable `Question`/`Decision`; map response to Work/slice/attempt/harness session.
### Frontend
Attention card with recommendation, alternatives, consequences; contextual composer.
### Acceptance
- answer persists before resume;
- harness restart does not lose decision;
- stale questions cannot mutate newer plan;
- response never creates unrelated thread;
- attention queue orders blockers correctly.
---
## Slice 9 — Multi-slice integration verification
### User outcome
Several passing slices are combined and tested as one exact candidate before PR readiness.
### Backend
Add `IntegrationActor`/use case:
```text
compose commits
detect overlap/conflict
rebase/resolve policy
run impacted checks on integrated SHA
```
### Frontend
Integration state, conflict blocker, combined evidence.
### Acceptance
- individually passing slices cannot skip combined checks;
- conflicts become explicit blockers;
- integrated SHA is the published SHA;
- failed integration can replan/repair.
---
## Slice 10 — Preview, release, and observation
### User outcome
The user can inspect a preview, approve delivery, and see whether the released behavior works.
### Backend
Provider-neutral `PreviewRuntime`/release adapter. Add:
```text
RolloutPlan
RollbackPlan
HealthSignal
ObservationWindow
WorkResult
```
### Frontend
Preview link, release gate, health state, expected-vs-actual result.
### Acceptance
- preview binds candidate SHA;
- release requires policy-defined approval;
- observation records actual behavior;
- rollback trigger is explicit for critical work;
- merged PR alone does not mark outcome achieved.
---
## Slice 11 — Learning and knowledge proposals
### User outcome
Completed Work produces reviewable improvements to project knowledge and execution policy.
### Backend
Synthesize:
- planning errors;
- missing context;
- useful/failing tools;
- escaped defects;
- Kit/test recommendations.
Create proposal artifacts, never direct canonical mutation.
### Frontend
Diffable learning cards: accept/edit/reject.
### Acceptance
- every proposal cites run/evidence;
- rejection is retained;
- accepted update is versioned;
- no autonomous rewrite of canonical docs.
---
## Slice 12 — Dynamic Kit Builder and controlled fleet
### User outcome
Zopu selects/composes the right roles, tools, runtime, and checks for different software Work while preserving policy.
### Backend
Kit compiler inputs:
```text
Work Definition + risk + Design Packet + project policy
+ runtime/tool registry + previous results
```
Outputs immutable/versioned `ExecutionKit`.
Add roles only with measurable value:
```text
investigator implementer verifier security-reviewer
browser-tester integration-coordinator
```
### Acceptance
- Kit is explainable/versioned;
- tool grants are least privilege;
- budgets enforced;
- role addition has evaluation evidence;
- dynamic tools are proposed/reviewed before trust;
- fallback static Kit remains available.
## Dependency graph
```text
1 → 2 → 3 → 4 → 5 → 6 → 7 → 8 → 9 → 10 → 11 → 12
```
Parallel engineering is allowed _within_ a slice after contracts land, but product release order remains sequential.

View File

@@ -205,24 +205,24 @@ Represents the Project-to-AgentOS binding.
```ts
interface ProjectRuntime {
projectId: ProjectId
organizationId: OrganizationId
provider: "agentos"
runtimeId?: string
vmId?: string
workspacePath: string
repositoryPath: string
projectId: ProjectId;
organizationId: OrganizationId;
provider: "agentos";
runtimeId?: string;
vmId?: string;
workspacePath: string;
repositoryPath: string;
status:
| "requested"
| "creating_vm"
| "cloning"
| "checking_repository"
| "ready"
| "failed"
repositoryCommit?: string
lastError?: RuntimeError
createdAt: number
updatedAt: number
| "failed";
repositoryCommit?: string;
lastError?: RuntimeError;
createdAt: number;
updatedAt: number;
}
```
@@ -246,17 +246,17 @@ Maps one Project to one Flue agent instance.
```ts
interface ConversationAgentBinding {
id: AgentId
organizationId: OrganizationId
userId: string
projectId: ProjectId
runtimeId: string
globalTimelineId: TimelineId
status: "creating" | "ready" | "busy" | "error" | "disabled"
flueConversationId?: string
lastEventId?: EventId
createdAt: number
updatedAt: number
id: AgentId;
organizationId: OrganizationId;
userId: string;
projectId: ProjectId;
runtimeId: string;
globalTimelineId: TimelineId;
status: "creating" | "ready" | "busy" | "error" | "disabled";
flueConversationId?: string;
lastEventId?: EventId;
createdAt: number;
updatedAt: number;
}
```
@@ -268,20 +268,20 @@ One request to deliver one Event to one agent.
```ts
interface AgentDispatch {
id: DispatchId
sourceEventId: EventId
organizationId: OrganizationId
projectId: ProjectId
agentId: AgentId
workId?: WorkId
threadId?: ThreadId
status: "queued" | "sending" | "accepted" | "completed" | "failed"
attempt: number
handlerVersion: number
idempotencyKey: string
lastError?: string
createdAt: number
updatedAt: number
id: DispatchId;
sourceEventId: EventId;
organizationId: OrganizationId;
projectId: ProjectId;
agentId: AgentId;
workId?: WorkId;
threadId?: ThreadId;
status: "queued" | "sending" | "accepted" | "completed" | "failed";
attempt: number;
handlerVersion: number;
idempotencyKey: string;
lastError?: string;
createdAt: number;
updatedAt: number;
}
```
@@ -291,14 +291,14 @@ Represents an execution of a Flue specialist flow beneath a Thread.
```ts
interface FlowRun {
id: FlowRunId
flowType: "onboarding_explore" | "explore" | "issue"
flowVersion: number
projectId: ProjectId
workId?: WorkId
threadId?: ThreadId
sourceEventId: EventId
agentId: AgentId
id: FlowRunId;
flowType: "onboarding_explore" | "explore" | "issue";
flowVersion: number;
projectId: ProjectId;
workId?: WorkId;
threadId?: ThreadId;
sourceEventId: EventId;
agentId: AgentId;
status:
| "queued"
| "running"
@@ -306,12 +306,12 @@ interface FlowRun {
| "generating_artifact"
| "completed"
| "failed"
| "cancelled"
state?: unknown
attempt: number
startedAt?: number
finishedAt?: number
updatedAt: number
| "cancelled";
state?: unknown;
attempt: number;
startedAt?: number;
finishedAt?: number;
updatedAt: number;
}
```
@@ -327,17 +327,17 @@ summary.md
```ts
interface ProjectContextDocument {
id: ContextDocumentId
projectId: ProjectId
kind: "repository_summary" | "environment_manifest" | "other"
title: string
contentType: "text/markdown" | "application/json"
body?: string
storageId?: string
sourceFlowRunId?: FlowRunId
revision: number
status: "current" | "superseded" | "failed"
createdAt: number
id: ContextDocumentId;
projectId: ProjectId;
kind: "repository_summary" | "environment_manifest" | "other";
title: string;
contentType: "text/markdown" | "application/json";
body?: string;
storageId?: string;
sourceFlowRunId?: FlowRunId;
revision: number;
status: "current" | "superseded" | "failed";
createdAt: number;
}
```
@@ -347,20 +347,26 @@ Tracks the generation and publication of a static artifact.
```ts
interface ArtifactBuild {
id: ArtifactBuildId
projectId: ProjectId
workId?: WorkId
threadId?: ThreadId
flowRunId: FlowRunId
artifactKind: "project_setup" | "exploration" | "issue"
status: "drafting" | "rendering" | "validating" | "uploading" | "published" | "failed"
sourceManifest?: unknown
outputStorageId?: string
artifactId?: ArtifactId
attempt: number
lastError?: string
createdAt: number
updatedAt: number
id: ArtifactBuildId;
projectId: ProjectId;
workId?: WorkId;
threadId?: ThreadId;
flowRunId: FlowRunId;
artifactKind: "project_setup" | "exploration" | "issue";
status:
| "drafting"
| "rendering"
| "validating"
| "uploading"
| "published"
| "failed";
sourceManifest?: unknown;
outputStorageId?: string;
artifactId?: ArtifactId;
attempt: number;
lastError?: string;
createdAt: number;
updatedAt: number;
}
```
@@ -382,11 +388,11 @@ Input:
```ts
interface CreateProjectInput {
repositoryConnectionId: string
repositoryOwner: string
repositoryName: string
repositoryUrl: string
defaultBranch: string
repositoryConnectionId: string;
repositoryOwner: string;
repositoryName: string;
repositoryUrl: string;
defaultBranch: string;
}
```
@@ -418,9 +424,9 @@ Required result:
```ts
{
runtimeId: string
vmId: string
workspacePath: string
runtimeId: string;
vmId: string;
workspacePath: string;
}
```
@@ -609,14 +615,14 @@ load_source_event
```ts
interface ConversationEventInput {
dispatchId: DispatchId
sourceEvent: EventEnvelope
agentId: AgentId
organizationId: OrganizationId
projectId: ProjectId
globalTimelineId: TimelineId
workId?: WorkId
threadId?: ThreadId
dispatchId: DispatchId;
sourceEvent: EventEnvelope;
agentId: AgentId;
organizationId: OrganizationId;
projectId: ProjectId;
globalTimelineId: TimelineId;
workId?: WorkId;
threadId?: ThreadId;
}
```
@@ -645,28 +651,28 @@ Suggested tool surface:
```ts
interface TimelineTool {
postMessage(input: {
text: string
replyToEventId?: EventId
workId?: WorkId
importance?: "normal" | "high"
idempotencyKey: string
}): Promise<{ eventId: EventId }>
text: string;
replyToEventId?: EventId;
workId?: WorkId;
importance?: "normal" | "high";
idempotencyKey: string;
}): Promise<{ eventId: EventId }>;
postUpdate(input: {
text: string
workId: WorkId
threadId?: ThreadId
status?: "working" | "blocked" | "waiting" | "done"
idempotencyKey: string
}): Promise<{ eventId: EventId }>
text: string;
workId: WorkId;
threadId?: ThreadId;
status?: "working" | "blocked" | "waiting" | "done";
idempotencyKey: string;
}): Promise<{ eventId: EventId }>;
publishArtifact(input: {
artifactId: ArtifactId
workId?: WorkId
threadId?: ThreadId
text?: string
idempotencyKey: string
}): Promise<{ eventId: EventId }>
artifactId: ArtifactId;
workId?: WorkId;
threadId?: ThreadId;
text?: string;
idempotencyKey: string;
}): Promise<{ eventId: EventId }>;
}
```
@@ -884,12 +890,12 @@ Purpose:
```ts
interface OnboardingExploreInput {
projectId: ProjectId
runtimeId: string
repositoryPath: string
repositoryCommit: string
environmentVariableNames: string[]
sourceEventId: EventId
projectId: ProjectId;
runtimeId: string;
repositoryPath: string;
repositoryCommit: string;
environmentVariableNames: string[];
sourceEventId: EventId;
}
```
@@ -964,26 +970,26 @@ frame_question
```ts
interface ExplorationResult {
question: string
summary: string
question: string;
summary: string;
findings: Array<{
title: string
detail: string
title: string;
detail: string;
evidence: Array<{
path: string
lineRange?: string
note: string
}>
}>
path: string;
lineRange?: string;
note: string;
}>;
}>;
systemMap?: Array<{
from: string
to: string
relationship: string
}>
risks: string[]
unknowns: string[]
suggestedNextActions: string[]
generatedFiles: string[]
from: string;
to: string;
relationship: string;
}>;
risks: string[];
unknowns: string[];
suggestedNextActions: string[];
generatedFiles: string[];
}
```
@@ -1030,21 +1036,21 @@ understand_request
```ts
interface IssueResult {
title: string
summary: string
currentWorld: string
desiredWorld: string
scope: string[]
nonGoals: string[]
acceptanceCriteria: string[]
title: string;
summary: string;
currentWorld: string;
desiredWorld: string;
scope: string[];
nonGoals: string[];
acceptanceCriteria: string[];
evidence: Array<{
path?: string
note: string
}>
risks: string[]
openQuestions: string[]
suggestedImplementationShape?: string[]
generatedFiles: string[]
path?: string;
note: string;
}>;
risks: string[];
openQuestions: string[];
suggestedImplementationShape?: string[];
generatedFiles: string[];
}
```
@@ -1098,30 +1104,30 @@ Every specialist must produce a typed manifest before HTML generation.
```ts
interface ArtifactManifest {
kind: "project_setup" | "exploration" | "issue"
title: string
summary: string
projectId: ProjectId
workId?: WorkId
threadId?: ThreadId
flowRunId: FlowRunId
sourceEventIds: EventId[]
kind: "project_setup" | "exploration" | "issue";
title: string;
summary: string;
projectId: ProjectId;
workId?: WorkId;
threadId?: ThreadId;
flowRunId: FlowRunId;
sourceEventIds: EventId[];
card: {
label: string
facts: Array<{ label: string; value: string }>
label: string;
facts: Array<{ label: string; value: string }>;
actions?: Array<{
id: string
label: string
eventType: "client.action"
style?: "primary" | "secondary"
}>
}
sections: unknown[]
id: string;
label: string;
eventType: "client.action";
style?: "primary" | "secondary";
}>;
};
sections: unknown[];
evidence: Array<{
path?: string
note: string
}>
generatedFiles: string[]
path?: string;
note: string;
}>;
generatedFiles: string[];
}
```
@@ -1860,4 +1866,3 @@ Repository selected
→ every meaningful result becomes a polished artifact
→ Convex publishes the result back into the timeline
```

View File

@@ -557,7 +557,7 @@ Use one shared envelope for every event.
```ts
export type TimelineScope =
| { kind: "global"; organizationId: Id<"organizations"> }
| { kind: "work"; organizationId: Id<"organizations">; workId: Id<"works"> }
| { kind: "work"; organizationId: Id<"organizations">; workId: Id<"works"> };
export type Actor =
| { kind: "user"; userId: string }
@@ -565,26 +565,26 @@ export type Actor =
| { kind: "work"; workId: Id<"works"> }
| { kind: "thread"; threadId: Id<"threads"> }
| { kind: "tool"; tool: string }
| { kind: "integration"; integration: string }
| { kind: "integration"; integration: string };
export interface EventEnvelope<TType extends string, TPayload> {
type: TType
actor: Actor
scope: TimelineScope
payload: TPayload
type: TType;
actor: Actor;
scope: TimelineScope;
payload: TPayload;
projectId?: Id<"projects">
workId?: Id<"works">
threadId?: Id<"threads">
runId?: string
projectId?: Id<"projects">;
workId?: Id<"works">;
threadId?: Id<"threads">;
runId?: string;
causationId?: Id<"events">
correlationId: string
replyToEventId?: Id<"events">
artifactIds?: Id<"artifacts">[]
causationId?: Id<"events">;
correlationId: string;
replyToEventId?: Id<"events">;
artifactIds?: Id<"artifacts">[];
idempotencyKey?: string
occurredAt: number
idempotencyKey?: string;
occurredAt: number;
}
```
@@ -643,10 +643,7 @@ artifact.failed
Every event should have a visibility classification:
```ts
type EventVisibility =
| "timeline"
| "compact"
| "internal"
type EventVisibility = "timeline" | "compact" | "internal";
```
- `timeline`: render as a full timeline item
@@ -682,22 +679,22 @@ export type ArtifactKind =
| "review"
| "verification"
| "blocker"
| "report"
| "report";
export interface ArtifactCard {
component: "summary" | "setup" | "plan" | "preview" | "blocker"
label: string
title: string
summary: string
status: "working" | "ready" | "blocked" | "failed" | "superseded"
facts?: Array<{ label: string; value: string }>
component: "summary" | "setup" | "plan" | "preview" | "blocker";
label: string;
title: string;
summary: string;
status: "working" | "ready" | "blocked" | "failed" | "superseded";
facts?: Array<{ label: string; value: string }>;
actions?: Array<{
id: string
label: string
style?: "primary" | "secondary" | "danger"
emits: "client.action"
payload?: unknown
}>
id: string;
label: string;
style?: "primary" | "secondary" | "danger";
emits: "client.action";
payload?: unknown;
}>;
}
export type ArtifactContent =
@@ -705,7 +702,7 @@ export type ArtifactContent =
| { type: "html"; storageId: Id<"_storage">; sandboxed: true }
| { type: "file"; storageId: Id<"_storage">; mimeType: string }
| { type: "external"; url: string }
| { type: "structured"; schema: string; data: unknown }
| { type: "structured"; schema: string; data: unknown };
```
Do not initially allow arbitrary generated JavaScript inside timeline cards.
@@ -1201,7 +1198,7 @@ const eventRenderers = {
"thread.started": CompactSystemEvent,
"thread.completed": CompactSystemEvent,
"artifact.published": ArtifactMessage,
}
};
```
Unknown event types must degrade safely to a generic system event during development.
@@ -1472,7 +1469,6 @@ Events preserve history.
Artifacts communicate results.
```
---
# CONTINUATION — Intelligence & Delivery Runtime
@@ -1681,24 +1677,24 @@ Represents the Project-to-AgentOS binding.
```ts
interface ProjectRuntime {
projectId: ProjectId
organizationId: OrganizationId
provider: "agentos"
runtimeId?: string
vmId?: string
workspacePath: string
repositoryPath: string
projectId: ProjectId;
organizationId: OrganizationId;
provider: "agentos";
runtimeId?: string;
vmId?: string;
workspacePath: string;
repositoryPath: string;
status:
| "requested"
| "creating_vm"
| "cloning"
| "checking_repository"
| "ready"
| "failed"
repositoryCommit?: string
lastError?: RuntimeError
createdAt: number
updatedAt: number
| "failed";
repositoryCommit?: string;
lastError?: RuntimeError;
createdAt: number;
updatedAt: number;
}
```
@@ -1722,17 +1718,17 @@ Maps one Project to one Flue agent instance.
```ts
interface ConversationAgentBinding {
id: AgentId
organizationId: OrganizationId
userId: string
projectId: ProjectId
runtimeId: string
globalTimelineId: TimelineId
status: "creating" | "ready" | "busy" | "error" | "disabled"
flueConversationId?: string
lastEventId?: EventId
createdAt: number
updatedAt: number
id: AgentId;
organizationId: OrganizationId;
userId: string;
projectId: ProjectId;
runtimeId: string;
globalTimelineId: TimelineId;
status: "creating" | "ready" | "busy" | "error" | "disabled";
flueConversationId?: string;
lastEventId?: EventId;
createdAt: number;
updatedAt: number;
}
```
@@ -1744,20 +1740,20 @@ One request to deliver one Event to one agent.
```ts
interface AgentDispatch {
id: DispatchId
sourceEventId: EventId
organizationId: OrganizationId
projectId: ProjectId
agentId: AgentId
workId?: WorkId
threadId?: ThreadId
status: "queued" | "sending" | "accepted" | "completed" | "failed"
attempt: number
handlerVersion: number
idempotencyKey: string
lastError?: string
createdAt: number
updatedAt: number
id: DispatchId;
sourceEventId: EventId;
organizationId: OrganizationId;
projectId: ProjectId;
agentId: AgentId;
workId?: WorkId;
threadId?: ThreadId;
status: "queued" | "sending" | "accepted" | "completed" | "failed";
attempt: number;
handlerVersion: number;
idempotencyKey: string;
lastError?: string;
createdAt: number;
updatedAt: number;
}
```
@@ -1767,14 +1763,14 @@ Represents an execution of a Flue specialist flow beneath a Thread.
```ts
interface FlowRun {
id: FlowRunId
flowType: "onboarding_explore" | "explore" | "issue"
flowVersion: number
projectId: ProjectId
workId?: WorkId
threadId?: ThreadId
sourceEventId: EventId
agentId: AgentId
id: FlowRunId;
flowType: "onboarding_explore" | "explore" | "issue";
flowVersion: number;
projectId: ProjectId;
workId?: WorkId;
threadId?: ThreadId;
sourceEventId: EventId;
agentId: AgentId;
status:
| "queued"
| "running"
@@ -1782,12 +1778,12 @@ interface FlowRun {
| "generating_artifact"
| "completed"
| "failed"
| "cancelled"
state?: unknown
attempt: number
startedAt?: number
finishedAt?: number
updatedAt: number
| "cancelled";
state?: unknown;
attempt: number;
startedAt?: number;
finishedAt?: number;
updatedAt: number;
}
```
@@ -1803,17 +1799,17 @@ summary.md
```ts
interface ProjectContextDocument {
id: ContextDocumentId
projectId: ProjectId
kind: "repository_summary" | "environment_manifest" | "other"
title: string
contentType: "text/markdown" | "application/json"
body?: string
storageId?: string
sourceFlowRunId?: FlowRunId
revision: number
status: "current" | "superseded" | "failed"
createdAt: number
id: ContextDocumentId;
projectId: ProjectId;
kind: "repository_summary" | "environment_manifest" | "other";
title: string;
contentType: "text/markdown" | "application/json";
body?: string;
storageId?: string;
sourceFlowRunId?: FlowRunId;
revision: number;
status: "current" | "superseded" | "failed";
createdAt: number;
}
```
@@ -1823,20 +1819,26 @@ Tracks the generation and publication of a static artifact.
```ts
interface ArtifactBuild {
id: ArtifactBuildId
projectId: ProjectId
workId?: WorkId
threadId?: ThreadId
flowRunId: FlowRunId
artifactKind: "project_setup" | "exploration" | "issue"
status: "drafting" | "rendering" | "validating" | "uploading" | "published" | "failed"
sourceManifest?: unknown
outputStorageId?: string
artifactId?: ArtifactId
attempt: number
lastError?: string
createdAt: number
updatedAt: number
id: ArtifactBuildId;
projectId: ProjectId;
workId?: WorkId;
threadId?: ThreadId;
flowRunId: FlowRunId;
artifactKind: "project_setup" | "exploration" | "issue";
status:
| "drafting"
| "rendering"
| "validating"
| "uploading"
| "published"
| "failed";
sourceManifest?: unknown;
outputStorageId?: string;
artifactId?: ArtifactId;
attempt: number;
lastError?: string;
createdAt: number;
updatedAt: number;
}
```
@@ -1858,11 +1860,11 @@ Input:
```ts
interface CreateProjectInput {
repositoryConnectionId: string
repositoryOwner: string
repositoryName: string
repositoryUrl: string
defaultBranch: string
repositoryConnectionId: string;
repositoryOwner: string;
repositoryName: string;
repositoryUrl: string;
defaultBranch: string;
}
```
@@ -1894,9 +1896,9 @@ Required result:
```ts
{
runtimeId: string
vmId: string
workspacePath: string
runtimeId: string;
vmId: string;
workspacePath: string;
}
```
@@ -2085,14 +2087,14 @@ load_source_event
```ts
interface ConversationEventInput {
dispatchId: DispatchId
sourceEvent: EventEnvelope
agentId: AgentId
organizationId: OrganizationId
projectId: ProjectId
globalTimelineId: TimelineId
workId?: WorkId
threadId?: ThreadId
dispatchId: DispatchId;
sourceEvent: EventEnvelope;
agentId: AgentId;
organizationId: OrganizationId;
projectId: ProjectId;
globalTimelineId: TimelineId;
workId?: WorkId;
threadId?: ThreadId;
}
```
@@ -2121,28 +2123,28 @@ Suggested tool surface:
```ts
interface TimelineTool {
postMessage(input: {
text: string
replyToEventId?: EventId
workId?: WorkId
importance?: "normal" | "high"
idempotencyKey: string
}): Promise<{ eventId: EventId }>
text: string;
replyToEventId?: EventId;
workId?: WorkId;
importance?: "normal" | "high";
idempotencyKey: string;
}): Promise<{ eventId: EventId }>;
postUpdate(input: {
text: string
workId: WorkId
threadId?: ThreadId
status?: "working" | "blocked" | "waiting" | "done"
idempotencyKey: string
}): Promise<{ eventId: EventId }>
text: string;
workId: WorkId;
threadId?: ThreadId;
status?: "working" | "blocked" | "waiting" | "done";
idempotencyKey: string;
}): Promise<{ eventId: EventId }>;
publishArtifact(input: {
artifactId: ArtifactId
workId?: WorkId
threadId?: ThreadId
text?: string
idempotencyKey: string
}): Promise<{ eventId: EventId }>
artifactId: ArtifactId;
workId?: WorkId;
threadId?: ThreadId;
text?: string;
idempotencyKey: string;
}): Promise<{ eventId: EventId }>;
}
```
@@ -2360,12 +2362,12 @@ Purpose:
```ts
interface OnboardingExploreInput {
projectId: ProjectId
runtimeId: string
repositoryPath: string
repositoryCommit: string
environmentVariableNames: string[]
sourceEventId: EventId
projectId: ProjectId;
runtimeId: string;
repositoryPath: string;
repositoryCommit: string;
environmentVariableNames: string[];
sourceEventId: EventId;
}
```
@@ -2440,26 +2442,26 @@ frame_question
```ts
interface ExplorationResult {
question: string
summary: string
question: string;
summary: string;
findings: Array<{
title: string
detail: string
title: string;
detail: string;
evidence: Array<{
path: string
lineRange?: string
note: string
}>
}>
path: string;
lineRange?: string;
note: string;
}>;
}>;
systemMap?: Array<{
from: string
to: string
relationship: string
}>
risks: string[]
unknowns: string[]
suggestedNextActions: string[]
generatedFiles: string[]
from: string;
to: string;
relationship: string;
}>;
risks: string[];
unknowns: string[];
suggestedNextActions: string[];
generatedFiles: string[];
}
```
@@ -2506,21 +2508,21 @@ understand_request
```ts
interface IssueResult {
title: string
summary: string
currentWorld: string
desiredWorld: string
scope: string[]
nonGoals: string[]
acceptanceCriteria: string[]
title: string;
summary: string;
currentWorld: string;
desiredWorld: string;
scope: string[];
nonGoals: string[];
acceptanceCriteria: string[];
evidence: Array<{
path?: string
note: string
}>
risks: string[]
openQuestions: string[]
suggestedImplementationShape?: string[]
generatedFiles: string[]
path?: string;
note: string;
}>;
risks: string[];
openQuestions: string[];
suggestedImplementationShape?: string[];
generatedFiles: string[];
}
```
@@ -2574,30 +2576,30 @@ Every specialist must produce a typed manifest before HTML generation.
```ts
interface ArtifactManifest {
kind: "project_setup" | "exploration" | "issue"
title: string
summary: string
projectId: ProjectId
workId?: WorkId
threadId?: ThreadId
flowRunId: FlowRunId
sourceEventIds: EventId[]
kind: "project_setup" | "exploration" | "issue";
title: string;
summary: string;
projectId: ProjectId;
workId?: WorkId;
threadId?: ThreadId;
flowRunId: FlowRunId;
sourceEventIds: EventId[];
card: {
label: string
facts: Array<{ label: string; value: string }>
label: string;
facts: Array<{ label: string; value: string }>;
actions?: Array<{
id: string
label: string
eventType: "client.action"
style?: "primary" | "secondary"
}>
}
sections: unknown[]
id: string;
label: string;
eventType: "client.action";
style?: "primary" | "secondary";
}>;
};
sections: unknown[];
evidence: Array<{
path?: string
note: string
}>
generatedFiles: string[]
path?: string;
note: string;
}>;
generatedFiles: string[];
}
```
@@ -3336,4 +3338,3 @@ Repository selected
→ every meaningful result becomes a polished artifact
→ Convex publishes the result back into the timeline
```