This commit is contained in:
Nathan Flurry 2026-03-14 20:28:41 -07:00
parent 3263d4f5e1
commit 0fbea6ce61
166 changed files with 6675 additions and 7105 deletions

View file

@ -6,19 +6,19 @@ Date: 2026-02-08
## Locked Decisions
1. Entire rewrite is TypeScript. All Rust code will be deleted at cutover.
2. Repo stays a single monorepo, managed with `pnpm` workspaces + Turborepo.
2. Repo stays a single monorepo, managed with `pnpm` organizations + Turborepo.
3. `core` package is renamed to `shared`.
4. `integrations` and `providers` live inside the backend package (not top-level packages).
5. Rivet-backed state uses SQLite + Drizzle only.
6. RivetKit dependencies come from local `../rivet` builds only; no published npm packages.
7. Everything is workspace-scoped. Workspace is configurable from CLI.
8. `ControlPlaneActor` is renamed to `WorkspaceActor` (workspace coordinator).
9. Every actor key is prefixed by workspace.
10. `--workspace` is optional; commands resolve workspace via flag -> config default -> `default`.
7. Everything is organization-scoped. Organization is configurable from CLI.
8. `ControlPlaneActor` is renamed to `OrganizationActor` (organization coordinator).
9. Every actor key is prefixed by organization.
10. `--organization` is optional; commands resolve organization via flag -> config default -> `default`.
11. RivetKit local dependency wiring is `link:`-based.
12. Keep the existing config file path (`~/.config/foundry/config.toml`) and evolve keys in place.
13. `.agents` and skill files are in scope for migration updates.
14. Parent orchestration actors (`workspace`, `project`, `task`) use command-only loops with no timeout.
14. Parent orchestration actors (`organization`, `repository`, `task`) use command-only loops with no timeout.
15. Periodic syncing/polling runs in dedicated child actors, each with a single timeout cadence.
16. For each actor, define the main loop and exactly what data it mutates; keep single-writer ownership strict.
@ -38,10 +38,10 @@ The core architecture changes from "worktree-per-task" to "provider-selected san
1. Rust binaries/backend removed.
2. Existing IPC replaced by new TypeScript transport.
3. Configuration schema changes for workspace selection and sandbox provider defaults.
4. Runtime model changes from global control plane to workspace coordinator actor.
5. Database schema migrates to workspace + provider + sandbox identity model.
6. Command options evolve to include workspace and provider selection.
3. Configuration schema changes for organization selection and sandbox provider defaults.
4. Runtime model changes from global control plane to organization coordinator actor.
5. Database schema migrates to organization + provider + sandbox identity model.
6. Command options evolve to include organization and provider selection.
## Monorepo and Build Tooling
@ -49,7 +49,7 @@ Root tooling is standardized:
- `pnpm-workspace.yaml`
- `turbo.json`
- workspace scripts through `pnpm` + `turbo run ...`
- organization scripts through `pnpm` + `turbo run ...`
Target package layout:
@ -59,13 +59,13 @@ packages/
backend/
src/
actors/
workspace.ts
project.ts
organization.ts
repository.ts
task.ts
sandbox-instance.ts
history.ts
project-pr-sync.ts
project-branch-sync.ts
repository-pr-sync.ts
repository-branch-sync.ts
task-status-sync.ts
keys.ts
events.ts
@ -88,13 +88,13 @@ packages/
server.ts
types.ts
config/
workspace.ts
organization.ts
backend.ts
cli/ # hf command surface
src/
commands/
client/ # backend transport client
workspace/ # workspace selection resolver
organization/ # organization selection resolver
tui/ # OpenTUI app
src/
app/
@ -111,13 +111,13 @@ CLI and TUI are separate packages in the same monorepo, not separate repositorie
Backend actor files and responsibilities:
1. `packages/backend/src/actors/workspace.ts`
- `WorkspaceActor` implementation.
- Provider profile resolution and workspace-level coordination.
- Spawns/routes to `ProjectActor` handles.
1. `packages/backend/src/actors/organization.ts`
- `OrganizationActor` implementation.
- Provider profile resolution and organization-level coordination.
- Spawns/routes to `RepositoryActor` handles.
2. `packages/backend/src/actors/project.ts`
- `ProjectActor` implementation.
2. `packages/backend/src/actors/repository.ts`
- `RepositoryActor` implementation.
- Branch snapshot refresh, PR cache orchestration, stream publication.
- Routes task actions to `TaskActor`.
@ -134,7 +134,7 @@ Backend actor files and responsibilities:
- Writes workflow events to SQLite via Drizzle.
6. `packages/backend/src/actors/keys.ts`
- Workspace-prefixed actor key builders/parsers.
- Organization-prefixed actor key builders/parsers.
7. `packages/backend/src/actors/events.ts`
- Internal actor event envelopes and stream payload types.
@ -145,13 +145,13 @@ Backend actor files and responsibilities:
9. `packages/backend/src/actors/index.ts`
- Actor exports and composition wiring.
10. `packages/backend/src/actors/project-pr-sync.ts`
10. `packages/backend/src/actors/repository-pr-sync.ts`
- Read-only PR polling loop (single timeout cadence).
- Sends sync results back to `ProjectActor`.
- Sends sync results back to `RepositoryActor`.
11. `packages/backend/src/actors/project-branch-sync.ts`
11. `packages/backend/src/actors/repository-branch-sync.ts`
- Read-only branch snapshot polling loop (single timeout cadence).
- Sends sync results back to `ProjectActor`.
- Sends sync results back to `RepositoryActor`.
12. `packages/backend/src/actors/task-status-sync.ts`
- Read-only session/sandbox status polling loop (single timeout cadence).
@ -169,17 +169,17 @@ pnpm build -F rivetkit
2. Consume via local `link:` dependencies to built artifacts.
3. Keep dependency wiring deterministic and documented in repo scripts.
## Workspace Model
## Organization Model
Every command executes against a resolved workspace context.
Every command executes against a resolved organization context.
Workspace selection:
Organization selection:
1. CLI flag: `--workspace <name>`
2. Config default workspace
1. CLI flag: `--organization <name>`
2. Config default organization
3. Fallback to `default`
Workspace controls:
Organization controls:
1. provider profile defaults
2. sandbox policy
@ -188,45 +188,45 @@ Workspace controls:
## New Actor Implementation Overview
RivetKit registry actor keys are workspace-prefixed:
RivetKit registry actor keys are organization-prefixed:
1. `WorkspaceActor` (workspace coordinator)
- Key: `["ws", workspaceId]`
- Owns workspace config/runtime coordination, provider registry, workspace health.
- Resolves provider defaults and workspace-level policies.
1. `OrganizationActor` (organization coordinator)
- Key: `["ws", organizationId]`
- Owns organization config/runtime coordination, provider registry, organization health.
- Resolves provider defaults and organization-level policies.
2. `ProjectActor`
- Key: `["ws", workspaceId, "project", repoId]`
2. `RepositoryActor`
- Key: `["ws", organizationId, "repository", repoId]`
- Owns repo snapshot cache and PR cache refresh orchestration.
- Routes branch/task commands to task actors.
- Streams project updates to CLI/TUI subscribers.
- Streams repository updates to CLI/TUI subscribers.
3. `TaskActor`
- Key: `["ws", workspaceId, "project", repoId, "task", taskId]`
- Key: `["ws", organizationId, "repository", repoId, "task", taskId]`
- Owns task metadata/runtime state.
- Creates/resumes sandbox + session through provider adapter.
- Handles attach/push/sync/merge/archive/kill and post-idle automation.
4. `SandboxInstanceActor` (optional but recommended)
- Key: `["ws", workspaceId, "provider", providerId, "sandbox", sandboxId]`
- Key: `["ws", organizationId, "provider", providerId, "sandbox", sandboxId]`
- Owns sandbox lifecycle, heartbeat, endpoint readiness, recovery.
5. `HistoryActor`
- Key: `["ws", workspaceId, "project", repoId, "history"]`
- Key: `["ws", organizationId, "repository", repoId, "history"]`
- Owns `events` writes and workflow timeline completeness.
6. `ProjectPrSyncActor` (child poller)
- Key: `["ws", workspaceId, "project", repoId, "pr-sync"]`
- Polls PR state on interval and emits results to `ProjectActor`.
- Key: `["ws", organizationId, "repository", repoId, "pr-sync"]`
- Polls PR state on interval and emits results to `RepositoryActor`.
- Does not write DB directly.
7. `ProjectBranchSyncActor` (child poller)
- Key: `["ws", workspaceId, "project", repoId, "branch-sync"]`
- Polls branch/worktree state on interval and emits results to `ProjectActor`.
- Key: `["ws", organizationId, "repository", repoId, "branch-sync"]`
- Polls branch/worktree state on interval and emits results to `RepositoryActor`.
- Does not write DB directly.
8. `TaskStatusSyncActor` (child poller)
- Key: `["ws", workspaceId, "project", repoId, "task", taskId, "status-sync"]`
- Key: `["ws", organizationId, "repository", repoId, "task", taskId, "status-sync"]`
- Polls agent/session/sandbox health on interval and emits results to `TaskActor`.
- Does not write DB directly.
@ -236,10 +236,10 @@ Ownership rule: each table/row has one actor writer.
Always define actor run-loop + mutated state together:
1. `WorkspaceActor`
- Mutates: `workspaces`, `workspace_provider_profiles`.
1. `OrganizationActor`
- Mutates: `organizations`, `workspace_provider_profiles`.
2. `ProjectActor`
2. `RepositoryActor`
- Mutates: `repos`, `branches`, `pr_cache` (applies child poller results).
3. `TaskActor`
@ -251,30 +251,30 @@ Always define actor run-loop + mutated state together:
5. `HistoryActor`
- Mutates: `events`.
6. Child sync actors (`project-pr-sync`, `project-branch-sync`, `task-status-sync`)
6. Child sync actors (`repository-pr-sync`, `repository-branch-sync`, `task-status-sync`)
- Mutates: none (read-only pollers; publish result messages only).
## Run Loop Patterns (Required)
Parent orchestration actors: no timeout, command-only queue loops.
### `WorkspaceActor` (no timeout)
### `OrganizationActor` (no timeout)
```ts
run: async (c) => {
while (true) {
const msg = await c.queue.next("workspace.command");
await handleWorkspaceCommand(c, msg); // writes workspace-owned tables only
const msg = await c.queue.next("organization.command");
await handleOrganizationCommand(c, msg); // writes organization-owned tables only
}
};
```
### `ProjectActor` (no timeout)
### `RepositoryActor` (no timeout)
```ts
run: async (c) => {
while (true) {
const msg = await c.queue.next("project.command");
const msg = await c.queue.next("repository.command");
await handleProjectCommand(c, msg); // includes applying sync results to branches/pr_cache
}
};
@ -321,10 +321,10 @@ Child sync actors: one timeout each, one cadence each.
run: async (c) => {
const intervalMs = 30_000;
while (true) {
const msg = await c.queue.next("project.pr_sync.command", { timeout: intervalMs });
const msg = await c.queue.next("repository.pr_sync.command", { timeout: intervalMs });
if (!msg) {
const result = await pollPrState();
await sendToProject({ name: "project.pr_sync.result", result });
await sendToProject({ name: "repository.pr_sync.result", result });
continue;
}
await handlePrSyncControl(c, msg); // force/stop/update-interval
@ -338,10 +338,10 @@ run: async (c) => {
run: async (c) => {
const intervalMs = 5_000;
while (true) {
const msg = await c.queue.next("project.branch_sync.command", { timeout: intervalMs });
const msg = await c.queue.next("repository.branch_sync.command", { timeout: intervalMs });
if (!msg) {
const result = await pollBranchState();
await sendToProject({ name: "project.branch_sync.result", result });
await sendToProject({ name: "repository.branch_sync.result", result });
continue;
}
await handleBranchSyncControl(c, msg);
@ -368,7 +368,7 @@ run: async (c) => {
## Sandbox Provider Interface
Provider contract lives under `packages/backend/src/providers/provider-api` and is consumed by workspace/project/task actors.
Provider contract lives under `packages/backend/src/providers/provider-api` and is consumed by organization/repository/task actors.
```ts
interface SandboxProvider {
@ -398,26 +398,26 @@ Initial providers:
- Boots/ensures Sandbox Agent inside sandbox.
- Returns endpoint/token for session operations.
## Command Surface (Workspace + Provider Aware)
## Command Surface (Organization + Provider Aware)
1. `hf create ... --workspace <ws> --provider <worktree|daytona>`
2. `hf switch --workspace <ws> [target]`
3. `hf attach --workspace <ws> [task]`
4. `hf list --workspace <ws>`
5. `hf kill|archive|merge|push|sync --workspace <ws> ...`
6. `hf workspace use <ws>` to set default workspace
1. `hf create ... --organization <ws> --provider <worktree|daytona>`
2. `hf switch --organization <ws> [target]`
3. `hf attach --organization <ws> [task]`
4. `hf list --organization <ws>`
5. `hf kill|archive|merge|push|sync --organization <ws> ...`
6. `hf organization use <ws>` to set default organization
List/TUI include provider and sandbox health metadata.
`--workspace` remains optional; omitted values use the standard resolution order.
`--organization` remains optional; omitted values use the standard resolution order.
## Data Model v2 (SQLite + Drizzle)
All persistent state is SQLite via Drizzle schema + migrations.
Tables (workspace-scoped):
Tables (organization-scoped):
1. `workspaces`
1. `organizations`
2. `workspace_provider_profiles`
3. `repos` (`workspace_id`, `repo_id`, ...)
4. `branches` (`workspace_id`, `repo_id`, ...)
@ -433,10 +433,10 @@ Migration approach: one-way migration from existing schema during TS backend boo
1. TypeScript backend exposes local control API (socket or localhost HTTP).
2. CLI/TUI are thin clients; all mutations go through backend actors.
3. OpenTUI subscribes to project streams from workspace-scoped project actors.
4. Workspace is required context on all backend mutation requests.
3. OpenTUI subscribes to repository streams from organization-scoped repository actors.
4. Organization is required context on all backend mutation requests.
CLI/TUI are responsible for resolving workspace context before calling backend mutations.
CLI/TUI are responsible for resolving organization context before calling backend mutations.
## CLI + TUI Packaging
@ -451,10 +451,10 @@ The package still calls the same backend API and shares contracts from `packages
## Implementation Phases
## Phase 0: Contracts and Workspace Spec
## Phase 0: Contracts and Organization Spec
1. Freeze workspace model, provider contract, and actor ownership map.
2. Freeze command flags for workspace + provider selection.
1. Freeze organization model, provider contract, and actor ownership map.
2. Freeze command flags for organization + provider selection.
3. Define Drizzle schema draft and migration plan.
Exit criteria:
@ -462,7 +462,7 @@ Exit criteria:
## Phase 1: TypeScript Monorepo Bootstrap
1. Add `pnpm` workspace + Turborepo pipeline.
1. Add `pnpm` organization + Turborepo pipeline.
2. Create `shared`, `backend`, and `cli` packages (with TUI integrated into CLI).
3. Add strict TypeScript config and CI checks.
@ -473,10 +473,10 @@ Exit criteria:
1. Wire local RivetKit dependency from `../rivet`.
2. Add SQLite + Drizzle migrations and query layer.
3. Implement actor registry with workspace-prefixed keys.
3. Implement actor registry with organization-prefixed keys.
Exit criteria:
- Backend boot + workspace actor health checks pass.
- Backend boot + organization actor health checks pass.
## Phase 3: Provider Layer in Backend
@ -487,9 +487,9 @@ Exit criteria:
Exit criteria:
- `create/list/switch/attach/push/sync/kill` pass on worktree provider.
## Phase 4: Workspace/Task Lifecycle
## Phase 4: Organization/Task Lifecycle
1. Implement workspace coordinator flows.
1. Implement organization coordinator flows.
2. Implement TaskActor full lifecycle + post-idle automation.
3. Implement history events and PR/CI/review change tracking.
@ -509,7 +509,7 @@ Exit criteria:
1. Build interactive list/switch UI in OpenTUI.
2. Implement key actions (attach/open PR/archive/merge/sync).
3. Add workspace switcher UX and provider/sandbox indicators.
3. Add organization switcher UX and provider/sandbox indicators.
Exit criteria:
- TUI parity and responsive streaming updates.
@ -534,7 +534,7 @@ Exit criteria:
2. Integration tests
- backend + sqlite + provider fakes
- workspace isolation boundaries
- organization isolation boundaries
- session recovery and restart handling
3. E2E tests