mirror of
https://github.com/harivansh-afk/sandbox-agent.git
synced 2026-04-15 07:04:48 +00:00
* Move Foundry HTTP APIs out of /api/rivet
* Move Foundry HTTP APIs onto /v1
* Fix Foundry Rivet base path and frontend endpoint fallback
* Configure Foundry Rivet runner pool for /v1
* Remove Foundry Rivet runner override
* Serve Foundry Rivet routes directly from Bun
* Log Foundry RivetKit deployment friction
* Add actor display metadata
* Tighten actor schema constraints
* Reset actor persistence baseline
* Remove temporary actor key version prefix
Railway has no persistent volumes so stale actors are wiped on
each deploy. The v2 key rotation is no longer needed.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Cache app workspace actor handle across requests
Every request was calling getOrCreate on the Rivet engine API
to resolve the workspace actor, even though it's always the same
actor. Cache the handle and invalidate on error so retries
re-resolve. This eliminates redundant cross-region round-trips
to api.rivet.dev on every request.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Add temporary debug logging to GitHub OAuth exchange
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Make squashed baseline migrations idempotent
Use CREATE TABLE IF NOT EXISTS and CREATE UNIQUE INDEX IF NOT
EXISTS so the squashed baseline can run against actors that
already have tables from the pre-squash migration sequence.
This fixes the "table already exists" error when org workspace
actors wake up with stale migration journals.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Revert "Make squashed baseline migrations idempotent"
This reverts commit 356c146035.
* Fix GitHub OAuth callback by removing retry wrapper
OAuth authorization codes are single-use. The appWorkspaceAction wrapper
retries failed calls up to 20 times, but if the code exchange succeeds
and a later step fails, every retry sends the already-consumed code,
producing "bad_verification_code" from GitHub.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Add runner versioning to RivetKit registry
Uses Date.now() so each process start gets a unique version.
This ensures Rivet Cloud migrates actors to the new runner on
deploy instead of routing requests to stale runners.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Add backend request and workspace logging
* Log callback request headers
* Make GitHub OAuth callback idempotent against duplicate requests
Clear oauthState before exchangeCode so duplicate callback requests
fail the state check instead of hitting GitHub with a consumed code.
Marked as HACK — root cause of duplicate HTTP requests is unknown.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Add temporary header dump on GitHub OAuth callback
Log all request headers on the callback endpoint to diagnose
the source of duplicate requests (Railway proxy, Cloudflare, browser).
Remove once root cause is identified.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Defer slow GitHub org sync to workflow queue for fast OAuth callback
Split syncGithubSessionFromToken into a fast path (initGithubSession:
exchange code, get viewer, store token+identity) and a slow path
(syncGithubOrganizations: list orgs/installations, sync workspaces).
completeAppGithubAuth now returns the 302 redirect in ~2s instead of
~18s by enqueuing the org sync to the workspace workflow queue
(fire-and-forget). This eliminates the proxy timeout window that was
causing duplicate callback requests.
bootstrapAppGithubSession (dev-only) still calls the full synchronous
sync since proxy timeouts are not a concern and it needs the session
fully populated before returning.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* foundry: async app repo import on org select
* foundry: parallelize app snapshot org reads
* repo: push all current workspace changes
* foundry: update runner version and snapshot logging
* Refactor Foundry GitHub state and sandbox runtime
Refactors Foundry around organization/repository ownership and adds an organization-scoped GitHub state actor plus a user-scoped GitHub auth actor, removing the old project PR/branch sync actors and repo PR cache.
Updates sandbox provisioning to rely on sandbox-agent for in-sandbox work, hardens Daytona startup and image-build behavior, and surfaces runtime and task-startup errors more clearly in the UI.
Extends workbench and GitHub state handling to track merged PR state, adds runtime-issue tracking, refreshes client/test/config wiring, and documents the main live Foundry test flow plus actor coordination rules.
Also updates the remaining Sandbox Agent install-version references in docs/examples to the current pinned minor channel.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
223 lines
15 KiB
Markdown
223 lines
15 KiB
Markdown
# Project Instructions
|
|
|
|
## Language Policy
|
|
|
|
Use TypeScript for all source code.
|
|
|
|
- Never add raw JavaScript source files (`.js`, `.mjs`, `.cjs`).
|
|
- Prefer `.ts`/`.tsx` for runtime code, scripts, tests, and tooling.
|
|
- If touching old JavaScript, migrate it to TypeScript instead of extending it.
|
|
|
|
## Monorepo + Tooling
|
|
|
|
Use `pnpm` workspaces and Turborepo.
|
|
|
|
- Workspace root uses `pnpm-workspace.yaml` and `turbo.json`.
|
|
- Packages live in `packages/*`.
|
|
- `core` is renamed to `shared`.
|
|
- `packages/cli` is disabled and excluded from active workspace validation.
|
|
- Integrations and providers live under `packages/backend/src/{integrations,providers}`.
|
|
|
|
## CLI Status
|
|
|
|
- `packages/cli` is fully disabled for active development.
|
|
- Do not implement new behavior in `packages/cli` unless explicitly requested.
|
|
- Frontend is the primary product surface; prioritize `packages/frontend` + supporting `packages/client`/`packages/backend`.
|
|
- Workspace `build`, `typecheck`, and `test` intentionally exclude `@sandbox-agent/foundry-cli`.
|
|
- `pnpm-workspace.yaml` excludes `packages/cli` from workspace package resolution.
|
|
|
|
## Common Commands
|
|
|
|
- Foundry is the canonical name for this product tree. Do not introduce or preserve legacy pre-Foundry naming in code, docs, commands, or runtime paths.
|
|
- Install deps: `pnpm install`
|
|
- Full active-workspace validation: `pnpm -w typecheck`, `pnpm -w build`, `pnpm -w test`
|
|
- Start the full dev stack: `just foundry-dev`
|
|
- Start the local production-build preview stack: `just foundry-preview`
|
|
- Start only the backend locally: `just foundry-backend-start`
|
|
- Start only the frontend locally: `pnpm --filter @sandbox-agent/foundry-frontend dev`
|
|
- Start the frontend against the mock workbench client: `FOUNDRY_FRONTEND_CLIENT_MODE=mock pnpm --filter @sandbox-agent/foundry-frontend dev`
|
|
- Stop the compose dev stack: `just foundry-dev-down`
|
|
- Tail compose logs: `just foundry-dev-logs`
|
|
- Stop the preview stack: `just foundry-preview-down`
|
|
- Tail preview logs: `just foundry-preview-logs`
|
|
|
|
## Railway Logs
|
|
|
|
- Production Foundry Railway logs can be read from a linked workspace with `railway logs --deployment --lines 200` or `railway logs <deployment-id> --deployment --lines 200`.
|
|
- Production deploys should go through `git push` to the deployment branch/workflow. Do not use `railway up` for Foundry deploys.
|
|
- If Railway logs fail because the workspace is not linked to the correct project/service/environment, run:
|
|
`railway link --project 33e3e2df-32c5-41c5-a4af-dca8654acb1d --environment cf387142-61fd-4668-8cf7-b3559e0983cb --service 91c7e450-d6d2-481a-b2a4-0a916f4160fc`
|
|
- That links this directory to the `sandbox-agent` project, `production` environment, and `foundry-api` service.
|
|
- Production proxy chain: `api.sandboxagent.dev` routes through Cloudflare → Fastly/Varnish → Railway. When debugging request duplication, timeouts, or retry behavior, check headers like `cf-ray`, `x-varnish`, `x-railway-edge`, and `cdn-loop` to identify which layer is involved.
|
|
|
|
## Frontend + Client Boundary
|
|
|
|
- Keep a browser-friendly GUI implementation aligned with the TUI interaction model wherever possible.
|
|
- Do not import `rivetkit` directly in CLI or GUI packages. RivetKit client access must stay isolated inside `packages/client`.
|
|
- All backend interaction (actor calls, metadata/health checks, backend HTTP endpoint access) must go through the dedicated client library in `packages/client`.
|
|
- Outside `packages/client`, do not call backend endpoints directly (for example `fetch(.../v1/rivet...)`), except in black-box E2E tests that intentionally exercise raw transport behavior.
|
|
- GUI state should update in realtime (no manual refresh buttons). Prefer RivetKit push reactivity and actor-driven events; do not add polling/refetch for normal product flows.
|
|
- Keep the mock workbench types and mock client in `packages/shared` + `packages/client` up to date with the frontend contract. The mock is the UI testing reference implementation while backend functionality catches up.
|
|
- Keep frontend route/state coverage current in code and tests; there is no separate page-inventory doc to maintain.
|
|
- If Foundry uses a shared component from `@sandbox-agent/react`, make changes in `sdks/react` instead of copying or forking that component into Foundry.
|
|
- When changing shared React components in `sdks/react` for Foundry, verify they still work in the Sandbox Agent Inspector before finishing.
|
|
- When making UI changes, verify the live flow with `agent-browser`, take screenshots of the updated UI, and offer to open those screenshots in Preview when you finish.
|
|
- When asked for screenshots, capture all relevant affected screens and modal states, not just a single viewport. Include empty, populated, success, and blocked/error states when they are part of the changed flow.
|
|
- If a screenshot catches a transition frame, blank modal, or otherwise misleading state, retake it before reporting it.
|
|
|
|
## Runtime Policy
|
|
|
|
- Runtime is Bun-native.
|
|
- Use Bun for CLI/backend execution paths and process spawning.
|
|
- Do not add Node compatibility fallbacks for OpenTUI/runtime execution.
|
|
|
|
## Defensive Error Handling
|
|
|
|
- Write code defensively: validate assumptions at boundaries and state transitions.
|
|
- If the system reaches an unexpected state, raise an explicit error with actionable context.
|
|
- Do not fail silently, swallow errors, or auto-ignore inconsistent data.
|
|
- Prefer fail-fast behavior over hidden degradation when correctness is uncertain.
|
|
|
|
## RivetKit Dependency Policy
|
|
|
|
For all Rivet/RivetKit implementation:
|
|
|
|
1. Use SQLite + Drizzle for persistent state.
|
|
2. SQLite is **per actor instance** (per actor key), not a shared backend-global database:
|
|
- Each actor instance gets its own SQLite DB.
|
|
- Schema design should assume a single actor instance owns the entire DB.
|
|
- Do not add `workspaceId`/`repoId`/`taskId` columns just to "namespace" rows for a given actor instance; use actor state and/or the actor key instead.
|
|
- Example: the `task` actor instance already represents `(workspaceId, repoId, taskId)`, so its SQLite tables should not need those columns for primary keys.
|
|
3. Do not use backend-global SQLite singletons; database access must go through actor `db` providers (`c.db`).
|
|
4. The default dependency source for RivetKit is the published `rivetkit` package so workspace installs and CI remain self-contained.
|
|
5. When working on coordinated RivetKit changes, you may temporarily relink to a local checkout instead of the published package.
|
|
- Dedicated local checkout for this workspace: `/Users/nathan/conductor/workspaces/task/rivet-checkout`
|
|
- Preferred local link target: `../rivet-checkout/rivetkit-typescript/packages/rivetkit`
|
|
- Sub-packages (`@rivetkit/sqlite-vfs`, etc.) resolve transitively from the RivetKit workspace when using the local checkout.
|
|
6. Before using a local checkout, build RivetKit in the rivet repo:
|
|
```bash
|
|
cd ../rivet-checkout/rivetkit-typescript
|
|
pnpm install
|
|
pnpm build -F rivetkit
|
|
```
|
|
|
|
## Rivet Routing
|
|
|
|
- Mount RivetKit directly on `/v1/rivet` via `registry.handler(c.req.raw)`.
|
|
- Do not add an extra proxy or manager-specific route layer in the backend.
|
|
- Let RivetKit own metadata/public endpoint behavior for `/v1/rivet`.
|
|
|
|
## Workspace + Actor Rules
|
|
|
|
- Everything is scoped to a workspace.
|
|
- Workspace resolution order: `--workspace` flag -> config default -> `"default"`.
|
|
- `ControlPlaneActor` is replaced by `WorkspaceActor` (workspace coordinator).
|
|
- Every actor key must be prefixed with workspace namespace (`["ws", workspaceId, ...]`).
|
|
- CLI/TUI/GUI must use `@sandbox-agent/foundry-client` (`packages/client`) for backend access; `rivetkit/client` imports are only allowed inside `packages/client`.
|
|
- Do not add custom backend REST endpoints (no `/v1/*` shim layer).
|
|
- We own the sandbox-agent project; treat sandbox-agent defects as first-party bugs and fix them instead of working around them.
|
|
- Keep strict single-writer ownership: each table/row has exactly one actor writer.
|
|
- Parent actors (`workspace`, `project`, `task`, `history`, `sandbox-instance`) use command-only loops with no timeout.
|
|
- Periodic syncing lives in dedicated child actors with one timeout cadence each.
|
|
- Do not build blocking flows that wait on external systems to become ready or complete. Prefer push-based progression driven by actor messages, events, webhooks, or queue/workflow state changes.
|
|
- Use workflows/background commands for any repo sync, sandbox provisioning, agent install, branch restack/rebase, or other multi-step external work. Do not keep user-facing actions/requests open while that work runs.
|
|
- `send` policy: always `await` the `send(...)` call itself so enqueue failures surface immediately, but default to `wait: false`.
|
|
- Only use `send(..., { wait: true })` for short, bounded mutations that should finish quickly and do not depend on external readiness, polling actors, provider setup, repo/network I/O, or long-running queue drains.
|
|
- Request/action contract: wait only until the minimum resource needed for the client's next step exists. Example: task creation may wait for task actor creation/identity, but not for sandbox provisioning or session bootstrap.
|
|
- Read paths must not force refresh/sync work inline. Serve the latest cached projection, mark staleness explicitly, and trigger background refresh separately when needed.
|
|
- If a workflow needs to resume after some external work completes, model that as workflow state plus follow-up messages/events instead of holding the original request open.
|
|
- Do not rely on retries for correctness or normal control flow. If a queue/workflow/external dependency is not ready yet, model that explicitly and resume from a push/event, instead of polling or retry loops.
|
|
- Actor handle policy:
|
|
- Prefer explicit `get` or explicit `create` based on workflow intent; do not default to `getOrCreate`.
|
|
- Use `get`/`getForId` when the actor is expected to already exist; if missing, surface an explicit `Actor not found` error with recovery context.
|
|
- Use create semantics only on explicit provisioning/create paths where creating a new actor instance is intended.
|
|
- `getOrCreate` is a last resort for create paths when an explicit create API is unavailable; never use it in read/command paths.
|
|
- For long-lived cross-actor links (for example sandbox/session runtime access), persist actor identity (`actorId`) and keep a fallback lookup path by actor id.
|
|
- Docker dev: `compose.dev.yaml` mounts a named volume at `/root/.local/share/foundry/repos` to persist backend-managed git clones across restarts. Code must still work if this volume is not present (create directories as needed).
|
|
- RivetKit actor `c.state` is durable, but in Docker it is stored under `/root/.local/share/rivetkit`. If that path is not persisted, actor state-derived indexes (for example, in `project` actor state) can be lost after container recreation even when other data still exists.
|
|
- Workflow history divergence policy:
|
|
- Production: never auto-delete actor state to resolve `HistoryDivergedError`; ship explicit workflow migrations (`ctx.removed(...)`, step compatibility).
|
|
- Development: manual local state reset is allowed as an operator recovery path when migrations are not yet available.
|
|
- Storage rule of thumb:
|
|
- Put simple metadata in `c.state` (KV state): small scalars and identifiers like `{ taskId }`, `{ repoId }`, booleans, counters, timestamps, status strings.
|
|
- If it grows beyond trivial (arrays, maps, histories, query/filter needs, relational consistency), use SQLite + Drizzle in `c.db`.
|
|
|
|
## Testing Policy
|
|
|
|
- Never use vitest mocks (`vi.mock`, `vi.spyOn`, `vi.fn`). Instead, define driver interfaces for external I/O and pass test implementations via the actor runtime context.
|
|
- All external service calls (git CLI, GitHub CLI, sandbox-agent HTTP, tmux) must go through the `BackendDriver` interface on the runtime context.
|
|
- Integration tests use `setupTest()` from `rivetkit/test` and are gated behind `HF_ENABLE_ACTOR_INTEGRATION_TESTS=1`.
|
|
- End-to-end testing must run against the dev backend started via `docker compose -f compose.dev.yaml up` (host -> container). Do not run E2E against an in-process test runtime.
|
|
- E2E tests should talk to the backend over HTTP (default `http://127.0.0.1:7741/v1/rivet`) and use real GitHub repos/PRs.
|
|
- For Foundry live verification, use `rivet-dev/sandbox-agent-testing` as the default testing repo unless the task explicitly says otherwise.
|
|
- Secrets (e.g. `OPENAI_API_KEY`, `GITHUB_TOKEN`/`GH_TOKEN`) must be provided via environment variables, never hardcoded in the repo.
|
|
- `~/misc/env.txt` and `~/misc/the-foundry.env` contain the expected local OpenAI + GitHub OAuth/App config for dev.
|
|
- Do not assume `gh auth token` is sufficient for Foundry task provisioning against private repos. Sandbox/bootstrap git clone, push, and PR flows require a repo-capable `GITHUB_TOKEN`/`GH_TOKEN` in the backend container.
|
|
- Preferred product behavior for org workspaces is to mint a GitHub App installation token from the workspace installation and inject it into backend/sandbox git operations. Do not rely on an operator's ambient CLI auth as the long-term solution.
|
|
- Treat client E2E tests in `packages/client/test` as the primary end-to-end source of truth for product behavior.
|
|
- Keep backend tests small and targeted. Only retain backend-only tests for invariants or persistence rules that are not well-covered through client E2E.
|
|
- Do not keep large browser E2E suites around in a broken state. If a frontend browser E2E is not maintained and producing signal, remove it until it can be replaced with a reliable test.
|
|
|
|
## Config
|
|
|
|
- Keep config path at `~/.config/foundry/config.toml`.
|
|
- Evolve properties in place; do not move config location.
|
|
|
|
## Project Guidance
|
|
|
|
Project-specific guidance lives in `README.md`, `CONTRIBUTING.md`, and the relevant files under `research/`.
|
|
|
|
Keep those updated when:
|
|
|
|
- Commands change
|
|
- Configuration options change
|
|
- Architecture changes
|
|
- Plugins/providers change
|
|
- Actor ownership changes
|
|
|
|
## Friction Logs
|
|
|
|
Track friction at:
|
|
|
|
- `research/friction/rivet.mdx`
|
|
- `research/friction/sandbox-agent.mdx`
|
|
- `research/friction/sandboxes.mdx`
|
|
- `research/friction/general.mdx`
|
|
|
|
Category mapping:
|
|
|
|
- `rivet`: Rivet/RivetKit runtime, actor model, queues, keys
|
|
- `sandbox-agent`: sandbox-agent SDK/API behavior
|
|
- `sandboxes`: provider implementations (worktree/daytona/etc)
|
|
- `general`: everything else
|
|
|
|
Each entry must include:
|
|
|
|
- Date (`YYYY-MM-DD`)
|
|
- Commit SHA (or `uncommitted`)
|
|
- What you were implementing
|
|
- Friction/issue
|
|
- Attempted fix/workaround and outcome
|
|
|
|
## History Events
|
|
|
|
Log notable workflow changes to `events` so `hf history` remains complete:
|
|
|
|
- create
|
|
- attach
|
|
- push/sync/merge
|
|
- archive/kill
|
|
- status transitions
|
|
- PR state transitions
|
|
|
|
## Validation After Changes
|
|
|
|
Always run and fix failures:
|
|
|
|
```bash
|
|
pnpm -w typecheck
|
|
pnpm -w build
|
|
pnpm -w test
|
|
```
|
|
|
|
After making code changes, always update the dev server before declaring the work complete. If the dev stack is running through Docker Compose, restart or recreate the relevant dev services so the running app reflects the latest code.
|