feat: ship agent onboarding runtime
This commit is contained in:
@@ -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 19–47).
|
||||
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 environment’s Flue worker;
|
||||
- `FLUE_URL` / `AGENT_BACKEND_URL`, pointing at only that environment’s Flue worker (e.g. `https://agents.zopu.puter.wtf`);
|
||||
- `FLUE_DB_TOKEN` shared only with that environment’s 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.
|
||||
|
||||
Rivet’s 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. Pulumi’s 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; Vite’s 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, 10–11; `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/).
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 1–7 work reliably.
|
||||
676
docs/TECH.md
676
docs/TECH.md
@@ -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.
|
||||
@@ -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 agent’s “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
35
docs/agents/domain.md
Normal 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…_
|
||||
43
docs/agents/issue-tracker.md
Normal file
43
docs/agents/issue-tracker.md
Normal 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`.
|
||||
15
docs/agents/triage-labels.md
Normal file
15
docs/agents/triage-labels.md
Normal 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.
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
445
docs/dev-plan.md
445
docs/dev-plan.md
@@ -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 1–2)
|
||||
|
||||
### 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 3–5)
|
||||
|
||||
### 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 6–7)
|
||||
|
||||
### 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 9–10)
|
||||
|
||||
### 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 11–12)
|
||||
|
||||
### 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.
|
||||
1241
docs/dogfood-plan.md
1241
docs/dogfood-plan.md
File diff suppressed because it is too large
Load Diff
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
498
docs/slices.md
498
docs/slices.md
@@ -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;
|
||||
- 1–4 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.
|
||||
@@ -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
|
||||
```
|
||||
|
||||
|
||||
@@ -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
|
||||
```
|
||||
|
||||
|
||||
Reference in New Issue
Block a user